Permissions

The permissions system provides a way to assign permissions to specific users and groups of users. The model driving this data is provided by django.contrib.auth.models.Permission. Each Permission has a name, describing it and can be associated with one or more users or groups.

Two types of permissions exist: Model-Level and Object-Level.

Model-Level

A permission that is associated with a specific model, but not an instance of that model. This allows you to express concepts like “Hilde can modify all FileRemotes”.

Object-Level

A permission that is associated with a specific instance of a specific model. This allows you to express concepts like “Hilde can modify FileRemote(name=’foo remote’).

Model-Level Permissions

By default, each model receives four permissions:

  • The “add” permission limits the user’s ability to view the “add” form and add an object.

  • The “change” permission limits a user’s ability to view the change list, view the “change” form and change an object.

  • The “delete” permission limits the ability to delete an object.

  • The “view” permission limits the ability to view an object.

The Model-level permissions are created automatically by Django, and receive a name like: <app_name>_<action>_<model_name>. For example to change file remote the permission is named file.change_fileremote. You can view the Permissions on a system via the Django ORM with: Permission.objects.all(). See the Django Permissions Docs for more information on working with permissions.

Here’s an example of the Permissions automatically created for the FileRemote model:

  • file.add_fileremote

  • file.view_fileremote

  • file.change_fileremote

  • file.delete_fileremote

Object-Level Permissions

Object-level permissions are provided by django-guardian which is a dependency of Pulp and enabled by default. This extends the normal Django calls has_perm(perm, obj=None) has_perms(perm_list, obj=None to give meaning to the obj portion of the call which Django otherwise would ignore.

Django-guardian has great docs on what it provides for interacting with object-level permissions:

Defining Custom Permissions

Any model can define a custom permission, and Django will automatically make a migration to add it for you. See the Django Custom Permissions Documentation for more information on how to do that.

Custom Permission for Repository Content Modification

The Repository subclass is one place where it’s recommended to create a custom permission that manages the ability to modify RepositoryVersions underneath a Repository. While the add, create, view, and delete default permissions apply to the Repository itself, this new custom permission is intended to be required for any operations that produce RepositoryVersions, e.g. sync, modify, or upload.

Here’s an example of adding a permission like this for FileRepository:

class FileRepository(Repository):

    ...

    class Meta:
        ...
            permissions = (
        ('modify_repo_content', 'Modify Repository Content'),
        )

Note

It is not necessary to “namespace” this modify_repo_content permission because by including it in the meta class of your Detail view, it will already be namespaced on the correct object.

Permission Checking Machinery

drf-access-policy provides a feature to enable conditional checks to be globalls available as their docs describe here. Pulp enables the reusable_conditions in its settings.py file, allowing a variety of condition checks to be globally available. Pulp enables this as follows:

DRF_ACCESS_POLICY = {"reusable_conditions": "pulpcore.app.global_access_conditions"}

The pulpcore.app.global_access_conditions provides the following checks that are available for both users and plugin writers to use in their policies:

pulpcore.app.global_access_conditions.has_model_or_obj_perms(request, view, action, permission)

Checks if the current user has either model-level or object-level permissions.

The object in this case is the one the action is operating on, e.g. the URL /pulp/api/v3/tasks/15939b47-6b6d-4613-a441-939ca4ba6e63/ is operating on the Task object with pk=15939b47-6b6d-4613-a441-939ca4ba6e63.

This is usable as a conditional check in an AccessPolicy. Here is an example checking for “file.view_fileremote” permission at either the model-level or object-level.

{
    ...
    "condition": "has_model_or_obj_perms:file.view_fileremote",
}
Parameters
  • request (rest_framework.request.Request) – The request being made.

  • view (subclass rest_framework.viewsets.GenericViewSet) – The view being checked for authorization.

  • action (str) – The action being performed, e.g. “destroy”.

  • permission (str) – The name of the Permission to be checked. In the form app_label.codename, e.g. “core.delete_task”.

Returns

True if the user has the Permission named by the permission argument at the model-level

or on the object being operated on at the object-level. False otherwise.

pulpcore.app.global_access_conditions.has_model_perms(request, view, action, permission)

Checks if the current user has a model-level permission.

This is usable as a conditional check in an AccessPolicy. Here is an example checking for “file.add_fileremote” permission at the model-level.

{
    ...
    "condition": "has_model_perms:file.add_fileremote",
}
Parameters
  • request (rest_framework.request.Request) – The request being made.

  • view (subclass rest_framework.viewsets.GenericViewSet) – The view being checked for authorization.

  • action (str) – The action being performed, e.g. “destroy”.

  • permission (str) – The name of the Permission to be checked. In the form app_label.codename, e.g. “core.delete_task”.

Returns

True if the user has the Permission named by the permission argument at the model-level.

False otherwise.

pulpcore.app.global_access_conditions.has_obj_perms(request, view, action, permission)

Checks if the current user has object-level permission on the specific object.

The object in this case is the one the action is operating on, e.g. the URL /pulp/api/v3/tasks/15939b47-6b6d-4613-a441-939ca4ba6e63/ is operating on the Task object with pk=15939b47-6b6d-4613-a441-939ca4ba6e63.

This is usable as a conditional check in an AccessPolicy. Here is an example checking for the “file.view_fileremote” permissions at the object-level.

{
    ...
    "condition": "has_obj_perms:file.view_fileremote",
}
Parameters
  • request (rest_framework.request.Request) – The request being made.

  • view (subclass rest_framework.viewsets.GenericViewSet) – The view being checked for authorization.

  • action (str) – The action being performed, e.g. “destroy”.

  • permission (str) – The name of the Permission to be checked. In the form app_label.codename, e.g. “core.delete_task”.

Returns

True if the user has the Permission named by the permission argument on the object being

operated on at the object-level. False otherwise.

pulpcore.app.global_access_conditions.has_remote_param_model_or_obj_perms(request, view, action, permission)

Checks if the current user has either model-level or object-level permissions on the remote.

The object in this case is the one specified by the remote parameter. For example when syncing the remote parameter is passed in as an argument.

This is usable as a conditional check in an AccessPolicy. Here is an example checking for “file.view_fileremote” permission at either the model-level or object-level.

{
    ...
    "condition": "has_remote_param_model_or_obj_perms:file.view_fileremote",
}

Since it is checking a remote object the permission argument should be one of the following:

  • “file.change_fileremote” - Permission to change the FileRemote.

  • “file.view_fileremote” - Permission to view the FileRemote.

  • “file.delete_fileremote” - Permission to delete the FileRemote.

Parameters
  • request (rest_framework.request.Request) – The request being made.

  • view (subclass rest_framework.viewsets.GenericViewSet) – The view being checked for authorization.

  • action (str) – The action being performed, e.g. “destroy”.

  • permission (str) – The name of the Permission to be checked. In the form app_label.codename, e.g. “core.delete_task”.

Returns

True if the user has the Permission named by the permission at the model-level or on the

argument on the remote parameter at the object-level. False otherwise.

pulpcore.app.global_access_conditions.has_remote_param_obj_perms(request, view, action, permission)

Checks if the current user has object-level permission on the remote object.

The object in this case is the one specified by the remote parameter. For example when syncing the remote parameter is passed in as an argument.

This is usable as a conditional check in an AccessPolicy. Here is an example checking for the “file.view_fileremote” permissions at the object-level.

{
    ...
    "condition": "has_remote_param_obj_perms:file.view_fileremote",
}

Since it is checking a remote object the permission argument should be one of the following:

  • “file.change_fileremote” - Permission to change the FileRemote.

  • “file.view_fileremote” - Permission to view the FileRemote.

  • “file.delete_fileremote” - Permission to delete the FileRemote.

Parameters
  • request (rest_framework.request.Request) – The request being made.

  • view (subclass rest_framework.viewsets.GenericViewSet) – The view being checked for authorization.

  • action (str) – The action being performed, e.g. “destroy”.

  • permission (str) – The name of the Permission to be checked. In the form app_label.codename, e.g. “core.delete_task”.

Returns

True if the user has the Permission named by the permission argument on the remote

parameter at the object-level. False otherwise.