0002-custom_user-move-permission-mixin-code-from-django_r.patch
src/django_rbac/backends.py → src/authentic2/custom_user/backends.py | ||
---|---|---|
6 | 6 |
from django.core.exceptions import FieldDoesNotExist |
7 | 7 |
from django.db.models.query import Q |
8 | 8 | |
9 |
from . import utils
|
|
9 |
from django_rbac import utils
|
|
10 | 10 | |
11 | 11 | |
12 | 12 |
def get_fk_model(model, fieldname): |
src/authentic2/custom_user/models.py | ||
---|---|---|
17 | 17 | |
18 | 18 |
import base64 |
19 | 19 |
import datetime |
20 |
import functools |
|
21 |
import operator |
|
20 | 22 |
import os |
21 | 23 |
import uuid |
22 | 24 | |
23 |
from django.contrib.auth.models import AbstractBaseUser |
|
25 |
from django.contrib import auth |
|
26 |
from django.contrib.auth.models import AbstractBaseUser, Group |
|
27 |
from django.contrib.auth.models import Permission as AuthPermission |
|
28 |
from django.contrib.auth.models import _user_has_module_perms, _user_has_perm |
|
24 | 29 |
from django.contrib.contenttypes.fields import GenericRelation |
25 | 30 |
from django.contrib.postgres.fields import JSONField |
26 | 31 |
from django.core.exceptions import MultipleObjectsReturned, ValidationError |
... | ... | |
30 | 35 |
from django.utils import timezone |
31 | 36 |
from django.utils.translation import gettext_lazy as _ |
32 | 37 | |
38 |
try: |
|
39 |
from django.contrib.auth.models import _user_get_all_permissions |
|
40 |
except ImportError: |
|
41 |
from django.contrib.auth.models import _user_get_permissions |
|
42 | ||
43 |
def _user_get_all_permissions(user, obj): |
|
44 |
return _user_get_permissions(user, obj, 'all') |
|
45 | ||
46 | ||
33 | 47 |
from authentic2 import app_settings |
34 | 48 |
from authentic2.a2_rbac.models import RoleParenting |
35 | 49 |
from authentic2.decorators import errorcollector |
... | ... | |
38 | 52 |
from authentic2.utils.cache import RequestCache |
39 | 53 |
from authentic2.utils.models import generate_slug |
40 | 54 |
from authentic2.validators import email_validator |
41 |
from django_rbac.models import PermissionMixin |
|
42 | 55 | |
56 |
from .backends import DjangoRBACBackend |
|
43 | 57 |
from .managers import UserManager, UserQuerySet |
44 | 58 | |
45 | 59 | |
... | ... | |
132 | 146 |
return IsVerified(obj) |
133 | 147 | |
134 | 148 | |
135 |
class User(AbstractBaseUser, PermissionMixin):
|
|
149 |
class User(AbstractBaseUser): |
|
136 | 150 |
""" |
137 | 151 |
An abstract base class implementing a fully featured User model with |
138 | 152 |
admin-compliant permissions. |
... | ... | |
151 | 165 |
email_verified_date = models.DateTimeField( |
152 | 166 |
default=None, blank=True, null=True, verbose_name=_('email verified date') |
153 | 167 |
) |
168 |
is_superuser = models.BooleanField( |
|
169 |
_('superuser status'), |
|
170 |
default=False, |
|
171 |
help_text=_('Designates that this user has all permissions without explicitly assigning them.'), |
|
172 |
) |
|
154 | 173 |
is_staff = models.BooleanField( |
155 | 174 |
_('staff status'), |
156 | 175 |
default=False, |
... | ... | |
172 | 191 |
swappable=False, |
173 | 192 |
on_delete=models.CASCADE, |
174 | 193 |
) |
194 |
groups = models.ManyToManyField( |
|
195 |
to=Group, |
|
196 |
verbose_name=_('groups'), |
|
197 |
blank=True, |
|
198 |
help_text=_( |
|
199 |
'The groups this user belongs to. A user will get all permissions granted to each of his/her' |
|
200 |
' group.' |
|
201 |
), |
|
202 |
related_name="user_set", |
|
203 |
related_query_name="user", |
|
204 |
) |
|
205 |
user_permissions = models.ManyToManyField( |
|
206 |
to=AuthPermission, |
|
207 |
verbose_name=_('user permissions'), |
|
208 |
blank=True, |
|
209 |
help_text=_('Specific permissions for this user.'), |
|
210 |
related_name="user_set", |
|
211 |
related_query_name="user", |
|
212 |
) |
|
175 | 213 | |
176 | 214 |
# events dates |
177 | 215 |
date_joined = models.DateTimeField(_('date joined'), default=timezone.now) |
... | ... | |
198 | 236 |
verbose_name_plural = _('users') |
199 | 237 |
ordering = ('last_name', 'first_name', 'email', 'username') |
200 | 238 | |
239 |
def get_group_permissions(self, obj=None): |
|
240 |
""" |
|
241 |
Returns a list of permission strings that this user has through their |
|
242 |
groups. This method queries all available auth backends. If an object |
|
243 |
is passed in, only permissions matching this object are returned. |
|
244 |
""" |
|
245 |
permissions = set() |
|
246 |
for backend in auth.get_backends(): |
|
247 |
if hasattr(backend, "get_group_permissions"): |
|
248 |
permissions.update(backend.get_group_permissions(self, obj)) |
|
249 |
return permissions |
|
250 | ||
251 |
def get_all_permissions(self, obj=None): |
|
252 |
return _user_get_all_permissions(self, obj) |
|
253 | ||
254 |
def has_perm(self, perm, obj=None): |
|
255 |
""" |
|
256 |
Returns True if the user has the specified permission. This method |
|
257 |
queries all available auth backends, but returns immediately if any |
|
258 |
backend returns True. Thus, a user who has permission from a single |
|
259 |
auth backend is assumed to have permission in general. If an object is |
|
260 |
provided, permissions for this specific object are checked. |
|
261 |
""" |
|
262 | ||
263 |
# Active superusers have all permissions. |
|
264 |
if self.is_active and self.is_superuser: |
|
265 |
return True |
|
266 | ||
267 |
# Otherwise we need to check the backends. |
|
268 |
return _user_has_perm(self, perm, obj) |
|
269 | ||
270 |
def has_perms(self, perm_list, obj=None): |
|
271 |
""" |
|
272 |
Returns True if the user has each of the specified permissions. If |
|
273 |
object is passed, it checks if the user has all required perms for this |
|
274 |
object. |
|
275 |
""" |
|
276 |
# Active superusers have all permissions. |
|
277 |
if self.is_active and self.is_superuser: |
|
278 |
return True |
|
279 | ||
280 |
for perm in perm_list: |
|
281 |
if not self.has_perm(perm, obj): |
|
282 |
return False |
|
283 |
return True |
|
284 | ||
285 |
def has_module_perms(self, app_label): |
|
286 |
""" |
|
287 |
Returns True if the user has any permissions in the given app label. |
|
288 |
Uses pretty much the same logic as has_perm, above. |
|
289 |
""" |
|
290 |
# Active superusers have all permissions. |
|
291 |
if self.is_active and self.is_superuser: |
|
292 |
return True |
|
293 | ||
294 |
return _user_has_module_perms(self, app_label) |
|
295 | ||
296 |
def filter_by_perm(self, perm_or_perms, qs): |
|
297 |
results = [] |
|
298 |
for backend in auth.get_backends(): |
|
299 |
if hasattr(backend, "filter_by_perm"): |
|
300 |
results.append(backend.filter_by_perm(self, perm_or_perms, qs)) |
|
301 |
if results: |
|
302 |
return functools.reduce(operator.__or__, results) |
|
303 |
else: |
|
304 |
return qs |
|
305 | ||
306 |
def has_perm_any(self, perm_or_perms): |
|
307 |
# Active superusers have all permissions. |
|
308 |
if self.is_active and self.is_superuser: |
|
309 |
return True |
|
310 | ||
311 |
for backend in auth.get_backends(): |
|
312 |
if hasattr(backend, "has_perm_any"): |
|
313 |
if backend.has_perm_any(self, perm_or_perms): |
|
314 |
return True |
|
315 |
return False |
|
316 | ||
317 |
def has_ou_perm(self, perm, ou): |
|
318 |
# Active superusers have all permissions. |
|
319 |
if self.is_active and self.is_superuser: |
|
320 |
return True |
|
321 | ||
322 |
for backend in auth.get_backends(): |
|
323 |
if hasattr(backend, "has_ou_perm"): |
|
324 |
if backend.has_ou_perm(self, perm, ou): |
|
325 |
return True |
|
326 |
return False |
|
327 | ||
328 |
def ous_with_perm(self, perm, queryset=None): |
|
329 |
return DjangoRBACBackend().ous_with_perm(self, perm, queryset=queryset) |
|
330 | ||
201 | 331 |
def get_full_name(self): |
202 | 332 |
""" |
203 | 333 |
Returns the first_name plus the last_name, with a space in between. |
src/authentic2/manager/forms.py | ||
---|---|---|
33 | 33 | |
34 | 34 |
from authentic2.a2_rbac.models import OrganizationalUnit, Permission, Role, RoleAttribute |
35 | 35 |
from authentic2.a2_rbac.utils import generate_slug, get_default_ou |
36 |
from authentic2.custom_user.backends import DjangoRBACBackend |
|
36 | 37 |
from authentic2.forms.fields import ( |
37 | 38 |
CheckPasswordField, |
38 | 39 |
CommaSeparatedCharField, |
... | ... | |
45 | 46 |
from authentic2.passwords import generate_password, get_min_password_strength |
46 | 47 |
from authentic2.utils.misc import send_email_change_email, send_password_reset_mail, send_templated_mail |
47 | 48 |
from authentic2.validators import EmailValidator |
48 |
from django_rbac.backends import DjangoRBACBackend |
|
49 | 49 |
from django_rbac.models import Operation |
50 | 50 | |
51 | 51 |
from . import app_settings, fields, utils |
src/authentic2/models.py | ||
---|---|---|
41 | 41 | |
42 | 42 |
from authentic2.a2_rbac.models import Role |
43 | 43 |
from authentic2.a2_rbac.utils import get_default_ou_pk |
44 |
from authentic2.custom_user.backends import DjangoRBACBackend |
|
44 | 45 |
from authentic2.utils.crypto import base64url_decode, base64url_encode |
45 | 46 |
from authentic2.validators import HexaColourValidator |
46 |
from django_rbac.backends import DjangoRBACBackend |
|
47 | 47 | |
48 | 48 |
# install our natural_key implementation |
49 | 49 |
from . import managers |
src/authentic2/settings.py | ||
---|---|---|
165 | 165 |
'authentic2.backends.ldap_backend.LDAPBackend', |
166 | 166 |
'authentic2.backends.ldap_backend.LDAPBackendPasswordLost', |
167 | 167 |
'authentic2.backends.models_backend.DummyModelBackend', |
168 |
'django_rbac.backends.DjangoRBACBackend',
|
|
168 |
'authentic2.custom_user.backends.DjangoRBACBackend',
|
|
169 | 169 |
'authentic2_auth_saml.backends.SAMLBackend', |
170 | 170 |
'authentic2_auth_oidc.backends.OIDCBackend', |
171 | 171 |
'authentic2_auth_fc.backends.FcBackend', |
src/django_rbac/models.py | ||
---|---|---|
1 |
import functools |
|
2 |
import operator |
|
3 | ||
4 |
from django.contrib import auth |
|
5 |
from django.contrib.auth.models import Group |
|
6 |
from django.contrib.auth.models import Permission as AuthPermission |
|
7 |
from django.contrib.auth.models import _user_has_module_perms, _user_has_perm |
|
8 | ||
9 |
try: |
|
10 |
from django.contrib.auth.models import _user_get_all_permissions |
|
11 |
except ImportError: |
|
12 |
from django.contrib.auth.models import _user_get_permissions |
|
13 | ||
14 |
def _user_get_all_permissions(user, obj): |
|
15 |
return _user_get_permissions(user, obj, 'all') |
|
16 | ||
17 | ||
18 | 1 |
from django.db import models |
19 | 2 |
from django.utils.translation import gettext_lazy as _ |
20 | 3 |
from django.utils.translation import pgettext_lazy |
21 | 4 | |
22 |
from . import backends, managers
|
|
5 |
from . import managers |
|
23 | 6 | |
24 | 7 | |
25 | 8 |
class Operation(models.Model): |
... | ... | |
51 | 34 |
Operation._meta.natural_key = ['slug'] |
52 | 35 | |
53 | 36 | |
54 |
class PermissionMixin(models.Model): |
|
55 |
""" |
|
56 |
A mixin class that adds the fields and methods necessary to support |
|
57 |
Django's Group and Permission model using the ModelBackend. |
|
58 |
""" |
|
59 | ||
60 |
is_superuser = models.BooleanField( |
|
61 |
_('superuser status'), |
|
62 |
default=False, |
|
63 |
help_text=_('Designates that this user has all permissions without explicitly assigning them.'), |
|
64 |
) |
|
65 |
groups = models.ManyToManyField( |
|
66 |
to=Group, |
|
67 |
verbose_name=_('groups'), |
|
68 |
blank=True, |
|
69 |
help_text=_( |
|
70 |
'The groups this user belongs to. A user will get all permissions granted to each of his/her' |
|
71 |
' group.' |
|
72 |
), |
|
73 |
related_name="user_set", |
|
74 |
related_query_name="user", |
|
75 |
) |
|
76 |
user_permissions = models.ManyToManyField( |
|
77 |
to=AuthPermission, |
|
78 |
verbose_name=_('user permissions'), |
|
79 |
blank=True, |
|
80 |
help_text=_('Specific permissions for this user.'), |
|
81 |
related_name="user_set", |
|
82 |
related_query_name="user", |
|
83 |
) |
|
84 | ||
85 |
class Meta: |
|
86 |
abstract = True |
|
87 | ||
88 |
def get_group_permissions(self, obj=None): |
|
89 |
""" |
|
90 |
Returns a list of permission strings that this user has through their |
|
91 |
groups. This method queries all available auth backends. If an object |
|
92 |
is passed in, only permissions matching this object are returned. |
|
93 |
""" |
|
94 |
permissions = set() |
|
95 |
for backend in auth.get_backends(): |
|
96 |
if hasattr(backend, "get_group_permissions"): |
|
97 |
permissions.update(backend.get_group_permissions(self, obj)) |
|
98 |
return permissions |
|
99 | ||
100 |
def get_all_permissions(self, obj=None): |
|
101 |
return _user_get_all_permissions(self, obj) |
|
102 | ||
103 |
def has_perm(self, perm, obj=None): |
|
104 |
""" |
|
105 |
Returns True if the user has the specified permission. This method |
|
106 |
queries all available auth backends, but returns immediately if any |
|
107 |
backend returns True. Thus, a user who has permission from a single |
|
108 |
auth backend is assumed to have permission in general. If an object is |
|
109 |
provided, permissions for this specific object are checked. |
|
110 |
""" |
|
111 | ||
112 |
# Active superusers have all permissions. |
|
113 |
if self.is_active and self.is_superuser: |
|
114 |
return True |
|
115 | ||
116 |
# Otherwise we need to check the backends. |
|
117 |
return _user_has_perm(self, perm, obj) |
|
118 | ||
119 |
def has_perms(self, perm_list, obj=None): |
|
120 |
""" |
|
121 |
Returns True if the user has each of the specified permissions. If |
|
122 |
object is passed, it checks if the user has all required perms for this |
|
123 |
object. |
|
124 |
""" |
|
125 |
# Active superusers have all permissions. |
|
126 |
if self.is_active and self.is_superuser: |
|
127 |
return True |
|
128 | ||
129 |
for perm in perm_list: |
|
130 |
if not self.has_perm(perm, obj): |
|
131 |
return False |
|
132 |
return True |
|
133 | ||
134 |
def has_module_perms(self, app_label): |
|
135 |
""" |
|
136 |
Returns True if the user has any permissions in the given app label. |
|
137 |
Uses pretty much the same logic as has_perm, above. |
|
138 |
""" |
|
139 |
# Active superusers have all permissions. |
|
140 |
if self.is_active and self.is_superuser: |
|
141 |
return True |
|
142 | ||
143 |
return _user_has_module_perms(self, app_label) |
|
144 | ||
145 |
def filter_by_perm(self, perm_or_perms, qs): |
|
146 |
results = [] |
|
147 |
for backend in auth.get_backends(): |
|
148 |
if hasattr(backend, "filter_by_perm"): |
|
149 |
results.append(backend.filter_by_perm(self, perm_or_perms, qs)) |
|
150 |
if results: |
|
151 |
return functools.reduce(operator.__or__, results) |
|
152 |
else: |
|
153 |
return qs |
|
154 | ||
155 |
def has_perm_any(self, perm_or_perms): |
|
156 |
# Active superusers have all permissions. |
|
157 |
if self.is_active and self.is_superuser: |
|
158 |
return True |
|
159 | ||
160 |
for backend in auth.get_backends(): |
|
161 |
if hasattr(backend, "has_perm_any"): |
|
162 |
if backend.has_perm_any(self, perm_or_perms): |
|
163 |
return True |
|
164 |
return False |
|
165 | ||
166 |
def has_ou_perm(self, perm, ou): |
|
167 |
# Active superusers have all permissions. |
|
168 |
if self.is_active and self.is_superuser: |
|
169 |
return True |
|
170 | ||
171 |
for backend in auth.get_backends(): |
|
172 |
if hasattr(backend, "has_ou_perm"): |
|
173 |
if backend.has_ou_perm(self, perm, ou): |
|
174 |
return True |
|
175 |
return False |
|
176 | ||
177 |
def ous_with_perm(self, perm, queryset=None): |
|
178 |
return backends.DjangoRBACBackend().ous_with_perm(self, perm, queryset=queryset) |
|
179 | ||
180 | ||
181 | 37 |
ADMIN_OP = Operation.register(name=pgettext_lazy('permission', 'Management'), slug='admin') |
182 | 38 |
CHANGE_OP = Operation.register(name=pgettext_lazy('permission', 'Change'), slug='change') |
183 | 39 |
DELETE_OP = Operation.register(name=pgettext_lazy('permission', 'Delete'), slug='delete') |
tests/test_rbac.py | ||
---|---|---|
20 | 20 |
from django.db.models import Q |
21 | 21 |
from django.test.utils import CaptureQueriesContext |
22 | 22 | |
23 |
from django_rbac import backends, models, utils |
|
23 |
from authentic2.custom_user import backends |
|
24 |
from django_rbac import models, utils |
|
24 | 25 | |
25 | 26 |
OU = OrganizationalUnit = utils.get_ou_model() |
26 | 27 |
Permission = utils.get_permission_model() |
27 |
- |