Project

General

Profile

0002-django_rbac-annotate-roles-with-actual-auth-level-gi.patch

Valentin Deniaud, 05 June 2019 02:30 PM

Download (3.25 KB)

View differences:

Subject: [PATCH 2/3] django_rbac: annotate roles with actual auth level given
 context
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Example when this is useful :
A user has role R1 with an auth level of 2. R1 inherits from R2 (ie R1
is child, R2 is parent). When looking for the roles of the user
(Roles.objects.for_user(user)), we will get R1 and R2. Then we might
want to know which authentication level the user has to get in order to
be allowed to use R2. The good answer is 2, although R2.auth_level is 1.
This commit adds a way to find out the good answer.

Note : il y a un cas limite où ça devrait ne pas marcher, pourtant si,
et je sais pas pourquoi.
Si un utilisateur a R1 et R2, deux rôles de niv 2.
R1 hérite de R3 (niv 1) et R2 hérite de R4 (niv 1).
Jusque là la solution fonctionne.
Et maintenant si R3 hérite de R4 ? Pour déterminer le needed_auth_level
de R4, je pensais qu'on se retrouverait à faire :
min(niv(fils_de_R4)) = min(niv(R2, R3)) = min(2, 1) = 1
Ce qui serait bien sûr faux.
Ben dans mes tests set_needed_auth_level lui met un needed_auth_level de
2, mais je sais pas ce qui le fait marcher exactement !
À confirmer par un test très clair, quand on en sera à écrire des tests.
 src/django_rbac/managers.py | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)
src/django_rbac/managers.py
import threading
from django.db import models
from django.db.models import query
from django.db.models import query, Min, Case, When
from django.contrib.contenttypes.models import ContentType
from django.db.models.query import Q, Prefetch
from django.contrib.auth import get_user_model
......
class RoleQuerySet(query.QuerySet):
def for_user(self, user, max_auth_level=None):
def for_user(self, user, max_auth_level=None, annotate=False):
qs = self.filter(members=user)
if max_auth_level:
qs = qs.filter(auth_level__lte=max_auth_level)
qs = qs.parents()
if max_auth_level:
qs = qs.filter(auth_level__lte=max_auth_level)
return qs.distinct()
qs = qs.distinct()
if annotate:
qs = qs.set_needed_auth_levels(user)
return qs
def parents(self, include_self=True, annotate=False):
qs = self.model.objects.filter(child_relation__child__in=self)
......
qs = qs.annotate(direct=models.Max(IntCast('child_relation__direct')))
return qs
def set_needed_auth_levels(self, user):
return self.annotate(needed_auth_level=Case(
When(~Q(members=user), then=Min('child_relation__child__auth_level')),
default='auth_level'
))
def children(self, include_self=True, annotate=False):
qs = self.model.objects.filter(parent_relation__parent__in=self)
if include_self: