1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-21 09:58:23 +02:00

Merge branch 'master' into 'tranfer_front'

# Conflicts:
#   apps/activity/views.py
#   apps/permission/backends.py
#   locale/de/LC_MESSAGES/django.po
#   locale/fr/LC_MESSAGES/django.po
#   static/js/base.js
#   templates/base.html
#   templates/member/user_list.html
This commit is contained in:
ynerant
2020-05-07 18:48:35 +02:00
113 changed files with 10399 additions and 2120 deletions

View File

@ -37,21 +37,16 @@ class PermissionBackend(ModelBackend):
# Unauthenticated users have no permissions
return Permission.objects.none()
perms = Permission.objects.annotate(club=F("rolepermissions__role__membership__club")) \
.filter(
rolepermissions__role__membership__user=user,
rolepermissions__role__membership__date_start__lte=datetime.date.today(),
rolepermissions__role__membership__date_end__gte=datetime.date.today(),
type=t,
mask__rank__lte=get_current_session().get("permission_mask", 42),
)
if settings.DATABASES[perms.db]["ENGINE"] == 'django.db.backends.postgresql_psycopg2':
# We want one permission per club, and per permission type.
# SQLite does not support this kind of filter, that's why we don't filter the permissions with this
# kind of DB. This only increases performances (we can check multiple times the same permission)
# but don't have any semantic influence.
perms = perms.distinct('club', 'pk')
return perms
return Permission.objects.annotate(
club=F("rolepermissions__role__membership__club"),
membership=F("rolepermissions__role__membership"),
).filter(
rolepermissions__role__membership__user=user,
rolepermissions__role__membership__date_start__lte=datetime.date.today(),
rolepermissions__role__membership__date_end__gte=datetime.date.today(),
type=t,
mask__rank__lte=get_current_session().get("permission_mask", 0),
).distinct()
@staticmethod
def permissions(user, model, type):
@ -63,6 +58,7 @@ class PermissionBackend(ModelBackend):
:return: A generator of the requested permissions
"""
clubs = {}
memberships = {}
for permission in PermissionBackend.get_raw_permissions(user, type):
if not isinstance(model.model_class()(), permission.model.model_class()) or not permission.club:
@ -72,9 +68,16 @@ class PermissionBackend(ModelBackend):
clubs[permission.club] = club = Club.objects.get(pk=permission.club)
else:
club = clubs[permission.club]
if permission.membership not in memberships:
memberships[permission.membership] = membership = Membership.objects.get(pk=permission.membership)
else:
membership = memberships[permission.membership]
permission = permission.about(
user=user,
club=club,
membership=membership,
User=User,
Club=Club,
Membership=Membership,
@ -83,7 +86,9 @@ class PermissionBackend(ModelBackend):
NoteClub=NoteClub,
NoteSpecial=NoteSpecial,
F=F,
Q=Q
Q=Q,
now=datetime.datetime.now(),
today=datetime.date.today(),
)
yield permission

File diff suppressed because it is too large Load Diff

View File

@ -120,7 +120,12 @@ class Permission(models.Model):
('delete', 'delete')
]
model = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name='+')
model = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
related_name='+',
verbose_name=_("model"),
)
# A json encoded Q object with the following grammar
# query -> [] | {} (the empty query representing all objects)
@ -142,18 +147,34 @@ class Permission(models.Model):
# Examples:
# Q(is_superuser=True) := {"is_superuser": true}
# ~Q(is_superuser=True) := ["NOT", {"is_superuser": true}]
query = models.TextField()
query = models.TextField(
verbose_name=_("query"),
)
type = models.CharField(max_length=15, choices=PERMISSION_TYPES)
type = models.CharField(
max_length=15,
choices=PERMISSION_TYPES,
verbose_name=_("type"),
)
mask = models.ForeignKey(
PermissionMask,
on_delete=models.PROTECT,
related_name="permissions",
verbose_name=_("mask"),
)
field = models.CharField(max_length=255, blank=True)
field = models.CharField(
max_length=255,
blank=True,
verbose_name=_("field"),
)
description = models.CharField(max_length=255, blank=True)
description = models.CharField(
max_length=255,
blank=True,
verbose_name=_("description"),
)
class Meta:
unique_together = ('model', 'query', 'type', 'field')
@ -277,24 +298,22 @@ class Permission(models.Model):
return InstancedPermission(self.model, query, self.type, self.field, self.mask, **kwargs)
def __str__(self):
if self.field:
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 {query}").format(type=self.type, model=self.model, query=self.query)
return self.description
class RolePermissions(models.Model):
"""
Permissions associated with a Role
"""
role = models.ForeignKey(
role = models.OneToOneField(
Role,
on_delete=models.PROTECT,
related_name='+',
related_name='permissions',
verbose_name=_('role'),
)
permissions = models.ManyToManyField(
Permission,
verbose_name=_("permissions"),
)
def __str__(self):

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.core.exceptions import PermissionDenied
from django.utils.translation import gettext_lazy as _
from note_kfet.middlewares import get_current_authenticated_user
from permission.backends import PermissionBackend
@ -57,13 +58,19 @@ def pre_save_object(sender, instance, **kwargs):
if old_value == new_value:
continue
if not PermissionBackend.check_perm(user, app_label + ".change_" + model_name + "_" + field_name, instance):
raise PermissionDenied
raise PermissionDenied(
_("You don't have the permission to change the field {field} on this instance of model"
" {app_label}.{model_name}.")
.format(field=field_name, app_label=app_label, model_name=model_name, )
)
else:
# We check if the user has right to add the object
has_perm = PermissionBackend.check_perm(user, app_label + ".add_" + model_name, instance)
if not has_perm:
raise PermissionDenied
raise PermissionDenied(
_("You don't have the permission to add this instance of model {app_label}.{model_name}.")
.format(app_label=app_label, model_name=model_name, ))
def pre_delete_object(instance, **kwargs):
@ -88,4 +95,6 @@ def pre_delete_object(instance, **kwargs):
# We check if the user has rights to delete the object
if not PermissionBackend.check_perm(user, app_label + ".delete_" + model_name, instance):
raise PermissionDenied
raise PermissionDenied(
_("You don't have the permission to delete this instance of model {app_label}.{model_name}.")
.format(app_label=app_label, model_name=model_name))

10
apps/permission/urls.py Normal file
View File

@ -0,0 +1,10 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.urls import path
from permission.views import RightsView
app_name = 'permission'
urlpatterns = [
path('rights', RightsView.as_view(), name="rights"),
]

View File

@ -1,11 +1,60 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import date
from permission.backends import PermissionBackend
from django.forms import HiddenInput
from django.utils.translation import gettext_lazy as _
from django.views.generic import UpdateView, TemplateView
from member.models import Role, Membership
from .backends import PermissionBackend
class ProtectQuerysetMixin:
"""
This is a View class decorator and not a proper View class.
Ensure that the user has the right to see or update objects.
Display 404 error if the user can't see an object, remove the fields the user can't
update on an update form (useful if the user can't change only specified fields).
"""
def get_queryset(self, **kwargs):
qs = super().get_queryset(**kwargs)
return qs.filter(PermissionBackend.filter_queryset(self.request.user, qs.model, "view"))
def get_form(self, form_class=None):
form = super().get_form(form_class)
if not isinstance(self, UpdateView):
return form
# If we are in an UpdateView, we display only the fields the user has right to see.
# No worry if the user change the hidden fields: a 403 error will be performed if the user tries to make
# a custom request.
# We could also delete the field, but some views might be affected.
for key in form.base_fields:
if not PermissionBackend.check_perm(self.request.user, "wei.change_weiregistration_" + key, self.object):
form.fields[key].widget = HiddenInput()
return form
class RightsView(TemplateView):
template_name = "permission/all_rights.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = _("All rights")
roles = Role.objects.all()
context["roles"] = roles
if self.request.user.is_authenticated:
active_memberships = Membership.objects.filter(user=self.request.user,
date_start__lte=date.today(),
date_end__gte=date.today()).all()
else:
active_memberships = Membership.objects.none()
for role in roles:
role.clubs = [membership.club for membership in active_memberships if role in membership.roles.all()]
return context