Skip to content

Instantly share code, notes, and snippets.

@JakubDotPy
Created October 25, 2021 09:54
Show Gist options
  • Save JakubDotPy/1df395c90d0561072bfe0a1769e2e257 to your computer and use it in GitHub Desktop.
Save JakubDotPy/1df395c90d0561072bfe0a1769e2e257 to your computer and use it in GitHub Desktop.
Django Role permissions. Role model contains groups and/or individual permissions. Group permission groups.
@admin.register(Role)
class RoleAdmin(admin.ModelAdmin):
filter_horizontal = [
'permission_groups',
'permissions',
]
list_display = [
'name',
'perm_groups_str',
'perm_str',
]
# optimize query
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'permissions':
kwargs['queryset'] = Permission.objects.all().select_related('content_type')
return super().formfield_for_manytomany(db_field, request, **kwargs)
"""
Add this backend to other auth backends in settings.
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend', # default model auth
'hilmat.apps.users.backends.RoleBackend', # enriches user perms with Role perms
]
"""
class RoleBackend(ModelBackend):
"""Checks user permissions granted by his Role."""
def get_all_permissions(self, user_obj, obj=None):
perm_cache_name = '_role_perm_cache'
if not user_obj.role:
return set()
if not hasattr(user_obj, perm_cache_name):
perms = user_obj.role.get_all_permissions()
perms = perms.values_list('content_type__app_label', 'codename').order_by()
setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms})
return getattr(user_obj, perm_cache_name)
class Role(models.Model):
"""Employee role for better hierarchy.
Contains m2m perm. groups and m2m individual permissions.
Each user has only one.
e.g.: location admin, test manager, user ...
Assign to user model as usual.
"""
#: name of the employee role e.g.: location admin, test manager...
name = models.CharField(
max_length=20,
help_text=_('name of the employee role e.g.: location admin, test manager...'),
default='Guest',
)
#: integer level of the role for hierarchy comparison
level = models.SmallIntegerField(
default=0,
verbose_name=pgettext('hierarchy level at the Role model', 'level'),
help_text=_('integer level of the role for hierarchy comparison'),
)
permission_groups = models.ManyToManyField(
Group,
help_text=_('auth groups belonging to the role'),
related_name='roles',
related_query_name='role',
blank=True,
)
permissions = models.ManyToManyField(
Permission,
help_text=_('extra permissions assigned to the role'),
related_name='permissions',
related_query_name='permissions',
blank=True,
)
def __str__(self):
return f'{self.name}'
@property
def perm_groups_str(self):
return ', '.join(pg.name for pg in self.permission_groups.all())
# NOTE: sets description to the property getter
perm_groups_str.fget.short_description = _('Permission groups')
@property
def perm_str(self):
return ', '.join(perm.name for perm in self.permissions.all())
perm_str.fget.short_description = _('Additional permissions')
def get_all_permissions(self):
"""Get all permissions within this role.
From it's groups and from it's extra permissions.
:returns queryset
"""
# single permissions
perms = self.permissions.all()
# add group permissions
for group in self.permission_groups.all():
perms |= group.permissions.all()
return perms
class Meta:
verbose_name = _('role')
verbose_name_plural = pgettext('the employee role e.g.: location admin, test manager...', 'roles')
class User(AbstractBaseUser, PermissionsMixin):
"""Custom created User model
https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#substituting-a-custom-user-model
"""
# other user properties
...
role = models.ForeignKey(
Role,
verbose_name=_('role'),
help_text=_('Admin, Location Admin, Test Manager...'),
related_name='users',
related_query_name='user',
null=True,
on_delete=models.PROTECT,
blank=True,
)
# other user properties
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment