1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2024-12-27 09:52:22 +00:00
nk20/apps/permission/backends.py
charliep a6b479db19 Update 131 files
- /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
2024-02-07 02:26:49 +01:00

239 lines
8.6 KiB
Python

# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import date
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, F
from django.utils import timezone
from note.models import Note, NoteUser, NoteClub, NoteSpecial
from note_kfet.middlewares import get_current_request
from member.models import Membership, Club
from .decorators import memoize
from .models import Permission
class PermissionBackend(ModelBackend):
"""
Manage permissions of users
"""
supports_object_permissions = True
supports_anonymous_user = False
supports_inactive_user = False
@staticmethod
@memoize
def get_raw_permissions(request, t):
"""
Query permissions of a certain type for a user, then memoize it.
:param request: The current request
:param t: The type of the permissions: view, change, add or delete
:return: The queryset of the permissions of the user (memoized) grouped by clubs
"""
if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'):
# OAuth2 Authentication
user = request.auth.user
def permission_filter(membership_obj):
query = Q(pk=-1)
for scope in request.auth.scope.split(' '):
permission_id, club_id = scope.split('_')
if int(club_id) == membership_obj.club_id:
query |= Q(pk=permission_id)
return query
else:
user = request.user
def permission_filter(membership_obj):
return Q(mask__rank__lte=request.session.get("permission_mask", 42))
if user.is_anonymous:
# Unauthenticated users have no permissions
return Permission.objects.none()
memberships = Membership.objects.filter(user=user).all()
perms = []
for membership in memberships:
for role in membership.roles.all():
for perm in role.permissions.filter(permission_filter(membership), type=t).all():
if not perm.permanent:
if membership.date_start > date.today() or membership.date_end < date.today():
continue
perm.membership = membership
perms.append(perm)
return perms
@staticmethod
def permissions(request, model, type):
"""
List all permissions of the given user that applies to a given model and a give type
:param request: The current request
:param model: The model that the permissions shoud apply
:param type: The type of the permissions: view, change, add or delete
:return: A generator of the requested permissions
"""
if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'):
# OAuth2 Authentication
user = request.auth.user
else:
user = request.user
for permission in PermissionBackend.get_raw_permissions(request, type):
if not isinstance(model.model_class()(), permission.model.model_class()) or not permission.membership:
continue
membership = permission.membership
club = membership.club
permission = permission.about(
user=user,
club=club,
membership=membership,
User=User,
Club=Club,
Membership=Membership,
Note=Note,
NoteUser=NoteUser,
NoteClub=NoteClub,
NoteSpecial=NoteSpecial,
F=F,
Q=Q,
now=timezone.now(),
today=date.today(),
)
yield permission
@staticmethod
@memoize
def filter_queryset(request, model, t, field=None):
"""
Filter a queryset by considering the permissions of a given user.
:param request: The current request
:param model: The concerned model of the queryset
:param t: The type of modification (view, add, change, delete)
:param field: The field of the model to test, if concerned
:return: A query that corresponds to the filter to give to a queryset
"""
if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'):
# OAuth2 Authentication
user = request.auth.user
else:
user = request.user
if user is None or user.is_anonymous:
# Anonymous users can't do anything
return Q(pk=-1)
if user.is_superuser and request.session.get("permission_mask", -1) >= 42:
# Superusers have all rights
return Q()
if not isinstance(model, ContentType):
model = ContentType.objects.get_for_model(model)
# Never satisfied
query = Q(pk=-1)
perms = PermissionBackend.permissions(request, model, t)
for perm in perms:
if perm.field and field != perm.field:
continue
if perm.type != t or perm.model != model:
continue
perm.update_query()
query = query | perm.query
return query
@staticmethod
@memoize
def check_perm(request, perm, obj=None):
"""
Check is the given user has the permission over a given object.
The result is then memoized.
Exception: for add permissions, since the object is not hashable since it doesn't have any
primary key, the result is not memoized. Moreover, the right could change
(e.g. for a transaction, the balance of the user could change)
"""
# Requested by a shell
if request is None:
return False
user_obj = request.user
sess = request.session
if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'):
# OAuth2 Authentication
user_obj = request.auth.user
if user_obj is None or user_obj.is_anonymous:
return False
if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42:
return True
if obj is None:
return True
perm = perm.split('.')[-1].split('_', 2)
perm_type = perm[0]
perm_field = perm[2] if len(perm) == 3 else None
ct = ContentType.objects.get_for_model(obj)
if any(permission.applies(obj, perm_type, perm_field)
for permission in PermissionBackend.permissions(request, ct, perm_type)):
return True
return False
def has_perm(self, user_obj, perm, obj=None):
# Warning: this does not check that user_obj has the permission,
# but if the current request has the permission.
# This function is implemented for backward compatibility, and should not be used.
return PermissionBackend.check_perm(get_current_request(), perm, obj)
def has_module_perms(self, user_obj, app_label):
return False
@staticmethod
@memoize
def has_model_perm(request, model, type):
"""
Check is the given user has the permission over a given model for a given action.
The result is then memoized.
:param request: The current request
:param model: The model that the permissions shoud apply
:param type: The type of the permissions: view, change, add or delete
For view action, it is consider possible if user can view or change the model
"""
# Requested by a shell
if request is None:
return False
user_obj = request.user
sess = request.session
if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'):
# OAuth2 Authentication
user_obj = request.auth.user
if user_obj is None or user_obj.is_anonymous:
return False
if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42:
return True
ct = ContentType.objects.get_for_model(model)
if any(PermissionBackend.permissions(request, ct, type)):
return True
if type == "view" and any(PermissionBackend.permissions(request, ct, "change")):
return True
return False
def get_all_permissions(self, user_obj, obj=None):
ct = ContentType.objects.get_for_model(obj)
return list(self.permissions(get_current_request(), ct, "view"))