Search code examples
pythondjangopython-3.xdjango-permissions

Using both Groups and Individual Permissions


In Django I have created a system with various groups of users. Using django.auth I have also created permission groups and I have associated the appropriate application permissions for each group.

This works great for role based access, but now I have a requirement where I also need the ability to remove individual permissions from a specific user in a group. This means I need group permissions, but at the same time these group permissions can be unassigned to individual users.

Using Django groups this appears to not directly be possible as the permissions are abstracted from individual users.

How can I accomplish this?

I am in the process of changing everything to individual user permissions, but this seems a bit tedious for clients as they have to manually set permissions for each new user, I am hoping someone knows of a better way.


Solution

  • The solution for this only required a ManyToMany field in my User model to hold revoked permissions and custom backend authentication.

    User:

    revoked_permissions = models.ManyToManyField(Permission, blank=True)

    Authentication Backend:

    from django.contrib.auth.backends import ModelBackend
    from django.contrib.auth.models import Permission
    
    
    class UsersAuthenticationBackend(ModelBackend):
        def _get_revoked_perms(self, user_obj):
            if user_obj.is_superuser or user_obj.is_admin:
                revoked_perms = Permission.objects.none()
            elif hasattr(user_obj, 'revoked_permissions'):
                revoked_perms = getattr(user_obj, 'revoked_permissions').values_list('content_type__app_label', 'codename')
            else:
                revoked_perms = Permission.objects.none()
    
            revoked_perms = ["{}.{}".format(perm[0], perm[1]) for perm in revoked_perms]
            return revoked_perms
    
        def has_perm(self, user_obj, perm, obj=None):
            if not user_obj.is_active:
                return False
    
            revoked_perms = self._get_revoked_perms(user_obj)
            all_perms = self.get_all_permissions(user_obj)
            allowed_perms = [p for p in all_perms if not p in revoked_perms]
    
            return perm in allowed_perms
    
        def has_module_perms(self, user_obj, app_label):
            if not user_obj.is_active:
                return False
    
            revoked_perms = self._get_revoked_perms(user_obj)
            all_perms = self.get_all_permissions(user_obj)
            allowed_perms = [p for p in all_perms if not p in revoked_perms]
    
            for perm in allowed_perms:
                if perm[:perm.index('.')] == app_label:
                    return True
            return False
    

    Settings.py:

    AUTHENTICATION_BACKENDS = [
        "apps.users.backends.UsersAuthenticationBackend"
    ]
    

    Using this I am now able to set overall group permissions and then also individually revoke permissions for specific users.