Projet

Général

Profil

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

Valentin Deniaud, 28 mai 2019 17:24

Télécharger (7,29 ko)

Voir les différences:

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
52 52
           - `'<app_label>'`: contains a boolean, it indicates that the user own at least on
53 53
           permision on a model of this application.
54 54
        '''
55
        if not hasattr(user_obj, '_rbac_perms_cache'):
56
            perms_cache = {}
55
        auth_level = getattr(user_obj, '_auth_level', None)
56
        cache_suffix = '_auth_level' if auth_level else ''
57
        perms_cache = getattr(user_obj, '_rbac_perms_cache' + cache_suffix, {})
58

  
59
        if not perms_cache:
57 60
            Permission = utils.get_permission_model()
58
            qs = Permission.objects.for_user(user_obj)
61
            qs = Permission.objects.for_user(user_obj, max_auth_level=auth_level)
59 62
            ct_ct = ContentType.objects.get_for_model(ContentType)
60 63
            qs = qs.select_related('operation')
61 64
            for permission in qs:
......
83 86
                permissions.update(perms)
84 87
                # optimization for has_module_perms
85 88
                perms_cache[app_label] = True
86
            user_obj._rbac_perms_cache = perms_cache
87
        return user_obj._rbac_perms_cache
89
            setattr(user_obj, '_rbac_perms_cache' + cache_suffix, perms_cache)
90
        return perms_cache
88 91

  
89 92
    def get_all_permissions(self, user_obj, obj=None):
90 93
        if user_obj.is_anonymous():
src/django_rbac/exceptions.py
1
class InsufficientAuthLevel(Exception):
2
    pass
src/django_rbac/managers.py
78 78
        '''Filter permission whose target matches target'''
79 79
        return self.by_target_ct(target).filter(target_id=target.pk)
80 80

  
81
    def for_user(self, user):
81
    def for_user(self, user, max_auth_level=None):
82 82
        '''Retrieve all permissions hold by an user through its role and
83 83
           inherited roles.
84 84
        '''
85 85
        Role = utils.get_role_model()
86
        roles = Role.objects.for_user(user=user)
86
        roles = Role.objects.for_user(user=user, max_auth_level=max_auth_level)
87 87
        return self.filter(roles__in=roles)
88 88

  
89 89
    def cleanup(self):
src/django_rbac/models.py
20 20
from django.core.validators import MinValueValidator
21 21

  
22 22
from . import utils, constants, managers, backends
23
from .exceptions import InsufficientAuthLevel
23 24

  
24 25

  
25 26
@six.python_2_unicode_compatible
......
330 331
    def get_all_permissions(self, obj=None):
331 332
        return _user_get_all_permissions(self, obj)
332 333

  
334
    def _check_auth_level(perm_func):
335
        """Add authentication level check to a permission control function.
336

  
337
        perm_func can be passed a new keyword argument 'auth_level'. If
338
        present, expect perm_func to be ran two times, once with the user
339
        object annotated with an '_auth_level' attribute, and once without. If
340
        the return values do not match, ie some permissions are not granted
341
        when the user authentication level is taken into account, the decorator
342
        will take care of raising an InsufficientAuthLevel exception.
343
        """
344
        def wrapped_perm_func(self, *args, **kwargs):
345
            auth_level = kwargs.pop('auth_level', None)
346
            auth_level_result = None
347

  
348
            if auth_level:
349
                self._auth_level = auth_level
350
                auth_level_result = perm_func(self, *args, **kwargs)
351
                self._auth_level = None
352
                if auth_level_result is True:
353
                    # Performance trick: if the function returns True, assume
354
                    # that we can return right away.
355
                    return True
356

  
357
            new_result = perm_func(self, *args, **kwargs)
358
            if auth_level and auth_level_result != new_result:
359
                # Let the application know that permission could be granted
360
                # with higher authentication level.
361
                raise InsufficientAuthLevel
362

  
363
            return new_result
364
        return wrapped_perm_func
365

  
366
    @_check_auth_level
333 367
    def has_perm(self, perm, obj=None):
334 368
        """
335 369
        Returns True if the user has the specified permission. This method
......
346 380
        # Otherwise we need to check the backends.
347 381
        return _user_has_perm(self, perm, obj)
348 382

  
349
    def has_perms(self, perm_list, obj=None):
383
    def has_perms(self, perm_list, obj=None, auth_level=None):
350 384
        """
351 385
        Returns True if the user has each of the specified permissions. If
352 386
        object is passed, it checks if the user has all required perms for this
......
357 391
            return True
358 392

  
359 393
        for perm in perm_list:
360
            if not self.has_perm(perm, obj):
394
            if not self.has_perm(perm, obj, auth_level=auth_level):
361 395
                return False
362 396
        return True
363 397

  
398
    @_check_auth_level
364 399
    def has_module_perms(self, app_label):
365 400
        """
366 401
        Returns True if the user has any permissions in the given app label.
......
372 407

  
373 408
        return _user_has_module_perms(self, app_label)
374 409

  
410
    @_check_auth_level
375 411
    def filter_by_perm(self, perm_or_perms, qs):
376 412
        results = []
377 413
        for backend in auth.get_backends():
......
382 418
        else:
383 419
            return qs
384 420

  
421
    @_check_auth_level
385 422
    def has_perm_any(self, perm_or_perms):
386 423
        # Active superusers have all permissions.
387 424
        if self.is_active and self.is_superuser:
......
393 430
                    return True
394 431
        return False
395 432

  
433
    @_check_auth_level
396 434
    def has_ou_perm(self, perm, ou):
397 435
        # Active superusers have all permissions.
398 436
        if self.is_active and self.is_superuser:
......
404 442
                    return True
405 443
        return False
406 444

  
445
    @_check_auth_level
407 446
    def ous_with_perm(self, perm, queryset=None):
408 447
        return backends.DjangoRBACBackend().ous_with_perm(self, perm, queryset=queryset)
409 448

  
410
-