mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 15:50:03 +01:00 
			
		
		
		
	Update a lot of things
This commit is contained in:
		| @@ -78,6 +78,10 @@ def save_object(sender, instance, **kwargs): | ||||
|  | ||||
|     user, ip = get_user_and_ip(sender) | ||||
|  | ||||
|     from django.contrib.auth.models import AnonymousUser | ||||
|     if isinstance(user, AnonymousUser): | ||||
|         user = None | ||||
|  | ||||
|     if user is not None and instance._meta.label_lower == "auth.user" and previous: | ||||
|         # Don't save last login modifications | ||||
|         if instance.last_login != previous.last_login: | ||||
|   | ||||
| @@ -6,7 +6,7 @@ from django.contrib.auth.admin import UserAdmin | ||||
| from django.contrib.auth.models import User | ||||
|  | ||||
| from .forms import ProfileForm | ||||
| from .models import Club, Membership, Profile, Role | ||||
| from .models import Club, Membership, Profile, Role, RolePermissions | ||||
|  | ||||
|  | ||||
| class ProfileInline(admin.StackedInline): | ||||
| @@ -40,3 +40,4 @@ admin.site.register(User, CustomUserAdmin) | ||||
| admin.site.register(Club) | ||||
| admin.site.register(Membership) | ||||
| admin.site.register(Role) | ||||
| admin.site.register(RolePermissions) | ||||
|   | ||||
| @@ -1,33 +1,37 @@ | ||||
| from django.contribs.contenttype.models import ContentType | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| from member.models import Club, Membership, RolePermissions | ||||
| from django.contrib.auth.backends import ModelBackend | ||||
|  | ||||
|  | ||||
| class PermissionBackend(object): | ||||
| class PermissionBackend(ModelBackend): | ||||
|     supports_object_permissions = True | ||||
|     supports_anonymous_user = False | ||||
|     supports_inactive_user = False | ||||
|  | ||||
|     def authenticate(self, username, password): | ||||
|         return None | ||||
|  | ||||
|     def permissions(self, user, obj): | ||||
|         for membership in user.memberships.all(): | ||||
|             if not membership.valid() or membership.role is None: | ||||
|     def permissions(self, user): | ||||
|         for membership in Membership.objects.filter(user=user).all(): | ||||
|             if not membership.valid() or membership.roles is None: | ||||
|                 continue | ||||
|             for permission in RolePermissions.objects.get(role=membership.role).permissions.objects.all(): | ||||
|                 permission = permission.about(user=user, club=membership.club) | ||||
|                 yield permission | ||||
|             for role_permissions in RolePermissions.objects.filter(role=membership.roles).all(): | ||||
|                 for permission in role_permissions.permissions.all(): | ||||
|                     permission = permission.about(user=user, club=membership.club) | ||||
|                     yield permission | ||||
|  | ||||
|     def has_perm(self, user_obj, perm, obj=None): | ||||
|         if user_obj.is_superuser: | ||||
|             return True | ||||
|  | ||||
|         if obj is None: | ||||
|             return False | ||||
|         perm = perm.split('_', 3) | ||||
|         perm_type = perm[1] | ||||
|         perm_field = perm[2] if len(perm) == 3 else None | ||||
|         return any(permission.applies(obj, perm_type, perm_field) for obj in self.permissions(user_obj, obj)) | ||||
|         return any(permission.applies(obj, perm_type, perm_field) for permission in self.permissions(user_obj)) | ||||
|  | ||||
|     def has_module_perms(self, user_obj, app_label): | ||||
|         return False | ||||
|  | ||||
|     def get_all_permissions(self, user_obj, obj=None): | ||||
|         if obj is None: | ||||
|             return [] | ||||
|         else: | ||||
|             return list(self.permissions(user_obj, obj)) | ||||
|         return list(self.permissions(user_obj)) | ||||
|   | ||||
| @@ -154,9 +154,9 @@ class Membership(models.Model): | ||||
|  | ||||
|     def valid(self): | ||||
|         if self.date_end is not None: | ||||
|             return self.date_start <= datetime.datetime.now() < self.date_end | ||||
|             return self.date_start.toordinal() <= datetime.datetime.now().toordinal() < self.date_end.toordinal() | ||||
|         else: | ||||
|             return self.date_start <= datetime.datetime.now() | ||||
|             return self.date_start.toordinal() <= datetime.datetime.now().toordinal() | ||||
|  | ||||
|     class Meta: | ||||
|         verbose_name = _('membership') | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-lateré | ||||
|  | ||||
| from django.contrib import admin | ||||
|  | ||||
| from .models import Permission | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| from django.apps import AppConfig | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| import functools | ||||
| import json | ||||
| import operator | ||||
| @@ -24,28 +27,25 @@ class InstancedPermission: | ||||
|         """ | ||||
|         if self.type == 'add': | ||||
|             if permission_type == self.type: | ||||
|                 return self.query(obj) | ||||
|                 return obj in self.model.modelclass().objects.get(self.query) | ||||
|         if ContentType.objects.get_for_model(obj) != self.model: | ||||
|             # The permission does not apply to the model | ||||
|             return False | ||||
|         if self.permission is None: | ||||
|             if permission_type == self.type: | ||||
|                 if field_name is not None: | ||||
|                     return field_name == self.field | ||||
|                 else: | ||||
|                     return True | ||||
|             else: | ||||
|         if permission_type == self.type: | ||||
|             if field_name and field_name != self.field: | ||||
|                 return False | ||||
|         elif obj in self.model.objects.get(self.query): | ||||
|             return True | ||||
|             return obj in self.model.model_class().objects.filter(self.query).all() | ||||
|         else: | ||||
|             return False | ||||
|  | ||||
|     def __repr__(self): | ||||
|         if self.field: | ||||
|             return _("Can {type} {model}.{field} in {permission}").format(type=self.type, model=self.model, field=self.field, permission=self.permission) | ||||
|             return _("Can {type} {model}.{field} in {query}").format(type=self.type, model=self.model, field=self.field, query=self.query) | ||||
|         else: | ||||
|             return _("Can {type} {model} in {permission}").format(type=self.type, model=self.model, permission=self.permission) | ||||
|             return _("Can {type} {model} in {query}").format(type=self.type, model=self.model, query=self.query) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.__repr__() | ||||
|  | ||||
|  | ||||
| class Permission(models.Model): | ||||
| @@ -61,24 +61,24 @@ class Permission(models.Model): | ||||
|  | ||||
|     # A json encoded Q object with the following grammar | ||||
|     #  query -> [] | {}  (the empty query representing all objects) | ||||
|     #  query -> ['AND', query, …]            AND multiple queries | ||||
|     #         | ['OR', query, …]             OR multiple queries | ||||
|     #         | ['NOT', query]               Opposite of query | ||||
|     #  query -> ["AND", query, …]            AND multiple queries | ||||
|     #         | ["OR", query, …]             OR multiple queries | ||||
|     #         | ["NOT", query]               Opposite of query | ||||
|     #  query -> {key: value, …}              A list of fields and values of a Q object | ||||
|     #  key   -> string                       A field name | ||||
|     #  value -> int | string | bool | null   Literal values | ||||
|     #         | [parameter]                  A parameter | ||||
|     #         | {'F': oper}                  An F object | ||||
|     #         | {"F": oper}                  An F object | ||||
|     #  oper  -> [string]                     A parameter | ||||
|     #         | ['ADD', oper, …]             Sum multiple F objects or literal | ||||
|     #         | ['SUB', oper, oper]          Substract two F objects or literal | ||||
|     #         | ['MUL', oper, …]             Multiply F objects or literals | ||||
|     #         | ["ADD", oper, …]             Sum multiple F objects or literal | ||||
|     #         | ["SUB", oper, oper]          Substract two F objects or literal | ||||
|     #         | ["MUL", oper, …]             Multiply F objects or literals | ||||
|     #         | int | string | bool | null   Literal values | ||||
|     #         | ['F', string]                A field | ||||
|     #         | ["F", string]                A field | ||||
|     # | ||||
|     # Examples: | ||||
|     #  Q(is_admin=True)  := {'is_admin': ['TYPE', 'bool', 'True']} | ||||
|     #  ~Q(is_admin=True) := ['NOT', {'is_admin': ['TYPE', 'bool', 'True']}] | ||||
|     #  Q(is_superuser=True)  := {"is_superuser": true} | ||||
|     #  ~Q(is_superuser=True) := ["NOT", {"is_superuser": true}] | ||||
|     query = models.TextField() | ||||
|  | ||||
|     type = models.CharField(max_length=15, choices=PERMISSION_TYPES) | ||||
| @@ -94,23 +94,22 @@ class Permission(models.Model): | ||||
|         if self.field and self.type not in {'view', 'change'}: | ||||
|             raise ValidationError(_("Specifying field applies only to view and change permission types.")) | ||||
|  | ||||
|     def save(self): | ||||
|     def save(self, **kwargs): | ||||
|         self.full_clean() | ||||
|         super().save() | ||||
|  | ||||
|     @staticmethod | ||||
|     def compute_f(_oper, **kwargs): | ||||
|         oper = _oper | ||||
|     def compute_f(oper, **kwargs): | ||||
|         if isinstance(oper, list): | ||||
|             if len(oper) == 1: | ||||
|                 return kwargs[oper[0]].pk | ||||
|             elif len(oper) >= 2: | ||||
|                 if oper[0] == 'ADD': | ||||
|                     return functools.reduce(operator.add, [compute_f(oper, **kwargs) for oper in oper[1:]]) | ||||
|                     return functools.reduce(operator.add, [Permission.compute_f(oper, **kwargs) for oper in oper[1:]]) | ||||
|                 elif oper[0] == 'SUB': | ||||
|                     return compute_f(oper[1], **kwargs) - compute_f(oper[2], **kwargs) | ||||
|                     return Permission.compute_f(oper[1], **kwargs) - Permission.compute_f(oper[2], **kwargs) | ||||
|                 elif oper[0] == 'MUL': | ||||
|                     return functools.reduce(operator.mul, [compute_f(oper, **kwargs) for oper in oper[1:]]) | ||||
|                     return functools.reduce(operator.mul, [Permission.compute_f(oper, **kwargs) for oper in oper[1:]]) | ||||
|                 elif oper[0] == 'F': | ||||
|                     return F(oper[1]) | ||||
|         else: | ||||
| @@ -118,9 +117,7 @@ class Permission(models.Model): | ||||
|         # TODO: find a better way to crash here | ||||
|         raise Exception("F is wrong") | ||||
|  | ||||
|     def _about(_self, _query, **kwargs): | ||||
|         self = _self | ||||
|         query = _query | ||||
|     def _about(self, query, **kwargs): | ||||
|         if self.type == 'add': | ||||
|             # Handle add permission differently | ||||
|             return self._about_add(query, **kwargs) | ||||
| @@ -145,7 +142,7 @@ class Permission(models.Model): | ||||
|                     q_kwargs[key] = kwargs[value[0]].pk | ||||
|                 elif isinstance(value, dict): | ||||
|                     # It is an F object | ||||
|                     q_kwargs[key] = compute_f(query['F'], **kwargs) | ||||
|                     q_kwargs[key] = Permission.compute_f(query['F'], **kwargs) | ||||
|                 else: | ||||
|                     q_kwargs[key] = value | ||||
|             return Q(**q_kwargs) | ||||
| @@ -153,16 +150,15 @@ class Permission(models.Model): | ||||
|             # TODO: find a better way to crash here | ||||
|             raise Exception("query {} is wrong".format(self.query)) | ||||
|  | ||||
|     def _about_add(_self, _query, **kwargs): | ||||
|         self = _self | ||||
|     def _about_add(self, _query, **kwargs): | ||||
|         query = _query | ||||
|         if len(query) == 0: | ||||
|             return lambda _: True | ||||
|         if isinstance(query, list): | ||||
|             if query[0] == 'AND': | ||||
|                 return lambda obj: functools.reduce(operator.and_, [self._about_add(query, **kwargs)(obj) for query in query[1:]]) | ||||
|                 return lambda obj: functools.reduce(operator.and_, [self._about_add(q, **kwargs)(obj) for q in query[1:]]) | ||||
|             elif query[0] == 'OR': | ||||
|                 return lambda obj: functools.reduce(operator.or_, [self._about_add(query, **kwargs)(obj) for query in query[1:]]) | ||||
|                 return lambda obj: functools.reduce(operator.or_, [self._about_add(q, **kwargs)(obj) for q in query[1:]]) | ||||
|             elif query[0] == 'NOT': | ||||
|                 return lambda obj: not self._about_add(query[1], **kwargs)(obj) | ||||
|         elif isinstance(query, dict): | ||||
| @@ -174,7 +170,7 @@ class Permission(models.Model): | ||||
|                     q_kwargs[key] = kwargs[value[0]].pk | ||||
|                 elif isinstance(value, dict): | ||||
|                     # It is an F object | ||||
|                     q_kwargs[key] = compute_f(query['F'], **kwargs) | ||||
|                     q_kwargs[key] = Permission.compute_f(query['F'], **kwargs) | ||||
|                 else: | ||||
|                     q_kwargs[key] = value | ||||
|             def func(obj): | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| from django.test import TestCase | ||||
|  | ||||
| # Create your tests here. | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| from django.shortcuts import render | ||||
|  | ||||
| # Create your views here. | ||||
|   | ||||
| @@ -37,7 +37,6 @@ INSTALLED_APPS = [ | ||||
|  | ||||
|     # External apps | ||||
|     'polymorphic', | ||||
|     'guardian', | ||||
|     'reversion', | ||||
|     'crispy_forms', | ||||
|     'django_tables2', | ||||
| @@ -134,8 +133,8 @@ PASSWORD_HASHERS = [ | ||||
| # Django Guardian object permissions | ||||
|  | ||||
| AUTHENTICATION_BACKENDS = ( | ||||
|     'django.contrib.auth.backends.ModelBackend',  # this is default | ||||
|     'guardian.backends.ObjectPermissionBackend', | ||||
|     #'django.contrib.auth.backends.ModelBackend',  # this is default | ||||
|     'member.backends.PermissionBackend', | ||||
|     'cas.backends.CASBackend', | ||||
| ) | ||||
|  | ||||
| @@ -153,8 +152,6 @@ REST_FRAMEWORK = { | ||||
|  | ||||
| ANONYMOUS_USER_NAME = None  # Disable guardian anonymous user | ||||
|  | ||||
| GUARDIAN_GET_CONTENT_TYPE = 'polymorphic.contrib.guardian.get_polymorphic_base_content_type' | ||||
|  | ||||
| # Internationalization | ||||
| # https://docs.djangoproject.com/en/2.2/topics/i18n/ | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,6 @@ django-cas-server==1.1.0 | ||||
| django-crispy-forms==1.7.2 | ||||
| django-extensions==2.1.9 | ||||
| django-filter==2.2.0 | ||||
| django-guardian==2.1.0 | ||||
| django-polymorphic==2.0.3 | ||||
| djangorestframework==3.9.0 | ||||
| django-rest-polymorphic==0.1.8 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user