MDalt
MDalt

Reputation: 1801

Create a permission in Django that can be optionally applied to superusers

I want to create a permission (or something that achieves the same thing) that can be applied to all users optionally, including super users.

I can create a custom permission very easily:

ct = ContentType.objects.get_for_model(MyModel)
Permission.objects.create(
        name='optional permission',
        codename='optional_permission,
        content_type=ct)

However, this will always return True for superusers user.has_perm('my_app_label.optional_permission')

My use-case is: I'd like to create a queryset manager function that only returns Draft articles for users in Groups that have optional_permission applied. I'd like superusers to be exempt from seeing these draft articles if they want.

Upvotes: 0

Views: 862

Answers (2)

MDalt
MDalt

Reputation: 1801

I had to implement a slightly ugly workaround in the end, so would be interested to know if someone can do better.

I had to add an override boolean for superusers on our User model

class User(AbstractUser):
   superuser_view_draft_override = models.BooleanField(
        default=False, help_text='This allows us to toggle the \'see draft\' permission for superusers only')

   @cached_property
   def can_see_draft_content(self):
        # Have to use the boolean override for superusers because they
        # have all permissions turned on by default
        if self.is_superuser:
            return self.superuser_view_draft_override

        # Otherwise use the permission...
        return self.has_perm('my_app_label.optional_permission')
    
    

Upvotes: 0

minglyu
minglyu

Reputation: 3337

You could try to override has_perm by passing an additional keyword argument to the original implementation.

class User(AbstractUser):
   ...
   def has_perm(self, perm, obj=None, superuser_exempt=True):
       # Active superusers have optional permissions.
       if superuser_exempt:
           if self.is_active and self.is_superuser:
               return True
       # Otherwise we need to check the backends.
       return _user_has_perm(self, perm, obj)

Take a look of has_perm source code:

class PermissionMixin:
    def has_perm(self, perm, obj=None):
        """
        Return True if the user has the specified permission. Query all
        available auth backends, but return immediately if any backend returns
        True. Thus, a user who has permission from a single auth backend is
        assumed to have permission in general. If an object is provided, check
        permissions for that object.
        """
        # Active superusers have all permissions.
        if self.is_active and self.is_superuser:
            return True

        # Otherwise we need to check the backends.
        return _user_has_perm(self, perm, obj)

AbstractUser inherits has_perm method from PermissionMixin

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    ...

Upvotes: 2

Related Questions