From 0488477a3001d5e1fd83987d18953127a7849d4c Mon Sep 17 00:00:00 2001 From: Matthias Dellweg Date: Fri, 1 Apr 2022 19:08:00 +0200 Subject: [PATCH] WIP: Update RBAC guide for plugin writers Explain the concept of roles, their relationship to permissions, how they are associated with personas and objects and also remove any mention of django-guardian that is to be removed in 3.20. fixes #2463 Co-authored-by: bmbouter --- CHANGES/plugin_api/2463.doc | 2 + .../concepts/rbac/access_policy.rst | 25 ++++- .../rbac/adding_automatic_permissions.rst | 2 +- .../concepts/rbac/permissions.rst | 103 +++++++++--------- .../concepts/rbac/queryset_scoping.rst | 4 +- .../concepts/rbac/users_groups.rst | 5 +- 6 files changed, 81 insertions(+), 60 deletions(-) create mode 100644 CHANGES/plugin_api/2463.doc diff --git a/CHANGES/plugin_api/2463.doc b/CHANGES/plugin_api/2463.doc new file mode 100644 index 00000000000..722e12e9a5c --- /dev/null +++ b/CHANGES/plugin_api/2463.doc @@ -0,0 +1,2 @@ +Updated plugin writers RBAC guide to explain more roles and less permissions. Removed mentions of +django-guardian. diff --git a/docs/plugins/plugin-writer/concepts/rbac/access_policy.rst b/docs/plugins/plugin-writer/concepts/rbac/access_policy.rst index 204688c1396..92b83e15027 100644 --- a/docs/plugins/plugin-writer/concepts/rbac/access_policy.rst +++ b/docs/plugins/plugin-writer/concepts/rbac/access_policy.rst @@ -83,9 +83,7 @@ authorized. The ``admin`` user created on installations prior to RBAC being enabled has ``is_superuser=True``. Django assumes a superuser has any model-level permission even without it - being assigned. Additionally, django-guardian when checking object-level permissions defaults to - assuming the same although it is configurable. Generally, superusers are expected to bypass - authorization checks. + being assigned. Django's permission checking machinery assumes superusers bypass authorization checks. Custom ViewSet Actions @@ -235,3 +233,24 @@ different Permission check by declaring the ``permission_classes`` check. For ex ... permission_classes = tuple() ... + + +.. _permission_checking_machinery: + +Permission Checking Machinery +----------------------------- + +drf-access-policy provides a feature to enable conditional checks to be globally 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: + +.. code-block:: python + + 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: + +.. automodule:: pulpcore.app.global_access_conditions + :members: diff --git a/docs/plugins/plugin-writer/concepts/rbac/adding_automatic_permissions.rst b/docs/plugins/plugin-writer/concepts/rbac/adding_automatic_permissions.rst index 9f65f4f1bcd..5f85091f993 100644 --- a/docs/plugins/plugin-writer/concepts/rbac/adding_automatic_permissions.rst +++ b/docs/plugins/plugin-writer/concepts/rbac/adding_automatic_permissions.rst @@ -57,7 +57,7 @@ example assigning the ``"core.task_viewer"`` role to the group ``"foo"``. .. code-block:: python { - "function": "add_for_groups", + "function": "add_roles_for_groups", "parameters": { "roles": ["core.task_viewer"], "groups": "foo", diff --git a/docs/plugins/plugin-writer/concepts/rbac/permissions.rst b/docs/plugins/plugin-writer/concepts/rbac/permissions.rst index dfc3f0382cf..21a147a8be9 100644 --- a/docs/plugins/plugin-writer/concepts/rbac/permissions.rst +++ b/docs/plugins/plugin-writer/concepts/rbac/permissions.rst @@ -1,23 +1,18 @@ -Permissions -=========== +Permissions and Roles +===================== -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. +The permissions system provides a way to assign permissions as part of roles to specific users and groups of users. +The models driving this data are ``django.contrib.auth.models.Permission`` and ``pulpcore.plugin.models.role.Role``. Each +``Permission`` has a name, describing it and can be associated with one or more ``Role``. +Roles can be assigned to users or groups either on the Model-Level or Object-Level. -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_permissions: +Model Permissions +----------------- -.. _model_level_permissions: - -Model-Level Permissions ------------------------ - +``Permissions`` in Django (or should i say Pulp?) are tied to models and usually map to certain actions performed thereon. By default, each model receives four permissions: * The “add” permission limits the user’s ability to view the “add” form and add an object. @@ -25,7 +20,7 @@ By default, each model receives four permissions: * 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: +The Model permissions are created automatically by Django, and receive a name like: ``._``. 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 `_ 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: - -* `Assigning object permissions `_ -* `Checking object permissions `_ -* `Removing object permissions `_ -* `Helpful shortcut functions `_ - - .. _defining_custom_permissions: Defining Custom Permissions --------------------------- -Any model can define a custom permission, and Django will automatically make a migration to add it +Any model can define custom permissions, 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. +In contrast to ``AccessPolicies`` and ``creation_hooks``, permissions can only be defined by the plugin writer. +As a rule of thumb, permissions should be the atomic building blocks for roles and each action that can be performed on an object should have its own permission. .. _custom_permission_for_repository_content_modification: @@ -91,8 +68,8 @@ Here's an example of adding a permission like this for ``FileRepository``: class Meta: ... - permissions = ( - ('modify_repo_content', 'Modify Repository Content'), + permissions = ( + ('modify_repo_content', 'Modify Repository Content'), ) .. note:: @@ -100,22 +77,44 @@ Here's an example of adding a permission like this for ``FileRepository``: 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: -Permission Checking Machinery ------------------------------ +.. _roles: -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: +Roles +----- -.. code-block:: python +``Roles`` are basically sets of ``Permissions`` and in Pulp, users and groups should receive their ``Permissions`` exclusively via role assignments. +Typical roles are ``owner`` for an object with all the permissions to view modify and delete the object, or ``viewer`` limited to see the object. +To scope the reach of the permissions in a role, these role are assigned to ``Users`` or ``Groups`` either on the model-level or the object-level. - DRF_ACCESS_POLICY = {"reusable_conditions": "pulpcore.app.global_access_conditions"} +:Model-Level: A role is associated to a user or group for access to a specific model, but not an instance of that + model. This allows you to express concepts like "Hilde can administer all FileRemotes". +:Object-Level: A role is associated to a user or group for access to a specific instance of a specific model. This + allows you to express concepts like "Hilde can administer FileRemote(name='foo remote'). -The ``pulpcore.app.global_access_conditions`` provides the following checks that are available for -both users and plugin writers to use in their policies: +Certain roles may contain permissions that are only ever checked on the model-level. +For example the ``creator`` role for a model that contains the models ``add`` permission. + +In the case for ``FileRemote``, the typical set of roles provided by the plugin looks like: + +.. code-block:: python -.. automodule:: pulpcore.app.global_access_conditions - :members: + LOCKED_ROLES = { + "file.fileremote_creator": ["file.add_fileremote"], + "file.fileremote_owner": [ + "file.view_fileremote", + "file.change_fileremote", + "file.delete_fileremote", + "file.manage_roles_fileremote", + ], + "file.fileremote_viewer": ["file.view_fileremote"], + } + +Roles com in two flavors. +First there are so called locked roles that are provided by plugins. +Their name needs to be prefixed by the plugin ``app_label`` followed by a dot (see the example above). +They can be seen, but not modified via the api, and are kept up to date with their definition in the plugin code. +That way, plugins can ship default access policies that rely on those roles. +The other flavor are user defined roles. +These are managed and owned by the site admin via the Pulp API, and plugin code will not interfere with them. +Users can opt to use the the provided locked roles or roll their own. diff --git a/docs/plugins/plugin-writer/concepts/rbac/queryset_scoping.rst b/docs/plugins/plugin-writer/concepts/rbac/queryset_scoping.rst index 99e5d9a35c3..3df4bcf0b7c 100644 --- a/docs/plugins/plugin-writer/concepts/rbac/queryset_scoping.rst +++ b/docs/plugins/plugin-writer/concepts/rbac/queryset_scoping.rst @@ -41,8 +41,8 @@ like more control over the QuerySet Scoping feature it can be added manually by ``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 or django-guardian. Here's an example where all items are displayed accessible -via either of the permission frameworks: +provided by pulpcore. Here's an example where all items are displayed accessible via either of the +permission frameworks: .. code-block:: python diff --git a/docs/plugins/plugin-writer/concepts/rbac/users_groups.rst b/docs/plugins/plugin-writer/concepts/rbac/users_groups.rst index c20ebd03cff..6c9e9880440 100644 --- a/docs/plugins/plugin-writer/concepts/rbac/users_groups.rst +++ b/docs/plugins/plugin-writer/concepts/rbac/users_groups.rst @@ -9,5 +9,6 @@ Users and Groups is always stored in the Django database. This is a requirement :User: Provided by Django with the ``django.contrib.auth.models.User`` model. :Group: Provided by Django with the ``django.contrib.auth.models.Group`` model. -Any role or permission can be assigned to either users, groups, or both. This includes both -Model-level and Object-level roles as well as permissions. +Any role can be assigned to either users, groups, or both. This includes both Model-level and +Object-level role assignments. Direct permission assignments are not recommended and cannot be +operated on within the Pulp-API.