mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-11-30 04:13:01 +00:00
a6b479db19
- /apps/activity/api/serializers.py - /apps/activity/api/urls.py - /apps/activity/api/views.py - /apps/activity/tests/test_activities.py - /apps/activity/__init__.py - /apps/activity/admin.py - /apps/activity/apps.py - /apps/activity/forms.py - /apps/activity/tables.py - /apps/activity/urls.py - /apps/activity/views.py - /apps/api/__init__.py - /apps/api/apps.py - /apps/api/serializers.py - /apps/api/tests.py - /apps/api/urls.py - /apps/api/views.py - /apps/api/viewsets.py - /apps/logs/signals.py - /apps/logs/apps.py - /apps/logs/__init__.py - /apps/logs/api/serializers.py - /apps/logs/api/urls.py - /apps/logs/api/views.py - /apps/member/api/serializers.py - /apps/member/api/urls.py - /apps/member/api/views.py - /apps/member/templatetags/memberinfo.py - /apps/member/__init__.py - /apps/member/admin.py - /apps/member/apps.py - /apps/member/auth.py - /apps/member/forms.py - /apps/member/hashers.py - /apps/member/signals.py - /apps/member/tables.py - /apps/member/urls.py - /apps/member/views.py - /apps/note/api/serializers.py - /apps/note/api/urls.py - /apps/note/api/views.py - /apps/note/models/__init__.py - /apps/note/static/note/js/consos.js - /apps/note/templates/note/mails/negative_balance.txt - /apps/note/templatetags/getenv.py - /apps/note/templatetags/pretty_money.py - /apps/note/tests/test_transactions.py - /apps/note/__init__.py - /apps/note/admin.py - /apps/note/apps.py - /apps/note/forms.py - /apps/note/signals.py - /apps/note/tables.py - /apps/note/urls.py - /apps/note/views.py - /apps/permission/api/serializers.py - /apps/permission/api/urls.py - /apps/permission/api/views.py - /apps/permission/templatetags/perms.py - /apps/permission/tests/test_oauth2.py - /apps/permission/tests/test_permission_denied.py - /apps/permission/tests/test_permission_queries.py - /apps/permission/tests/test_rights_page.py - /apps/permission/__init__.py - /apps/permission/admin.py - /apps/permission/backends.py - /apps/permission/apps.py - /apps/permission/decorators.py - /apps/permission/permissions.py - /apps/permission/scopes.py - /apps/permission/signals.py - /apps/permission/tables.py - /apps/permission/urls.py - /apps/permission/views.py - /apps/registration/tests/test_registration.py - /apps/registration/__init__.py - /apps/registration/apps.py - /apps/registration/forms.py - /apps/registration/tables.py - /apps/registration/tokens.py - /apps/registration/urls.py - /apps/registration/views.py - /apps/treasury/api/serializers.py - /apps/treasury/api/urls.py - /apps/treasury/api/views.py - /apps/treasury/templatetags/escape_tex.py - /apps/treasury/tests/test_treasury.py - /apps/treasury/__init__.py - /apps/treasury/admin.py - /apps/treasury/apps.py - /apps/treasury/forms.py - /apps/treasury/signals.py - /apps/treasury/tables.py - /apps/treasury/urls.py - /apps/treasury/views.py - /apps/wei/api/serializers.py - /apps/wei/api/urls.py - /apps/wei/api/views.py - /apps/wei/forms/surveys/__init__.py - /apps/wei/forms/surveys/base.py - /apps/wei/forms/surveys/wei2021.py - /apps/wei/forms/surveys/wei2022.py - /apps/wei/forms/surveys/wei2023.py - /apps/wei/forms/__init__.py - /apps/wei/forms/registration.py - /apps/wei/management/commands/export_wei_registrations.py - /apps/wei/management/commands/import_scores.py - /apps/wei/management/commands/wei_algorithm.py - /apps/wei/templates/wei/weilist_sample.tex - /apps/wei/tests/test_wei_algorithm_2021.py - /apps/wei/tests/test_wei_algorithm_2022.py - /apps/wei/tests/test_wei_algorithm_2023.py - /apps/wei/tests/test_wei_registration.py - /apps/wei/__init__.py - /apps/wei/admin.py - /apps/wei/apps.py - /apps/wei/tables.py - /apps/wei/urls.py - /apps/wei/views.py - /note_kfet/settings/__init__.py - /note_kfet/settings/base.py - /note_kfet/settings/development.py - /note_kfet/settings/secrets_example.py - /note_kfet/static/js/base.js - /note_kfet/admin.py - /note_kfet/inputs.py - /note_kfet/middlewares.py - /note_kfet/urls.py - /note_kfet/views.py - /note_kfet/wsgi.py - /entrypoint.sh
169 lines
7.4 KiB
Python
169 lines
7.4 KiB
Python
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
from collections import OrderedDict
|
|
from datetime import date
|
|
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.contrib.auth.models import User
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.db import transaction
|
|
from django.db.models import Q
|
|
from django.forms import HiddenInput
|
|
from django.http import Http404
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.views.generic import UpdateView, TemplateView, CreateView
|
|
from member.models import Membership
|
|
|
|
from .backends import PermissionBackend
|
|
from .models import Role
|
|
from .tables import RightsTable, SuperuserTable
|
|
|
|
|
|
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, filter_permissions=True, **kwargs):
|
|
qs = super().get_queryset(**kwargs)
|
|
return qs.filter(PermissionBackend.filter_queryset(self.request, qs.model, "view")).distinct()\
|
|
if filter_permissions else qs
|
|
|
|
def get_object(self, queryset=None):
|
|
try:
|
|
return super().get_object(queryset)
|
|
except Http404 as e:
|
|
try:
|
|
super().get_object(self.get_queryset(filter_permissions=False))
|
|
raise PermissionDenied()
|
|
except Http404:
|
|
raise e
|
|
|
|
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.
|
|
meta = form.instance._meta
|
|
for key in form.base_fields:
|
|
if not PermissionBackend.check_perm(self.request,
|
|
f"{meta.app_label}.change_{meta.model_name}_" + key, self.object):
|
|
form.fields[key].widget = HiddenInput()
|
|
|
|
return form
|
|
|
|
@transaction.atomic
|
|
def form_valid(self, form):
|
|
"""
|
|
Submit the form, if the page is a FormView.
|
|
If a PermissionDenied exception is raised, catch the error and display it at the top of the form.
|
|
"""
|
|
try:
|
|
return super().form_valid(form)
|
|
except PermissionDenied:
|
|
if isinstance(self, UpdateView):
|
|
form.add_error(None, _("You don't have the permission to update this instance of the model \"{model}\""
|
|
" with these parameters. Please correct your data and retry.")
|
|
.format(model=self.model._meta.verbose_name))
|
|
else:
|
|
form.add_error(None, _("You don't have the permission to create an instance of the model \"{model}\""
|
|
" with these parameters. Please correct your data and retry.")
|
|
.format(model=self.model._meta.verbose_name))
|
|
return self.form_invalid(form)
|
|
|
|
|
|
class ProtectedCreateView(LoginRequiredMixin, CreateView):
|
|
"""
|
|
Extends a CreateView to check is the user has the right to create a sample instance of the given Model.
|
|
If not, a 403 error is displayed.
|
|
"""
|
|
|
|
def get_sample_object(self): # pragma: no cover
|
|
"""
|
|
return a sample instance of the Model.
|
|
It should be valid (can be stored properly in database), but must not collide with existing data.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
# Check that the user is authenticated before that he/she has the permission to access here
|
|
if not request.user.is_authenticated:
|
|
return self.handle_no_permission()
|
|
|
|
model_class = self.model
|
|
# noinspection PyProtectedMember
|
|
app_label, model_name = model_class._meta.app_label, model_class._meta.model_name.lower()
|
|
perm = app_label + ".add_" + model_name
|
|
if not PermissionBackend.check_perm(request, perm, self.get_sample_object()):
|
|
raise PermissionDenied(_("You don't have the permission to add an instance of model "
|
|
"{app_label}.{model_name}.").format(app_label=app_label, model_name=model_name))
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
|
|
class RightsView(TemplateView):
|
|
template_name = "permission/all_rights.html"
|
|
extra_context = {"title": _("Rights")}
|
|
|
|
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()]
|
|
|
|
if self.request.user.is_authenticated:
|
|
special_memberships = Membership.objects.filter(
|
|
date_start__lte=date.today(),
|
|
date_end__gte=date.today(),
|
|
).filter(roles__in=Role.objects.filter((~(Q(name="Adhérent BDE")
|
|
| Q(name="Adhérent Kfet")
|
|
| Q(name="Membre de club")
|
|
| Q(name="Bureau de club"))
|
|
& Q(weirole__isnull=True))))\
|
|
.order_by("club__name", "user__last_name")\
|
|
.distinct().all()
|
|
context["special_memberships_table"] = RightsTable(special_memberships, prefix="clubs-")
|
|
context["superusers"] = SuperuserTable(User.objects.filter(is_superuser=True).order_by("last_name").all(),
|
|
prefix="superusers-")
|
|
|
|
return context
|
|
|
|
|
|
class ScopesView(LoginRequiredMixin, TemplateView):
|
|
template_name = "permission/scopes.html"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
from oauth2_provider.models import Application
|
|
from .scopes import PermissionScopes
|
|
|
|
scopes = PermissionScopes()
|
|
context["scopes"] = {}
|
|
all_scopes = scopes.get_all_scopes()
|
|
for app in Application.objects.filter(user=self.request.user).all():
|
|
available_scopes = scopes.get_available_scopes(app)
|
|
context["scopes"][app] = OrderedDict()
|
|
items = [(k, v) for (k, v) in all_scopes.items() if k in available_scopes]
|
|
items.sort(key=lambda x: (int(x[0].split("_")[1]), int(x[0].split("_")[0])))
|
|
for k, v in items:
|
|
context["scopes"][app][k] = v
|
|
|
|
return context
|