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