Created
October 25, 2021 09:54
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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