Project

General

Profile

0003-django_rbac-add-auth_level-arg-to-permission-methods.patch

Valentin Deniaud, 28 May 2019 05:24 PM

Download (7.29 KB)

View differences:

Subject: [PATCH 3/8] django_rbac: add auth_level arg to permission methods
 (#33515)

Internally we bypass _user_has_perm and similar internal methods fixed
signatures with an annotation on the user object.
 src/django_rbac/backends.py   | 13 +++++++----
 src/django_rbac/exceptions.py |  2 ++
 src/django_rbac/managers.py   |  4 ++--
 src/django_rbac/models.py     | 43 +++++++++++++++++++++++++++++++++--
 4 files changed, 53 insertions(+), 9 deletions(-)
 create mode 100644 src/django_rbac/exceptions.py
src/django_rbac/backends.py
- `'<app_label>'`: contains a boolean, it indicates that the user own at least on
permision on a model of this application.
'''
if not hasattr(user_obj, '_rbac_perms_cache'):
perms_cache = {}
auth_level = getattr(user_obj, '_auth_level', None)
cache_suffix = '_auth_level' if auth_level else ''
perms_cache = getattr(user_obj, '_rbac_perms_cache' + cache_suffix, {})
if not perms_cache:
Permission = utils.get_permission_model()
qs = Permission.objects.for_user(user_obj)
qs = Permission.objects.for_user(user_obj, max_auth_level=auth_level)
ct_ct = ContentType.objects.get_for_model(ContentType)
qs = qs.select_related('operation')
for permission in qs:
......
permissions.update(perms)
# optimization for has_module_perms
perms_cache[app_label] = True
user_obj._rbac_perms_cache = perms_cache
return user_obj._rbac_perms_cache
setattr(user_obj, '_rbac_perms_cache' + cache_suffix, perms_cache)
return perms_cache
def get_all_permissions(self, user_obj, obj=None):
if user_obj.is_anonymous():
src/django_rbac/exceptions.py
class InsufficientAuthLevel(Exception):
pass
src/django_rbac/managers.py
'''Filter permission whose target matches target'''
return self.by_target_ct(target).filter(target_id=target.pk)
def for_user(self, user):
def for_user(self, user, max_auth_level=None):
'''Retrieve all permissions hold by an user through its role and
inherited roles.
'''
Role = utils.get_role_model()
roles = Role.objects.for_user(user=user)
roles = Role.objects.for_user(user=user, max_auth_level=max_auth_level)
return self.filter(roles__in=roles)
def cleanup(self):
src/django_rbac/models.py
from django.core.validators import MinValueValidator
from . import utils, constants, managers, backends
from .exceptions import InsufficientAuthLevel
@six.python_2_unicode_compatible
......
def get_all_permissions(self, obj=None):
return _user_get_all_permissions(self, obj)
def _check_auth_level(perm_func):
"""Add authentication level check to a permission control function.
perm_func can be passed a new keyword argument 'auth_level'. If
present, expect perm_func to be ran two times, once with the user
object annotated with an '_auth_level' attribute, and once without. If
the return values do not match, ie some permissions are not granted
when the user authentication level is taken into account, the decorator
will take care of raising an InsufficientAuthLevel exception.
"""
def wrapped_perm_func(self, *args, **kwargs):
auth_level = kwargs.pop('auth_level', None)
auth_level_result = None
if auth_level:
self._auth_level = auth_level
auth_level_result = perm_func(self, *args, **kwargs)
self._auth_level = None
if auth_level_result is True:
# Performance trick: if the function returns True, assume
# that we can return right away.
return True
new_result = perm_func(self, *args, **kwargs)
if auth_level and auth_level_result != new_result:
# Let the application know that permission could be granted
# with higher authentication level.
raise InsufficientAuthLevel
return new_result
return wrapped_perm_func
@_check_auth_level
def has_perm(self, perm, obj=None):
"""
Returns True if the user has the specified permission. This method
......
# Otherwise we need to check the backends.
return _user_has_perm(self, perm, obj)
def has_perms(self, perm_list, obj=None):
def has_perms(self, perm_list, obj=None, auth_level=None):
"""
Returns True if the user has each of the specified permissions. If
object is passed, it checks if the user has all required perms for this
......
return True
for perm in perm_list:
if not self.has_perm(perm, obj):
if not self.has_perm(perm, obj, auth_level=auth_level):
return False
return True
@_check_auth_level
def has_module_perms(self, app_label):
"""
Returns True if the user has any permissions in the given app label.
......
return _user_has_module_perms(self, app_label)
@_check_auth_level
def filter_by_perm(self, perm_or_perms, qs):
results = []
for backend in auth.get_backends():
......
else:
return qs
@_check_auth_level
def has_perm_any(self, perm_or_perms):
# Active superusers have all permissions.
if self.is_active and self.is_superuser:
......
return True
return False
@_check_auth_level
def has_ou_perm(self, perm, ou):
# Active superusers have all permissions.
if self.is_active and self.is_superuser:
......
return True
return False
@_check_auth_level
def ous_with_perm(self, perm, queryset=None):
return backends.DjangoRBACBackend().ous_with_perm(self, perm, queryset=queryset)