4

I want to have hierarchical level permissions in my Django Applications.

For example:- Consider there are 4 levels -

Admin
Sub-admin(CountryLevel(CL) Admin)
sub-sub-admin(StateLevel(SL) Admin)
And then normal Users(U).

Admins will create CL, in return CL will create SL, and SL will finally onboard the users. The end goal is to onboard the users.

Admins have the access to apply CRUD operation on any user. CL should have access to those users(objects) which were onboarded(created) by the CL created SLs. In return, SL should have access to only those users(objects) onboarded(created) by him. Also, a user can get himself registered directly without any admins involved. In such case, the user shall have access to his own application.

How can I achieve such tree-level like permissions?

The solution that I can think of is (but not sure about it):-

I've updated auth_group table and added parent_id into it. Following is the schema.

id, group_name, parent_id

The significance of parent_id is to create a tree-like structure of the groups. The number of groups created is equal to the height of the tree. For eg consider following structure:-

id, group_name, parent_id

1 , admin, 0
2, CL, 1
3, SL, 2

Now when any user is created(onboarded), I'll assign an SL group_id, which will be added to auth_user_groups table.

Now, I want to ask the following questions

1) How do I manage hierarchy-level permission of the groups, ie SL should only be able to access users onboarded by him, CL should be able to access all the users onboarded by the SLs created by him and so on.

2) (Not sure I'm thinking correctly or not) Now, since I've assigned a group SL to all the users onboarded by the SL, will the user be able to access each other information, being part of the same group?

3) For the users who are onboarde directly(ie not via any SL), I shall not be mapping them to any group. Right?

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
Praful Bagai
  • 16,684
  • 50
  • 136
  • 267
  • 2
    [`django-guardian`](https://django-guardian.readthedocs.io/en/stable/) – Burhan Khalid Mar 15 '18 at 13:11
  • A small example would be very helpful. – Praful Bagai Mar 15 '18 at 13:17
  • 2
    If you click the link in my comment, you'll find a sample project. – Burhan Khalid Mar 15 '18 at 13:22
  • @BurhanKhalid - If I need to make a dedicated OAuth2 Server as a service, how shall the permissions be managed by the OAuth2 Server? – Praful Bagai Mar 19 '18 at 06:10
  • Well, I see & understand how to assign object-level permissions. One quick question, please. How do I create a hierarchy of groups? And when assigning the object permission to the lowest level group, how will the parent groups be assigned the permission to access the same object. OR should I manually assign it on each of those parent groups? – Praful Bagai Mar 28 '18 at 09:04
  • @BurhanKhalid - Any lead on this would be very helpful. Thanks! – Praful Bagai Mar 30 '18 at 06:45

1 Answers1

0

Django already allows for a Group, User permissions which we can customize for our needs.

  • Create the groups you need (form the admin panel for example, or by following this SO post.):

    1. 'country-admin-group`
    2. 'state-admin-group'
    3. 'user-group'
  • Create custom permissions for your user groups and add them accordingly:

    from django.contrib.auth.models import Group
    from django.contrib.contenttypes.models import ContentType
    from your_app_name.models import CustomUserModel
    
    ct = ContentType.objects.get_for_model(CustomUserModel)
    permission_sl = Permission.objects.create(
        codename='can_add_sl_user',
        name='Can add SL user',
        content_type=ct
    )
    permission_user = Permission.objects.create(
        codename='can_add_simple_user',
        name='Can add simple user',
        content_type=ct
    )
    ...
    cl_group = Groups.get(name='country-admin-group')
    cl_group.permissions.add(permission_sl)
    cl_group,permissions.add(permission_user)
    
    sl_group = Groups.get(name='state-admin-group')
    sl_group,permissions.add(permission_user)
    ...
    
  • Create views (and the corresponding urls) for each type of user.
    Personally I prefer to use class based views when applicable. We will control the access to each view with the method_decorator and permission_required decorators:

    from django.http import HttpResponse
    from django.views import View
    
    class CountryAdminView(View):
    
        @login_required
        def list(self, request):
            """
            Can access only users created by him
            """
            content = Users.objects.filter(parent_id=request.user.id)
            return HttpResponse(content)
    
        @method_decorator(@permission_required('your_app_name.can_add_sl_user'))
        def create(self, request):
           ...
           Logic for creating an SL admin user.
    
        ...
    

Keep in mind that the above is a simplified example to set you in the path to the solution.


As @BurhanKhalid points out, you can skip a part of the above by using django-guardian.

You can also use django-role-permissions to define per user roles and add permissions to those roles.

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
  • The main thing which is missing is the hierarchy of the users? How would Level0 user views all the users created by the subordinate levels.? – Praful Bagai Apr 02 '18 at 11:27
  • "How do I create a hierarchy of groups? And when assigning the object permission to the lowest level group, how will the parent groups be assigned the permission to access the same object. OR should I manually assign it on each of those parent groups?" – Praful Bagai Apr 02 '18 at 11:28
  • @PythonEnthusiast For your 1st comment: In a view, `return MyCustomUsers.objects.all()`... For the 2nd comment, the hierarchy will be manually created (see the answer). For the self "onboarded" users, you could define a group a `self-admitted-users` group and every level of admin can be a member of that group as well... – John Moutafis Apr 02 '18 at 12:03
  • Okay. I guess I didn't make myself clearly understood. On 1st comment, consider 3 Levels (0, 1, 2). L0 admin is making Level1 users. Now L1 admin is making L2 users. Now, since the L2 users(objects) were made by L1, I assigned the object level permission of L2 to L1. `How would L0 now view details of L2?` – Praful Bagai Apr 02 '18 at 12:18
  • @PythonEnthusiast If the L0 user is a superuser, he can access every kind of user. Differently, give the L0 users group every permission that you created. Similarly, give the L1 user group permission to view and create L2 and L3 users etc. – John Moutafis Apr 02 '18 at 12:22
  • There can be multiple L0 users. And each user from L0 group can create its OWN L1 users, and subsequently, each L1 user can create its OWN L2 users. Now, L0 user needs to view all L2 users which were created by L0 owned L1 users. Hence a hierarchy. How do I create such hierarchical permission? – Praful Bagai Apr 02 '18 at 12:26
  • I can't give L0 users all the permissions of all the objects. What I've thought is while creating a new L2/L3 object, I'll be traversing(fetching) all its parent-users and assign the object to each parent programmatically. Your thoughts? – Praful Bagai Apr 02 '18 at 12:31
  • @PythonEnthusiast Please restrain yourself to one comment at a time. Since you have a Super-Admin group (L0) they should have access to everything (they are not admins differently...). Also, you can access the objects following the correct `parent_id`s up or down the chain (you just need to think a way of doing it correctly for your needs, you are an enthusiast after all...) – John Moutafis Apr 02 '18 at 12:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/168044/discussion-between-pythonenthusiast-and-john-moutafis). – Praful Bagai Apr 02 '18 at 13:12