Restricting Viewable Objects¶
With limited object-level permissions on certain objects, its desirable to restrict the objects shown to users. This effectively causes a Pulp system with many users to have each user see only "their" permissions.
This feature is generally referred to as Queryset Scoping because it is applied as an additional filter on the base Queryset of a ViewSet. This causes the permission filtering to work with other filterings applied by a user.
Note
If Domains are enabled, querysets will be scoped by the current request's domain before being passed onto RBAC queryset scoping.
Enabling QuerySet Scoping¶
The support for this is built into pulpcore.plugin.viewsets.NamedModelViewSet
, which is often
the base class for any model-based ViewSet if Pulp. Queryset Scoping is performed by the ViewSet's
get_queryset
method which calls each permission class' method scope_queryset
if present.
Pulp's default permission class, pulpcore.app.AccessPolicyFromDB
, implementation of
scope_queryset
calls the ViewSet function in the AccessPolicy field queryset_scoping
if
defined. This field can be changed by the user to any method on the ViewSet or set empty if they
wish to turn off Queryset Scoping for that view:
DEFAULT_ACCESS_POLICY = {
...
# Call method `scope_queryset` on ViewSet to perform Queryset Scoping
"queryset_scoping": {"function": "scope_queryset"},
...
}
NamedModelViewSet
has a default scope_queryset
implementation that will scope the query
based of the queryset_filtering_required_permission
class attribute set on ViewSet.
Objects will only be shown to users that have access to this specific permission either at the
model-level or object-level.
For example Tasks are restricted only to those users with the "core.view_task" permission like this:
TaskViewSet(NamedModelViewSet):
...
queryset_filtering_required_permission = "core.view_task"
Manually Implementing QuerySet Scoping¶
Default scoping behavior can be overriden by supplying your own scope_queryset
method.
scope_queryset
takes one argument, the queryset to be scoped, and returns the scoped queryset.
Content ViewSet's have their scope_queryset
method overriden to scope based on repositories
the user can see.
Note
When queryset scoping is enabled for content you must also use the
has_required_repo_perms_on_upload
access condition on the upload endpoint to ensure users
specify a repository for upload or they won't be able to see their uploaded content.
Extra Queryset Scoping methods can be defined on the ViewSet to allow users to choose different
behaviors besides On/Off. The method must accept the queryset as the first argument. Additional
parameters can also be accepted by supplying them in a parameters
section of the
queryset_scoping
field of the AccessPolicy like so:
from pulpcore.plugin.viewsets import NamedModelViewSet
from pulpcore.plugin.util import get_objects_for_user
class MyViewSet(NamedModelViewSet):
DEFAULT_ACCESS_POLICY = {
# Statements omitted
"queryset_scoping" : {
# This entire field is editable by the user
"function": "different_permission_scope",
"parameters": {"permission": "my.example_permission"}
}
}
def different_permission_scope(qs, permission):
"""Example extra scoping method that uses a user specified permission to scope."""
return get_objects_for_user(self.request.user, permission, qs=qs)
If your ViewSet does not inherit from pulpcore.plugin.viewsets.NamedModelViewSet
or you would
like more control over the QuerySet Scoping feature it can be added manually by adding a
get_queryset
method to your ViewSet which returns the filtered QuerySet.
To look up objects by permission easily from an existing QuerySet use the get_objects_for_user
provided by pulpcore. Here's an example:
from pulpcore.plugin.util import get_objects_for_user
class MyViewSet(rest_framework.viewsets.GenericViewSet):
def get_queryset(self):
qs = super().get_queryset()
permission_name = "my.example_permission"
return get_objects_for_user(self.request.user, permission_name, qs=qs)
Warning
If you have custom ViewSets and plan to add Domains compatibility to your plugin, you must
scope your objects by the domain in the ViewSet's get_queryset
method to comply
with Domain's isolation policies.