mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-06-21 01:48:21 +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:
@ -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
@ -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):
|
||||
|
@ -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
10
apps/permission/urls.py
Normal 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"),
|
||||
]
|
@ -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
|
||||
|
Reference in New Issue
Block a user