# RBAC challenges ## Problem statements ### Problem 1 Changes to the default policy `statements` will lead to missing object permissions on the existing objects in the db. * change to the existing `action` BEFORE: { "action": ["update", "partial_update"], "principal": "authenticated", "effect": "allow", "condition": [ "has_model_or_obj_perms:container.change_containerremote", ], }, AFTER: { "action": ["update", "partial_update"], "principal": "authenticated", "effect": "allow", "condition": [ "has_model_or_obj_perms:container.change_containerremote", "has_model_or_obj_perms:container.view_containerremote", ], }, * new `action` has been introduced/enabled { "action": ["build_container"], "principal": "authenticated", "effect": "allow", "condition": [ "has_model_or_obj_perms:container.build_container", ], } How to mitigate situation when a user/group will no longer be able to access existing objects or will have a limit access? Solution: * write a migration * introduce roles ### Problem 2 `customized` flag on the Access policy is not honoured Solution: https://pulp.plan.io/issues/8883 ### Problem 3 we do not have named groups of default permissions which we can safely update #### Lots of groups? The Container plugin creates groups effectively as Roles, but for fine-grained control, say 3 groups per repo. So for 1000 repos, you'd have 3000 groups! #### Fine grained or not? Some installations want fine-grained control with some users in control of some groups, others don't, others want system-wider groups. So for 1000 repos, they just want 3 groups. #### Idea #1 I did an audit of djangopackages.org and this seems like a highly used good fit https://django-role-permissions.readthedocs.io/en/stable/ #### Idea #2 Stop using groups as roles, introduce roles (bunch of permissions) that can be attached to users, or groups, optionally limited to an object. Open question, are roles hard coded? Or part of the access policy? This way, groups can be used for teams (of people). ## bmbouter's testing Have a Role called TaskViewer which defines the ability to read tasks Have a group called Physics with Alice in it Have a group called Math with Bob in it ### Scenario 1 Assign the TaskViewer role to the Physics group. This will Represent a "model level role" ### Scenario 2 Assign the TaskViewer role to Bob. This will represent a model level role to a user ### Scenario 3 Assign the TaskViewer role to Math for a specific Task. The will represent an object level role ### Scenario 4 Assign the TaskViewer role to Alice for a specific Task ### Scenario 5 Add a permission on TaskViewer and observe that anyone who had that role now has it. ### Scenario 6 Remove a permission on TaskViewer and observe that anyone who had that role now lacks it. ### conclusions from testing with django-role-permission It's pretty close to what we need, but it lacks two big things: 1. object-level permissions AFAICT. It does have the ability to check for object level permissions with [`has_object_permission`](https://django-role-permissions.readthedocs.io/en/stable/utils.html#has_object_permission), but in the docs and the code I don't see anything like: `assign_role(user_or_group, role, obj) 2. The ability to natively work with groups. I don't see anything like `assign_role(group, role)` I only see `assign_role(user, role)`. #### Why not in Django-Guardian natively? Of these two things, the lack of object-level permissions, which are really provided by Django Guardian is the more serious gap. Django-guardian does not have Role support, but [they discussed it](https://github.com/django-guardian/django-guardian/issues/23). ### Proposal 1: DIY In pulpcore: * Create a Role object in Pulp (not a model). * Make utility helpers like: ``` assign_role(role, user_or_group, obj=None) remove_role(role, user_or_group, obj=None) ``` These roughly mimic the [django-guardian utils](https://django-guardian.readthedocs.io/en/stable/api/guardian.shortcuts.html#) except they delegate the set of permissions to the role. In each plugin: * Have each plugin make a roles.py with subclasses of Role, e.g. FileRepoAdministrator ### Proposal 2: Make a Role == Permission (not viable) [bmbouter UPDATE]: I don't think this is viable anymore because roles need to span multiple objects, e.g. a repository admin needs to be able to view a remote and have edit rights on the repository in order to perform sync. This proposal would have a single permission to capture that, which then cannot be assigned as an object level permission to multiple object types, e.g. role and repository. Here's a snippet demonstrating so: ``` admin = User.objects.get() task = Task.objects.get() perm = Permission.objects.first() assign_perm(perm, admin, task) ["Cannot persist permission not designed for this class (permission's type is <ContentType: log entry> and object's type is <ContentType: task>)"] ``` Stop relying so much on the built-in 4 CRUD permissions for a Model, and instead define new ones for objects and name them like a role. For example, For the [RPMRepository Personas](https://hackmd.io/@pulp/rbac_for_rpm#Five-User-Types) you have a few personas like: * Repository Superuser * Repository Administrator * Repository Consumer * Content Creator ^ would likely be defined as permissions on the RPMRepository [here](https://github.com/pulp/pulp_rpm/blob/637869c0725c8320682379c93682cb059ea2774e/pulp_rpm/app/models/repository.py#L312). Then treat them like roles. Have the AccessPolicy check and assign them as necessary. The pros of this approach are: * We build almost nothing * Permissions are very simple and they have direct meaning in the app * Adjusting permissions requires no change on permission/role assignment, only changes on where the checking occurs in DRF-access-policy or in the app code The cons of this approach are: * There is no granularity of action, so administrators cannot give a user "half a role", or some subset of a role's permissions * We would become incompatible with some third-part permissions that rely on the correct use of the built-in 4 django CRUD permissions ========================================================= ## Outcome of meeting June 7 1. In the short term use migrations to mitigate problem#1.This will unblock currently opened PRs * https://github.com/pulp/pulp_container/pull/321 * https://github.com/pulp/pulp_container/pull/320 3. In the long term introduce roles(aka groups of permissions) to mitigate problem#1 and problem#3 4. Matthias to file issue to address problem#2 5. Ina to schedule another planning session which wil be focused on the logn term solution and also migration path ## Outcome of meeting Jun 16 Possibly provide fine grained policy and system-wide roles. In order to move forward in planning we need usecases from more than one plugin. Would be great to get Rpm and Python plugins use cases ## Outcome of meeting Jun 30 Rpm use cases https://hackmd.io/@pulp/rbac_for_rpm#Five-User-Types Brian will take a look at django-role-permissions: * can a role be assigned to a group/user for a group of objects * if a role changes, will those changes be propagated to the already assigned permissions to the group/user * if a role is removed will permissions be removed from groups/users as well? ## Outcome of meeting July 7 ###### tags: `RBAC`