Check permissions per request instead of per user

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
Yohann D'ANELLO 2021-06-15 14:40:32 +02:00
parent 5e9f36ef1a
commit ea092803d7
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
25 changed files with 207 additions and 203 deletions

View File

@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _
from member.models import Club from member.models import Club
from note.models import Note, NoteUser from note.models import Note, NoteUser
from note_kfet.inputs import Autocomplete, DateTimePickerInput from note_kfet.inputs import Autocomplete, DateTimePickerInput
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from .models import Activity, Guest from .models import Activity, Guest
@ -24,7 +24,7 @@ class ActivityForm(forms.ModelForm):
self.fields["attendees_club"].initial = Club.objects.get(name="Kfet") self.fields["attendees_club"].initial = Club.objects.get(name="Kfet")
self.fields["attendees_club"].widget.attrs["placeholder"] = "Kfet" self.fields["attendees_club"].widget.attrs["placeholder"] = "Kfet"
clubs = list(Club.objects.filter(PermissionBackend clubs = list(Club.objects.filter(PermissionBackend
.filter_queryset(get_current_authenticated_user(), Club, "view")).all()) .filter_queryset(get_current_request(), Club, "view")).all())
shuffle(clubs) shuffle(clubs)
self.fields["organizer"].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..." self.fields["organizer"].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..."

View File

@ -74,12 +74,12 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView
upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now()) upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now())
context['upcoming'] = ActivityTable( context['upcoming'] = ActivityTable(
data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")), data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request, Activity, "view")),
prefix='upcoming-', prefix='upcoming-',
) )
started_activities = Activity.objects\ started_activities = Activity.objects\
.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ .filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))\
.filter(open=True, valid=True).all() .filter(open=True, valid=True).all()
context["started_activities"] = started_activities context["started_activities"] = started_activities
@ -98,7 +98,7 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context = super().get_context_data() context = super().get_context_data()
table = GuestTable(data=Guest.objects.filter(activity=self.object) table = GuestTable(data=Guest.objects.filter(activity=self.object)
.filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))) .filter(PermissionBackend.filter_queryset(self.request, Guest, "view")))
context["guests"] = table context["guests"] = table
context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start) context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start)
@ -144,7 +144,7 @@ class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView):
def get_form(self, form_class=None): def get_form(self, form_class=None):
form = super().get_form(form_class) form = super().get_form(form_class)
form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))\
.get(pk=self.kwargs["pk"]) .get(pk=self.kwargs["pk"])
form.fields["inviter"].initial = self.request.user.note form.fields["inviter"].initial = self.request.user.note
return form return form
@ -152,7 +152,7 @@ class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView):
@transaction.atomic @transaction.atomic
def form_valid(self, form): def form_valid(self, form):
form.instance.activity = Activity.objects\ form.instance.activity = Activity.objects\
.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"]) .filter(PermissionBackend.filter_queryset(self.request, Activity, "view")).get(pk=self.kwargs["pk"])
return super().form_valid(form) return super().form_valid(form)
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):
@ -173,7 +173,7 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
activity = Activity.objects.get(pk=self.kwargs["pk"]) activity = Activity.objects.get(pk=self.kwargs["pk"])
sample_entry = Entry(activity=activity, note=self.request.user.note) sample_entry = Entry(activity=activity, note=self.request.user.note)
if not PermissionBackend.check_perm(self.request.user, "activity.add_entry", sample_entry): if not PermissionBackend.check_perm(self.request, "activity.add_entry", sample_entry):
raise PermissionDenied(_("You are not allowed to display the entry interface for this activity.")) raise PermissionDenied(_("You are not allowed to display the entry interface for this activity."))
if not activity.activity_type.manage_entries: if not activity.activity_type.manage_entries:
@ -191,7 +191,7 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
guest_qs = Guest.objects\ guest_qs = Guest.objects\
.annotate(balance=F("inviter__balance"), note_name=F("inviter__user__username"))\ .annotate(balance=F("inviter__balance"), note_name=F("inviter__user__username"))\
.filter(activity=activity)\ .filter(activity=activity)\
.filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))\ .filter(PermissionBackend.filter_queryset(self.request, Guest, "view"))\
.order_by('last_name', 'first_name').distinct() .order_by('last_name', 'first_name').distinct()
if "search" in self.request.GET and self.request.GET["search"]: if "search" in self.request.GET and self.request.GET["search"]:
@ -230,7 +230,7 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
) )
# Filter with permission backend # Filter with permission backend
note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")) note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request, Alias, "view"))
if "search" in self.request.GET and self.request.GET["search"]: if "search" in self.request.GET and self.request.GET["search"]:
pattern = self.request.GET["search"] pattern = self.request.GET["search"]
@ -256,7 +256,7 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
""" """
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))\
.distinct().get(pk=self.kwargs["pk"]) .distinct().get(pk=self.kwargs["pk"])
context["activity"] = activity context["activity"] = activity
@ -281,9 +281,9 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk
activities_open = Activity.objects.filter(open=True).filter( activities_open = Activity.objects.filter(open=True).filter(
PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all() PermissionBackend.filter_queryset(self.request, Activity, "view")).distinct().all()
context["activities_open"] = [a for a in activities_open context["activities_open"] = [a for a in activities_open
if PermissionBackend.check_perm(self.request.user, if PermissionBackend.check_perm(self.request,
"activity.add_entry", "activity.add_entry",
Entry(activity=a, note=self.request.user.note,))] Entry(activity=a, note=self.request.user.note,))]

View File

@ -9,7 +9,6 @@ from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from note_kfet.middlewares import get_current_session
from note.models import Alias from note.models import Alias
from .serializers import UserSerializer, ContentTypeSerializer from .serializers import UserSerializer, ContentTypeSerializer
@ -25,9 +24,8 @@ class ReadProtectedModelViewSet(ModelViewSet):
self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class() self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class()
def get_queryset(self): def get_queryset(self):
user = self.request.user self.request.session.setdefault("permission_mask", 42)
get_current_session().setdefault("permission_mask", 42) return self.queryset.filter(PermissionBackend.filter_queryset(self.request, self.model, "view")).distinct()
return self.queryset.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct()
class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet): class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet):
@ -40,9 +38,8 @@ class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet):
self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class() self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class()
def get_queryset(self): def get_queryset(self):
user = self.request.user self.request.session.setdefault("permission_mask", 42)
get_current_session().setdefault("permission_mask", 42) return self.queryset.filter(PermissionBackend.filter_queryset(self.request, self.model, "view")).distinct()
return self.queryset.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct()
class UserViewSet(ReadProtectedModelViewSet): class UserViewSet(ReadProtectedModelViewSet):

View File

@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from note.models import NoteUser, Alias from note.models import NoteUser, Alias
from note_kfet.middlewares import get_current_authenticated_user, get_current_ip from note_kfet.middlewares import get_current_request
from .models import Changelog from .models import Changelog
@ -57,9 +57,9 @@ def save_object(sender, instance, **kwargs):
previous = instance._previous previous = instance._previous
# Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP # Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
user, ip = get_current_authenticated_user(), get_current_ip() request = get_current_request()
if user is None: if request is None:
# Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py` # Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
# On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée # On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
# IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info # IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
@ -71,9 +71,23 @@ def save_object(sender, instance, **kwargs):
# else: # else:
if note.exists(): if note.exists():
user = note.get().user user = note.get().user
else:
user = None
else:
user = request.user
if 'HTTP_X_REAL_IP' in request.META:
ip = request.META.get('HTTP_X_REAL_IP')
elif 'HTTP_X_FORWARDED_FOR' in request.META:
ip = request.META.get('HTTP_X_FORWARDED_FOR').split(', ')[0]
else:
ip = request.META.get('REMOTE_ADDR')
if not user.is_authenticated:
# For registration purposes
user = None
# noinspection PyProtectedMember # noinspection PyProtectedMember
if user is not None and instance._meta.label_lower == "auth.user" and previous: if request is not None and instance._meta.label_lower == "auth.user" and previous:
# On n'enregistre pas les connexions # On n'enregistre pas les connexions
if instance.last_login != previous.last_login: if instance.last_login != previous.last_login:
return return
@ -121,9 +135,9 @@ def delete_object(sender, instance, **kwargs):
return return
# Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP # Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
user, ip = get_current_authenticated_user(), get_current_ip() request = get_current_request()
if user is None: if request is None:
# Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py` # Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
# On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée # On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
# IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info # IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
@ -135,6 +149,16 @@ def delete_object(sender, instance, **kwargs):
# else: # else:
if note.exists(): if note.exists():
user = note.get().user user = note.get().user
else:
user = None
else:
user = request.user
if 'HTTP_X_REAL_IP' in request.META:
ip = request.META.get('HTTP_X_REAL_IP')
elif 'HTTP_X_FORWARDED_FOR' in request.META:
ip = request.META.get('HTTP_X_FORWARDED_FOR').split(', ')[0]
else:
ip = request.META.get('REMOTE_ADDR')
# On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
class CustomSerializer(ModelSerializer): class CustomSerializer(ModelSerializer):

View File

@ -6,7 +6,7 @@ import hashlib
from django.conf import settings from django.conf import settings
from django.contrib.auth.hashers import PBKDF2PasswordHasher from django.contrib.auth.hashers import PBKDF2PasswordHasher
from django.utils.crypto import constant_time_compare from django.utils.crypto import constant_time_compare
from note_kfet.middlewares import get_current_authenticated_user, get_current_session from note_kfet.middlewares import get_current_request
class CustomNK15Hasher(PBKDF2PasswordHasher): class CustomNK15Hasher(PBKDF2PasswordHasher):
@ -24,16 +24,22 @@ class CustomNK15Hasher(PBKDF2PasswordHasher):
def must_update(self, encoded): def must_update(self, encoded):
if settings.DEBUG: if settings.DEBUG:
current_user = get_current_authenticated_user() # Small hack to let superusers to impersonate people.
# Don't change their password.
request = get_current_request()
current_user = request.user
if current_user is not None and current_user.is_superuser: if current_user is not None and current_user.is_superuser:
return False return False
return True return True
def verify(self, password, encoded): def verify(self, password, encoded):
if settings.DEBUG: if settings.DEBUG:
current_user = get_current_authenticated_user() # Small hack to let superusers to impersonate people.
# If a superuser is already connected, let him/her log in as another person.
request = get_current_request()
current_user = request.user
if current_user is not None and current_user.is_superuser\ if current_user is not None and current_user.is_superuser\
and get_current_session().get("permission_mask", -1) >= 42: and request.session.get("permission_mask", -1) >= 42:
return True return True
if '|' in encoded: if '|' in encoded:
@ -51,8 +57,11 @@ class DebugSuperuserBackdoor(PBKDF2PasswordHasher):
def verify(self, password, encoded): def verify(self, password, encoded):
if settings.DEBUG: if settings.DEBUG:
current_user = get_current_authenticated_user() # Small hack to let superusers to impersonate people.
# If a superuser is already connected, let him/her log in as another person.
request = get_current_request()
current_user = request.user
if current_user is not None and current_user.is_superuser\ if current_user is not None and current_user.is_superuser\
and get_current_session().get("permission_mask", -1) >= 42: and request.session.get("permission_mask", -1) >= 42:
return True return True
return super().verify(password, encoded) return super().verify(password, encoded)

View File

@ -9,7 +9,7 @@ from django.utils.translation import gettext_lazy as _
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.html import format_html from django.utils.html import format_html
from note.templatetags.pretty_money import pretty_money from note.templatetags.pretty_money import pretty_money
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from .models import Club, Membership from .models import Club, Membership
@ -51,19 +51,19 @@ class UserTable(tables.Table):
def render_email(self, record, value): def render_email(self, record, value):
# Replace the email by a dash if the user can't see the profile detail # Replace the email by a dash if the user can't see the profile detail
# Replace also the URL # Replace also the URL
if not PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile): if not PermissionBackend.check_perm(get_current_request(), "member.view_profile", record.profile):
value = "" value = ""
record.email = value record.email = value
return value return value
def render_section(self, record, value): def render_section(self, record, value):
return value \ return value \
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile) \ if PermissionBackend.check_perm(get_current_request(), "member.view_profile", record.profile) \
else "" else ""
def render_balance(self, record, value): def render_balance(self, record, value):
return pretty_money(value)\ return pretty_money(value)\
if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", record.note) else "" if PermissionBackend.check_perm(get_current_request(), "note.view_note", record.note) else ""
class Meta: class Meta:
attrs = { attrs = {
@ -93,7 +93,7 @@ class MembershipTable(tables.Table):
def render_user(self, value): def render_user(self, value):
# If the user has the right, link the displayed user with the page of its detail. # If the user has the right, link the displayed user with the page of its detail.
s = value.username s = value.username
if PermissionBackend.check_perm(get_current_authenticated_user(), "auth.view_user", value): if PermissionBackend.check_perm(get_current_request(), "auth.view_user", value):
s = format_html("<a href={url}>{name}</a>", s = format_html("<a href={url}>{name}</a>",
url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s) url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s)
@ -102,7 +102,7 @@ class MembershipTable(tables.Table):
def render_club(self, value): def render_club(self, value):
# If the user has the right, link the displayed club with the page of its detail. # If the user has the right, link the displayed club with the page of its detail.
s = value.name s = value.name
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_club", value): if PermissionBackend.check_perm(get_current_request(), "member.view_club", value):
s = format_html("<a href={url}>{name}</a>", s = format_html("<a href={url}>{name}</a>",
url=reverse_lazy('member:club_detail', kwargs={"pk": value.pk}), name=s) url=reverse_lazy('member:club_detail', kwargs={"pk": value.pk}), name=s)
@ -127,7 +127,7 @@ class MembershipTable(tables.Table):
date_end=date.today(), date_end=date.today(),
fee=0, fee=0,
) )
if PermissionBackend.check_perm(get_current_authenticated_user(), if PermissionBackend.check_perm(get_current_request(),
"member.add_membership", empty_membership): # If the user has right "member.add_membership", empty_membership): # If the user has right
renew_url = reverse_lazy('member:club_renew_membership', renew_url = reverse_lazy('member:club_renew_membership',
kwargs={"pk": record.pk}) kwargs={"pk": record.pk})
@ -142,7 +142,7 @@ class MembershipTable(tables.Table):
# If the user has the right to manage the roles, display the link to manage them # If the user has the right to manage the roles, display the link to manage them
roles = record.roles.all() roles = record.roles.all()
s = ", ".join(str(role) for role in roles) s = ", ".join(str(role) for role in roles)
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.change_membership_roles", record): if PermissionBackend.check_perm(get_current_request(), "member.change_membership_roles", record):
s = format_html("<a href='" + str(reverse_lazy("member:club_manage_roles", kwargs={"pk": record.pk})) s = format_html("<a href='" + str(reverse_lazy("member:club_manage_roles", kwargs={"pk": record.pk}))
+ "'>" + s + "</a>") + "'>" + s + "</a>")
return s return s
@ -165,7 +165,7 @@ class ClubManagerTable(tables.Table):
def render_user(self, value): def render_user(self, value):
# If the user has the right, link the displayed user with the page of its detail. # If the user has the right, link the displayed user with the page of its detail.
s = value.username s = value.username
if PermissionBackend.check_perm(get_current_authenticated_user(), "auth.view_user", value): if PermissionBackend.check_perm(get_current_request(), "auth.view_user", value):
s = format_html("<a href={url}>{name}</a>", s = format_html("<a href={url}>{name}</a>",
url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s) url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s)

View File

@ -71,7 +71,7 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form.fields['email'].required = True form.fields['email'].required = True
form.fields['email'].help_text = _("This address must be valid.") form.fields['email'].help_text = _("This address must be valid.")
if PermissionBackend.check_perm(self.request.user, "member.change_profile", context['user_object'].profile): if PermissionBackend.check_perm(self.request, "member.change_profile", context['user_object'].profile):
context['profile_form'] = self.profile_form(instance=context['user_object'].profile, context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
data=self.request.POST if self.request.POST else None) data=self.request.POST if self.request.POST else None)
if not self.object.profile.report_frequency: if not self.object.profile.report_frequency:
@ -154,13 +154,13 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
history_list = \ history_list = \
Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note))\ Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note))\
.order_by("-created_at")\ .order_by("-created_at")\
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")) .filter(PermissionBackend.filter_queryset(self.request, Transaction, "view"))
history_table = HistoryTable(history_list, prefix='transaction-') history_table = HistoryTable(history_list, prefix='transaction-')
history_table.paginate(per_page=20, page=self.request.GET.get("transaction-page", 1)) history_table.paginate(per_page=20, page=self.request.GET.get("transaction-page", 1))
context['history_list'] = history_table context['history_list'] = history_table
club_list = Membership.objects.filter(user=user, date_end__gte=date.today() - timedelta(days=15))\ club_list = Membership.objects.filter(user=user, date_end__gte=date.today() - timedelta(days=15))\
.filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))\ .filter(PermissionBackend.filter_queryset(self.request, Membership, "view"))\
.order_by("club__name", "-date_start") .order_by("club__name", "-date_start")
# Display only the most recent membership # Display only the most recent membership
club_list = club_list.distinct("club__name")\ club_list = club_list.distinct("club__name")\
@ -177,21 +177,20 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
modified_note.is_active = True modified_note.is_active = True
modified_note.inactivity_reason = 'manual' modified_note.inactivity_reason = 'manual'
context["can_lock_note"] = user.note.is_active and PermissionBackend\ context["can_lock_note"] = user.note.is_active and PermissionBackend\
.check_perm(self.request.user, "note.change_noteuser_is_active", .check_perm(self.request, "note.change_noteuser_is_active", modified_note)
modified_note)
old_note = NoteUser.objects.select_for_update().get(pk=user.note.pk) old_note = NoteUser.objects.select_for_update().get(pk=user.note.pk)
modified_note.inactivity_reason = 'forced' modified_note.inactivity_reason = 'forced'
modified_note._force_save = True modified_note._force_save = True
modified_note.save() modified_note.save()
context["can_force_lock"] = user.note.is_active and PermissionBackend\ context["can_force_lock"] = user.note.is_active and PermissionBackend\
.check_perm(self.request.user, "note.change_note_is_active", modified_note) .check_perm(self.request, "note.change_note_is_active", modified_note)
old_note._force_save = True old_note._force_save = True
old_note._no_signal = True old_note._no_signal = True
old_note.save() old_note.save()
modified_note.refresh_from_db() modified_note.refresh_from_db()
modified_note.is_active = True modified_note.is_active = True
context["can_unlock_note"] = not user.note.is_active and PermissionBackend\ context["can_unlock_note"] = not user.note.is_active and PermissionBackend\
.check_perm(self.request.user, "note.change_note_is_active", modified_note) .check_perm(self.request, "note.change_note_is_active", modified_note)
return context return context
@ -238,7 +237,7 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
pre_registered_users = User.objects.filter(PermissionBackend.filter_queryset(self.request.user, User, "view"))\ pre_registered_users = User.objects.filter(PermissionBackend.filter_queryset(self.request, User, "view"))\
.filter(profile__registration_valid=False) .filter(profile__registration_valid=False)
context["can_manage_registrations"] = pre_registered_users.exists() context["can_manage_registrations"] = pre_registered_users.exists()
return context return context
@ -257,8 +256,8 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
note = context['object'].note note = context['object'].note
context["aliases"] = AliasTable( context["aliases"] = AliasTable(
note.alias.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all()) note.alias.filter(PermissionBackend.filter_queryset(self.request, Alias, "view")).distinct().all())
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias( context["can_create"] = PermissionBackend.check_perm(self.request, "note.add_alias", Alias(
note=context["object"].note, note=context["object"].note,
name="", name="",
normalized_name="", normalized_name="",
@ -383,7 +382,7 @@ class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["can_add_club"] = PermissionBackend.check_perm(self.request.user, "member.add_club", Club( context["can_add_club"] = PermissionBackend.check_perm(self.request, "member.add_club", Club(
name="", name="",
email="club@example.com", email="club@example.com",
)) ))
@ -405,7 +404,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
club = context["club"] club = context["club"]
if PermissionBackend.check_perm(self.request.user, "member.change_club_membership_start", club): if PermissionBackend.check_perm(self.request, "member.change_club_membership_start", club):
club.update_membership_dates() club.update_membership_dates()
# managers list # managers list
managers = Membership.objects.filter(club=self.object, roles__name="Bureau de club", managers = Membership.objects.filter(club=self.object, roles__name="Bureau de club",
@ -414,7 +413,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context["managers"] = ClubManagerTable(data=managers, prefix="managers-") context["managers"] = ClubManagerTable(data=managers, prefix="managers-")
# transaction history # transaction history
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\ club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\ .filter(PermissionBackend.filter_queryset(self.request, Transaction, "view"))\
.order_by('-created_at') .order_by('-created_at')
history_table = HistoryTable(club_transactions, prefix="history-") history_table = HistoryTable(club_transactions, prefix="history-")
history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1)) history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
@ -423,7 +422,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
club_member = Membership.objects.filter( club_member = Membership.objects.filter(
club=club, club=club,
date_end__gte=date.today() - timedelta(days=15), date_end__gte=date.today() - timedelta(days=15),
).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))\ ).filter(PermissionBackend.filter_queryset(self.request, Membership, "view"))\
.order_by("user__username", "-date_start") .order_by("user__username", "-date_start")
# Display only the most recent membership # Display only the most recent membership
club_member = club_member.distinct("user__username")\ club_member = club_member.distinct("user__username")\
@ -460,8 +459,8 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
note = context['object'].note note = context['object'].note
context["aliases"] = AliasTable(note.alias.filter( context["aliases"] = AliasTable(note.alias.filter(
PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all()) PermissionBackend.filter_queryset(self.request, Alias, "view")).distinct().all())
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias( context["can_create"] = PermissionBackend.check_perm(self.request, "note.add_alias", Alias(
note=context["object"].note, note=context["object"].note,
name="", name="",
normalized_name="", normalized_name="",
@ -536,7 +535,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
form = context['form'] form = context['form']
if "club_pk" in self.kwargs: # We create a new membership. if "club_pk" in self.kwargs: # We create a new membership.
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ club = Club.objects.filter(PermissionBackend.filter_queryset(self.request, Club, "view"))\
.get(pk=self.kwargs["club_pk"], weiclub=None) .get(pk=self.kwargs["club_pk"], weiclub=None)
form.fields['credit_amount'].initial = club.membership_fee_paid form.fields['credit_amount'].initial = club.membership_fee_paid
# Ensure that the user is member of the parent club and all its the family tree. # Ensure that the user is member of the parent club and all its the family tree.
@ -684,7 +683,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
""" """
# Get the club that is concerned by the membership # Get the club that is concerned by the membership
if "club_pk" in self.kwargs: # get from url of new membership if "club_pk" in self.kwargs: # get from url of new membership
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) \ club = Club.objects.filter(PermissionBackend.filter_queryset(self.request, Club, "view")) \
.get(pk=self.kwargs["club_pk"]) .get(pk=self.kwargs["club_pk"])
user = form.instance.user user = form.instance.user
old_membership = None old_membership = None
@ -868,7 +867,7 @@ class ClubMembersListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableV
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
club = Club.objects.filter( club = Club.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Club, "view") PermissionBackend.filter_queryset(self.request, Club, "view")
).get(pk=self.kwargs["pk"]) ).get(pk=self.kwargs["pk"])
context["club"] = club context["club"] = club

View File

@ -8,7 +8,7 @@ from rest_framework.exceptions import ValidationError
from rest_polymorphic.serializers import PolymorphicSerializer from rest_polymorphic.serializers import PolymorphicSerializer
from member.api.serializers import MembershipSerializer from member.api.serializers import MembershipSerializer
from member.models import Membership from member.models import Membership
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from rest_framework.utils import model_meta from rest_framework.utils import model_meta
@ -126,7 +126,7 @@ class ConsumerSerializer(serializers.ModelSerializer):
""" """
# If the user has no right to see the note, then we only display the note identifier # If the user has no right to see the note, then we only display the note identifier
return NotePolymorphicSerializer().to_representation(obj.note)\ return NotePolymorphicSerializer().to_representation(obj.note)\
if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", obj.note)\ if PermissionBackend.check_perm(get_current_request(), "note.view_note", obj.note)\
else dict( else dict(
id=obj.note.id, id=obj.note.id,
name=str(obj.note), name=str(obj.note),
@ -142,7 +142,7 @@ class ConsumerSerializer(serializers.ModelSerializer):
def get_membership(self, obj): def get_membership(self, obj):
if isinstance(obj.note, NoteUser): if isinstance(obj.note, NoteUser):
memberships = Membership.objects.filter( memberships = Membership.objects.filter(
PermissionBackend.filter_queryset(get_current_authenticated_user(), Membership, "view")).filter( PermissionBackend.filter_queryset(get_current_request(), Membership, "view")).filter(
user=obj.note.user, user=obj.note.user,
club=2, # Kfet club=2, # Kfet
).order_by("-date_start") ).order_by("-date_start")

View File

@ -10,7 +10,6 @@ from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
from note_kfet.middlewares import get_current_session
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer,\ from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer,\
@ -40,12 +39,12 @@ class NotePolymorphicViewSet(ReadProtectedModelViewSet):
Parse query and apply filters. Parse query and apply filters.
:return: The filtered set of requested notes :return: The filtered set of requested notes
""" """
user = self.request.user self.request.session.setdefault("permission_mask", 42)
get_current_session().setdefault("permission_mask", 42) queryset = self.queryset.filter(PermissionBackend.filter_queryset(self.request, Note, "view")
queryset = self.queryset.filter(PermissionBackend.filter_queryset(user, Note, "view") | PermissionBackend.filter_queryset(self.request, NoteUser, "view")
| PermissionBackend.filter_queryset(user, NoteUser, "view") | PermissionBackend.filter_queryset(self.request, NoteClub, "view")
| PermissionBackend.filter_queryset(user, NoteClub, "view") | PermissionBackend.filter_queryset(self.request, NoteSpecial, "view"))\
| PermissionBackend.filter_queryset(user, NoteSpecial, "view")).distinct() .distinct()
alias = self.request.query_params.get("alias", ".*") alias = self.request.query_params.get("alias", ".*")
queryset = queryset.filter( queryset = queryset.filter(
@ -205,7 +204,6 @@ class TransactionViewSet(ReadProtectedModelViewSet):
ordering_fields = ['created_at', 'amount', ] ordering_fields = ['created_at', 'amount', ]
def get_queryset(self): def get_queryset(self):
user = self.request.user self.request.session.setdefault("permission_mask", 42)
get_current_session().setdefault("permission_mask", 42) return self.model.objects.filter(PermissionBackend.filter_queryset(self.request, self.model, "view"))\
return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view"))\
.order_by("created_at", "id") .order_by("created_at", "id")

View File

@ -7,7 +7,7 @@ import django_tables2 as tables
from django.utils.html import format_html from django.utils.html import format_html
from django_tables2.utils import A from django_tables2.utils import A
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from .models.notes import Alias from .models.notes import Alias
@ -88,16 +88,16 @@ class HistoryTable(tables.Table):
"class": lambda record: "class": lambda record:
str(record.valid).lower() str(record.valid).lower()
+ (' validate' if record.source.is_active and record.destination.is_active and PermissionBackend + (' validate' if record.source.is_active and record.destination.is_active and PermissionBackend
.check_perm(get_current_authenticated_user(), "note.change_transaction_invalidity_reason", record) .check_perm(get_current_request(), "note.change_transaction_invalidity_reason", record)
else ''), else ''),
"data-toggle": "tooltip", "data-toggle": "tooltip",
"title": lambda record: (_("Click to invalidate") if record.valid else _("Click to validate")) "title": lambda record: (_("Click to invalidate") if record.valid else _("Click to validate"))
if PermissionBackend.check_perm(get_current_authenticated_user(), if PermissionBackend.check_perm(get_current_request(),
"note.change_transaction_invalidity_reason", record) "note.change_transaction_invalidity_reason", record)
and record.source.is_active and record.destination.is_active else None, and record.source.is_active and record.destination.is_active else None,
"onclick": lambda record: 'de_validate(' + str(record.id) + ', ' + str(record.valid).lower() "onclick": lambda record: 'de_validate(' + str(record.id) + ', ' + str(record.valid).lower()
+ ', "' + str(record.__class__.__name__) + '")' + ', "' + str(record.__class__.__name__) + '")'
if PermissionBackend.check_perm(get_current_authenticated_user(), if PermissionBackend.check_perm(get_current_request(),
"note.change_transaction_invalidity_reason", record) "note.change_transaction_invalidity_reason", record)
and record.source.is_active and record.destination.is_active else None, and record.source.is_active and record.destination.is_active else None,
"onmouseover": lambda record: '$("#invalidity_reason_' "onmouseover": lambda record: '$("#invalidity_reason_'
@ -126,7 +126,7 @@ class HistoryTable(tables.Table):
When the validation status is hovered, an input field is displayed to let the user specify an invalidity reason When the validation status is hovered, an input field is displayed to let the user specify an invalidity reason
""" """
has_perm = PermissionBackend \ has_perm = PermissionBackend \
.check_perm(get_current_authenticated_user(), "note.change_transaction_invalidity_reason", record) .check_perm(get_current_request(), "note.change_transaction_invalidity_reason", record)
val = "" if value else "" val = "" if value else ""
@ -165,7 +165,7 @@ class AliasTable(tables.Table):
extra_context={"delete_trans": _('delete')}, extra_context={"delete_trans": _('delete')},
attrs={'td': {'class': lambda record: 'col-sm-1' + ( attrs={'td': {'class': lambda record: 'col-sm-1' + (
' d-none' if not PermissionBackend.check_perm( ' d-none' if not PermissionBackend.check_perm(
get_current_authenticated_user(), "note.delete_alias", get_current_request(), "note.delete_alias",
record) else '')}}, verbose_name=_("Delete"), ) record) else '')}}, verbose_name=_("Delete"), )

View File

@ -38,7 +38,7 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl
def get_queryset(self, **kwargs): def get_queryset(self, **kwargs):
# retrieves only Transaction that user has the right to see. # retrieves only Transaction that user has the right to see.
return Transaction.objects.filter( return Transaction.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Transaction, "view") PermissionBackend.filter_queryset(self.request, Transaction, "view")
).order_by("-created_at").all()[:20] ).order_by("-created_at").all()[:20]
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -47,16 +47,16 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl
context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk
context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk
context['special_types'] = NoteSpecial.objects\ context['special_types'] = NoteSpecial.objects\
.filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\ .filter(PermissionBackend.filter_queryset(self.request, NoteSpecial, "view"))\
.order_by("special_type").all() .order_by("special_type").all()
# Add a shortcut for entry page for open activities # Add a shortcut for entry page for open activities
if "activity" in settings.INSTALLED_APPS: if "activity" in settings.INSTALLED_APPS:
from activity.models import Activity from activity.models import Activity
activities_open = Activity.objects.filter(open=True).filter( activities_open = Activity.objects.filter(open=True).filter(
PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all() PermissionBackend.filter_queryset(self.request, Activity, "view")).distinct().all()
context["activities_open"] = [a for a in activities_open context["activities_open"] = [a for a in activities_open
if PermissionBackend.check_perm(self.request.user, if PermissionBackend.check_perm(self.request,
"activity.add_entry", "activity.add_entry",
Entry(activity=a, Entry(activity=a,
note=self.request.user.note, ))] note=self.request.user.note, ))]
@ -159,7 +159,7 @@ class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
return self.handle_no_permission() return self.handle_no_permission()
templates = TransactionTemplate.objects.filter( templates = TransactionTemplate.objects.filter(
PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view") PermissionBackend().filter_queryset(self.request, TransactionTemplate, "view")
) )
if not templates.exists(): if not templates.exists():
raise PermissionDenied(_("You can't see any button.")) raise PermissionDenied(_("You can't see any button."))
@ -170,7 +170,7 @@ class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
restrict to the transaction history the user can see. restrict to the transaction history the user can see.
""" """
return Transaction.objects.filter( return Transaction.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Transaction, "view") PermissionBackend.filter_queryset(self.request, Transaction, "view")
).order_by("-created_at").all()[:20] ).order_by("-created_at").all()[:20]
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -180,13 +180,13 @@ class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
# for each category, find which transaction templates the user can see. # for each category, find which transaction templates the user can see.
for category in categories: for category in categories:
category.templates_filtered = category.templates.filter( category.templates_filtered = category.templates.filter(
PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view") PermissionBackend().filter_queryset(self.request, TransactionTemplate, "view")
).filter(display=True).order_by('name').all() ).filter(display=True).order_by('name').all()
context['categories'] = [cat for cat in categories if cat.templates_filtered] context['categories'] = [cat for cat in categories if cat.templates_filtered]
# some transactiontemplate are put forward to find them easily # some transactiontemplate are put forward to find them easily
context['highlighted'] = TransactionTemplate.objects.filter(highlighted=True).filter( context['highlighted'] = TransactionTemplate.objects.filter(highlighted=True).filter(
PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view") PermissionBackend().filter_queryset(self.request, TransactionTemplate, "view")
).order_by('name').all() ).order_by('name').all()
context['polymorphic_ctype'] = ContentType.objects.get_for_model(RecurrentTransaction).pk context['polymorphic_ctype'] = ContentType.objects.get_for_model(RecurrentTransaction).pk
@ -209,7 +209,7 @@ class TransactionSearchView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView
data = form.cleaned_data if form.is_valid() else {} data = form.cleaned_data if form.is_valid() else {}
transactions = Transaction.objects.annotate(total_amount=F("quantity") * F("amount")).filter( transactions = Transaction.objects.annotate(total_amount=F("quantity") * F("amount")).filter(
PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\ PermissionBackend.filter_queryset(self.request, Transaction, "view"))\
.filter(Q(source=self.object) | Q(destination=self.object)).order_by('-created_at') .filter(Q(source=self.object) | Q(destination=self.object)).order_by('-created_at')
if "source" in data and data["source"]: if "source" in data and data["source"]:

View File

@ -4,12 +4,12 @@
from datetime import date from datetime import date
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, F from django.db.models import Q, F
from django.utils import timezone from django.utils import timezone
from note.models import Note, NoteUser, NoteClub, NoteSpecial from note.models import Note, NoteUser, NoteClub, NoteSpecial
from note_kfet.middlewares import get_current_session from note_kfet.middlewares import get_current_request
from member.models import Membership, Club from member.models import Membership, Club
from .decorators import memoize from .decorators import memoize
@ -33,7 +33,7 @@ class PermissionBackend(ModelBackend):
:param t: The type of the permissions: view, change, add or delete :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 :return: The queryset of the permissions of the user (memoized) grouped by clubs
""" """
if isinstance(user, AnonymousUser): if not user.is_authenticated:
# Unauthenticated users have no permissions # Unauthenticated users have no permissions
return Permission.objects.none() return Permission.objects.none()
@ -43,7 +43,8 @@ class PermissionBackend(ModelBackend):
for membership in memberships: for membership in memberships:
for role in membership.roles.all(): for role in membership.roles.all():
for perm in role.permissions.filter(type=t, mask__rank__lte=get_current_session().get("permission_mask", -1)).all(): for perm in role.permissions.filter(
type=t, mask__rank__lte=get_current_request().session.get("permission_mask", -1)).all():
if not perm.permanent: if not perm.permanent:
if membership.date_start > date.today() or membership.date_end < date.today(): if membership.date_start > date.today() or membership.date_end < date.today():
continue continue
@ -88,20 +89,22 @@ class PermissionBackend(ModelBackend):
@staticmethod @staticmethod
@memoize @memoize
def filter_queryset(user, model, t, field=None): def filter_queryset(request, model, t, field=None):
""" """
Filter a queryset by considering the permissions of a given user. Filter a queryset by considering the permissions of a given user.
:param user: The owner of the permissions that are fetched :param request: The current request
:param model: The concerned model of the queryset :param model: The concerned model of the queryset
:param t: The type of modification (view, add, change, delete) :param t: The type of modification (view, add, change, delete)
:param field: The field of the model to test, if concerned :param field: The field of the model to test, if concerned
:return: A query that corresponds to the filter to give to a queryset :return: A query that corresponds to the filter to give to a queryset
""" """
if user is None or isinstance(user, AnonymousUser): user = request.user
if user is None or not user.is_authenticated:
# Anonymous users can't do anything # Anonymous users can't do anything
return Q(pk=-1) return Q(pk=-1)
if user.is_superuser and get_current_session().get("permission_mask", -1) >= 42: if user.is_superuser and get_current_request().session.get("permission_mask", -1) >= 42:
# Superusers have all rights # Superusers have all rights
return Q() return Q()
@ -122,7 +125,7 @@ class PermissionBackend(ModelBackend):
@staticmethod @staticmethod
@memoize @memoize
def check_perm(user_obj, perm, obj=None): def check_perm(request, perm, obj=None):
""" """
Check is the given user has the permission over a given object. Check is the given user has the permission over a given object.
The result is then memoized. The result is then memoized.
@ -130,10 +133,12 @@ class PermissionBackend(ModelBackend):
primary key, the result is not memoized. Moreover, the right could change primary key, the result is not memoized. Moreover, the right could change
(e.g. for a transaction, the balance of the user could change) (e.g. for a transaction, the balance of the user could change)
""" """
if user_obj is None or isinstance(user_obj, AnonymousUser): user_obj = request.user
if user_obj is None or not user_obj.is_authenticated:
return False return False
sess = get_current_session() sess = request.session
if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42: if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42:
return True return True
@ -152,7 +157,10 @@ class PermissionBackend(ModelBackend):
return False return False
def has_perm(self, user_obj, perm, obj=None): def has_perm(self, user_obj, perm, obj=None):
return PermissionBackend.check_perm(user_obj, perm, obj) # 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): def has_module_perms(self, user_obj, app_label):
return False return False

View File

@ -5,7 +5,7 @@ from functools import lru_cache
from time import time from time import time
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from note_kfet.middlewares import get_current_session from note_kfet.middlewares import get_current_request
def memoize(f): def memoize(f):
@ -48,11 +48,11 @@ def memoize(f):
last_collect = time() last_collect = time()
# If there is no session, then we don't memoize anything. # If there is no session, then we don't memoize anything.
sess = get_current_session() request = get_current_request()
if sess is None or sess.session_key is None: if request is None or request.session is None or request.session.session_key is None:
return f(*args, **kwargs) return f(*args, **kwargs)
sess_key = sess.session_key sess_key = request.session.session_key
if sess_key not in sess_funs: if sess_key not in sess_funs:
# lru_cache makes the job of memoization # lru_cache makes the job of memoization
# We store only the 512 latest data per session. It has to be enough. # We store only the 512 latest data per session. It has to be enough.

View File

@ -45,7 +45,7 @@ class StrongDjangoObjectPermissions(DjangoObjectPermissions):
perms = self.get_required_object_permissions(request.method, model_cls) perms = self.get_required_object_permissions(request.method, model_cls)
# if not user.has_perms(perms, obj): # if not user.has_perms(perms, obj):
if not all(PermissionBackend.check_perm(user, perm, obj) for perm in perms): if not all(PermissionBackend.check_perm(request, perm, obj) for perm in perms):
# If the user does not have permissions we need to determine if # If the user does not have permissions we need to determine if
# they have read permissions to see 403, or not, and simply see # they have read permissions to see 403, or not, and simply see
# a 404 response. # a 404 response.

View File

@ -3,7 +3,7 @@
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
@ -31,8 +31,8 @@ def pre_save_object(sender, instance, **kwargs):
if hasattr(instance, "_force_save") or hasattr(instance, "_no_signal"): if hasattr(instance, "_force_save") or hasattr(instance, "_no_signal"):
return return
user = get_current_authenticated_user() request = get_current_request()
if user is None: if request is None:
# Action performed on shell is always granted # Action performed on shell is always granted
return return
@ -45,7 +45,7 @@ def pre_save_object(sender, instance, **kwargs):
# We check if the user can change the model # We check if the user can change the model
# If the user has all right on a model, then OK # If the user has all right on a model, then OK
if PermissionBackend.check_perm(user, app_label + ".change_" + model_name, instance): if PermissionBackend.check_perm(request, app_label + ".change_" + model_name, instance):
return return
# In the other case, we check if he/she has the right to change one field # In the other case, we check if he/she has the right to change one field
@ -58,7 +58,8 @@ def pre_save_object(sender, instance, **kwargs):
# If the field wasn't modified, no need to check the permissions # If the field wasn't modified, no need to check the permissions
if old_value == new_value: if old_value == new_value:
continue continue
if not PermissionBackend.check_perm(user, app_label + ".change_" + model_name + "_" + field_name, instance): if not PermissionBackend.check_perm(request, 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" _("You don't have the permission to change the field {field} on this instance of model"
" {app_label}.{model_name}.") " {app_label}.{model_name}.")
@ -66,7 +67,7 @@ def pre_save_object(sender, instance, **kwargs):
) )
else: else:
# We check if the user has right to add the object # We check if the user has right to add the object
has_perm = PermissionBackend.check_perm(user, app_label + ".add_" + model_name, instance) has_perm = PermissionBackend.check_perm(request, app_label + ".add_" + model_name, instance)
if not has_perm: if not has_perm:
raise PermissionDenied( raise PermissionDenied(
@ -87,8 +88,8 @@ def pre_delete_object(instance, **kwargs):
# Don't check permissions on force-deleted objects # Don't check permissions on force-deleted objects
return return
user = get_current_authenticated_user() request = get_current_request()
if user is None: if request is None:
# Action performed on shell is always granted # Action performed on shell is always granted
return return
@ -97,7 +98,7 @@ def pre_delete_object(instance, **kwargs):
model_name = model_name_full[1] model_name = model_name_full[1]
# We check if the user has rights to delete the object # We check if the user has rights to delete the object
if not PermissionBackend.check_perm(user, app_label + ".delete_" + model_name, instance): if not PermissionBackend.check_perm(request, 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}.") _("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)) .format(app_label=app_label, model_name=model_name))

View File

@ -8,7 +8,7 @@ from django.urls import reverse_lazy
from django.utils.html import format_html from django.utils.html import format_html
from django_tables2 import A from django_tables2 import A
from member.models import Membership from member.models import Membership
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
@ -20,7 +20,7 @@ class RightsTable(tables.Table):
def render_user(self, value): def render_user(self, value):
# If the user has the right, link the displayed user with the page of its detail. # If the user has the right, link the displayed user with the page of its detail.
s = value.username s = value.username
if PermissionBackend.check_perm(get_current_authenticated_user(), "auth.view_user", value): if PermissionBackend.check_perm(get_current_request(), "auth.view_user", value):
s = format_html("<a href={url}>{name}</a>", s = format_html("<a href={url}>{name}</a>",
url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s) url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s)
return s return s
@ -28,7 +28,7 @@ class RightsTable(tables.Table):
def render_club(self, value): def render_club(self, value):
# If the user has the right, link the displayed user with the page of its detail. # If the user has the right, link the displayed user with the page of its detail.
s = value.name s = value.name
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_club", value): if PermissionBackend.check_perm(get_current_request(), "member.view_club", value):
s = format_html("<a href={url}>{name}</a>", s = format_html("<a href={url}>{name}</a>",
url=reverse_lazy('member:club_detail', kwargs={"pk": value.pk}), name=s) url=reverse_lazy('member:club_detail', kwargs={"pk": value.pk}), name=s)
@ -42,7 +42,7 @@ class RightsTable(tables.Table):
| Q(name="Bureau de club")) | Q(name="Bureau de club"))
& Q(weirole__isnull=True))).all() & Q(weirole__isnull=True))).all()
s = ", ".join(str(role) for role in roles) s = ", ".join(str(role) for role in roles)
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.change_membership_roles", record): if PermissionBackend.check_perm(get_current_request(), "member.change_membership_roles", record):
s = format_html("<a href='" + str(reverse_lazy("member:club_manage_roles", kwargs={"pk": record.pk})) s = format_html("<a href='" + str(reverse_lazy("member:club_manage_roles", kwargs={"pk": record.pk}))
+ "'>" + s + "</a>") + "'>" + s + "</a>")
return s return s

View File

@ -1,12 +1,12 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay # Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib.auth.models import AnonymousUser
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.template.defaultfilters import stringfilter from django.template.defaultfilters import stringfilter
from django import template from django import template
from note_kfet.middlewares import get_current_authenticated_user, get_current_session from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend
from ..backends import PermissionBackend
@stringfilter @stringfilter
@ -14,9 +14,10 @@ def not_empty_model_list(model_name):
""" """
Return True if and only if the current user has right to see any object of the given model. Return True if and only if the current user has right to see any object of the given model.
""" """
user = get_current_authenticated_user() request = get_current_request()
session = get_current_session() user = request.user
if user is None or isinstance(user, AnonymousUser): session = request.session
if user is None or not user.is_authenticated:
return False return False
elif user.is_superuser and session.get("permission_mask", -1) >= 42: elif user.is_superuser and session.get("permission_mask", -1) >= 42:
return True return True
@ -29,11 +30,12 @@ def model_list(model_name, t="view", fetch=True):
""" """
Return the queryset of all visible instances of the given model. Return the queryset of all visible instances of the given model.
""" """
user = get_current_authenticated_user() request = get_current_request()
user = request.user
spl = model_name.split(".") spl = model_name.split(".")
ct = ContentType.objects.get(app_label=spl[0], model=spl[1]) ct = ContentType.objects.get(app_label=spl[0], model=spl[1])
qs = ct.model_class().objects.filter(PermissionBackend.filter_queryset(user, ct, t)) qs = ct.model_class().objects.filter(PermissionBackend.filter_queryset(request, ct, t))
if user is None or isinstance(user, AnonymousUser): if user is None or not user.is_authenticated:
return qs.none() return qs.none()
if fetch: if fetch:
qs = qs.all() qs = qs.all()
@ -49,7 +51,7 @@ def model_list_length(model_name, t="view"):
def has_perm(perm, obj): def has_perm(perm, obj):
return PermissionBackend.check_perm(get_current_authenticated_user(), perm, obj) return PermissionBackend.check_perm(get_current_request(), perm, obj)
register = template.Library() register = template.Library()

View File

@ -28,7 +28,7 @@ class ProtectQuerysetMixin:
""" """
def get_queryset(self, filter_permissions=True, **kwargs): def get_queryset(self, filter_permissions=True, **kwargs):
qs = super().get_queryset(**kwargs) qs = super().get_queryset(**kwargs)
return qs.filter(PermissionBackend.filter_queryset(self.request.user, qs.model, "view")).distinct()\ return qs.filter(PermissionBackend.filter_queryset(self.request, qs.model, "view")).distinct()\
if filter_permissions else qs if filter_permissions else qs
def get_object(self, queryset=None): def get_object(self, queryset=None):
@ -53,7 +53,7 @@ class ProtectQuerysetMixin:
# We could also delete the field, but some views might be affected. # We could also delete the field, but some views might be affected.
meta = form.instance._meta meta = form.instance._meta
for key in form.base_fields: for key in form.base_fields:
if not PermissionBackend.check_perm(self.request.user, if not PermissionBackend.check_perm(self.request,
f"{meta.app_label}.change_{meta.model_name}_" + key, self.object): f"{meta.app_label}.change_{meta.model_name}_" + key, self.object):
form.fields[key].widget = HiddenInput() form.fields[key].widget = HiddenInput()
@ -101,7 +101,7 @@ class ProtectedCreateView(LoginRequiredMixin, CreateView):
# noinspection PyProtectedMember # noinspection PyProtectedMember
app_label, model_name = model_class._meta.app_label, model_class._meta.model_name.lower() app_label, model_name = model_class._meta.app_label, model_class._meta.model_name.lower()
perm = app_label + ".add_" + model_name perm = app_label + ".add_" + model_name
if not PermissionBackend.check_perm(request.user, perm, self.get_sample_object()): 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 " 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)) "{app_label}.{model_name}.").format(app_label=app_label, model_name=model_name))
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)

View File

@ -66,9 +66,11 @@ class UserCreateView(CreateView):
profile_form.instance.user = user profile_form.instance.user = user
profile = profile_form.save(commit=False) profile = profile_form.save(commit=False)
user.profile = profile user.profile = profile
user._force_save = True
user.save() user.save()
user.refresh_from_db() user.refresh_from_db()
profile.user = user profile.user = user
profile._force_save = True
profile.save() profile.save()
user.profile.send_email_validation_link() user.profile.send_email_validation_link()
@ -110,7 +112,9 @@ class UserValidateView(TemplateView):
self.validlink = True self.validlink = True
user.is_active = user.profile.registration_valid or user.is_superuser user.is_active = user.profile.registration_valid or user.is_superuser
user.profile.email_confirmed = True user.profile.email_confirmed = True
user._force_save = True
user.save() user.save()
user.profile._force_save = True
user.profile.save() user.profile.save()
return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400) return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400)
@ -384,7 +388,7 @@ class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View):
Delete the pre-registered user which id is given in the URL. Delete the pre-registered user which id is given in the URL.
""" """
user = User.objects.filter(profile__registration_valid=False)\ user = User.objects.filter(profile__registration_valid=False)\
.filter(PermissionBackend.filter_queryset(request.user, User, "change", "is_valid"))\ .filter(PermissionBackend.filter_queryset(request, User, "change", "is_valid"))\
.get(pk=self.kwargs["pk"]) .get(pk=self.kwargs["pk"])
# Delete associated soge credits before # Delete associated soge credits before
SogeCredit.objects.filter(user=user).delete() SogeCredit.objects.filter(user=user).delete()

View File

@ -107,7 +107,7 @@ class InvoiceListView(LoginRequiredMixin, SingleTableView):
name="", name="",
address="", address="",
) )
if not PermissionBackend.check_perm(self.request.user, "treasury.add_invoice", sample_invoice): if not PermissionBackend.check_perm(self.request, "treasury.add_invoice", sample_invoice):
raise PermissionDenied(_("You are not able to see the treasury interface.")) raise PermissionDenied(_("You are not able to see the treasury interface."))
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@ -194,7 +194,7 @@ class InvoiceRenderView(LoginRequiredMixin, View):
def get(self, request, **kwargs): def get(self, request, **kwargs):
pk = kwargs["pk"] pk = kwargs["pk"]
invoice = Invoice.objects.filter(PermissionBackend.filter_queryset(request.user, Invoice, "view")).get(pk=pk) invoice = Invoice.objects.filter(PermissionBackend.filter_queryset(request, Invoice, "view")).get(pk=pk)
tex = invoice.tex tex = invoice.tex
try: try:
@ -259,7 +259,7 @@ class RemittanceCreateView(ProtectQuerysetMixin, ProtectedCreateView):
context["table"] = RemittanceTable( context["table"] = RemittanceTable(
data=Remittance.objects.filter( data=Remittance.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) PermissionBackend.filter_queryset(self.request, Remittance, "view")).all())
context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none())
return context return context
@ -281,7 +281,7 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
remittance_type_id=1, remittance_type_id=1,
comment="", comment="",
) )
if not PermissionBackend.check_perm(self.request.user, "treasury.add_remittance", sample_remittance): if not PermissionBackend.check_perm(self.request, "treasury.add_remittance", sample_remittance):
raise PermissionDenied(_("You are not able to see the treasury interface.")) raise PermissionDenied(_("You are not able to see the treasury interface."))
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@ -290,7 +290,7 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
opened_remittances = RemittanceTable( opened_remittances = RemittanceTable(
data=Remittance.objects.filter(closed=False).filter( data=Remittance.objects.filter(closed=False).filter(
PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), PermissionBackend.filter_queryset(self.request, Remittance, "view")).all(),
prefix="opened-remittances-", prefix="opened-remittances-",
) )
opened_remittances.paginate(page=self.request.GET.get("opened-remittances-page", 1), per_page=10) opened_remittances.paginate(page=self.request.GET.get("opened-remittances-page", 1), per_page=10)
@ -298,7 +298,7 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
closed_remittances = RemittanceTable( closed_remittances = RemittanceTable(
data=Remittance.objects.filter(closed=True).filter( data=Remittance.objects.filter(closed=True).filter(
PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), PermissionBackend.filter_queryset(self.request, Remittance, "view")).all(),
prefix="closed-remittances-", prefix="closed-remittances-",
) )
closed_remittances.paginate(page=self.request.GET.get("closed-remittances-page", 1), per_page=10) closed_remittances.paginate(page=self.request.GET.get("closed-remittances-page", 1), per_page=10)
@ -307,7 +307,7 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
no_remittance_tr = SpecialTransactionTable( no_remittance_tr = SpecialTransactionTable(
data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)), data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
specialtransactionproxy__remittance=None).filter( specialtransactionproxy__remittance=None).filter(
PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), PermissionBackend.filter_queryset(self.request, Remittance, "view")).all(),
exclude=('remittance_remove', ), exclude=('remittance_remove', ),
prefix="no-remittance-", prefix="no-remittance-",
) )
@ -317,7 +317,7 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
with_remittance_tr = SpecialTransactionTable( with_remittance_tr = SpecialTransactionTable(
data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)), data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
specialtransactionproxy__remittance__closed=False).filter( specialtransactionproxy__remittance__closed=False).filter(
PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), PermissionBackend.filter_queryset(self.request, Remittance, "view")).all(),
exclude=('remittance_add', ), exclude=('remittance_add', ),
prefix="with-remittance-", prefix="with-remittance-",
) )
@ -342,7 +342,7 @@ class RemittanceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView)
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).filter( data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).filter(
PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all() PermissionBackend.filter_queryset(self.request, Remittance, "view")).all()
context["special_transactions"] = SpecialTransactionTable( context["special_transactions"] = SpecialTransactionTable(
data=data, data=data,
exclude=('remittance_add', 'remittance_remove', ) if self.object.closed else ('remittance_add', )) exclude=('remittance_add', 'remittance_remove', ) if self.object.closed else ('remittance_add', ))

View File

@ -8,7 +8,7 @@ from django.urls import reverse_lazy
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_tables2 import A from django_tables2 import A
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from .models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership from .models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership
@ -85,7 +85,7 @@ class WEIRegistrationTable(tables.Table):
def render_validate(self, record): def render_validate(self, record):
hasperm = PermissionBackend.check_perm( hasperm = PermissionBackend.check_perm(
get_current_authenticated_user(), "wei.add_weimembership", WEIMembership( get_current_request(), "wei.add_weimembership", WEIMembership(
club=record.wei, club=record.wei,
user=record.user, user=record.user,
date_start=date.today(), date_start=date.today(),
@ -110,7 +110,7 @@ class WEIRegistrationTable(tables.Table):
f"title=\"{tooltip}\" href=\"{url}\">{text}</a>") f"title=\"{tooltip}\" href=\"{url}\">{text}</a>")
def render_delete(self, record): def render_delete(self, record):
hasperm = PermissionBackend.check_perm(get_current_authenticated_user(), "wei.delete_weimembership", record) hasperm = PermissionBackend.check_perm(get_current_request(), "wei.delete_weimembership", record)
return _("Delete") if hasperm else format_html("<span class='no-perm'></span>") return _("Delete") if hasperm else format_html("<span class='no-perm'></span>")
class Meta: class Meta:

View File

@ -57,7 +57,7 @@ class WEIListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["can_create_wei"] = PermissionBackend.check_perm(self.request.user, "wei.add_weiclub", WEIClub( context["can_create_wei"] = PermissionBackend.check_perm(self.request, "wei.add_weiclub", WEIClub(
name="", name="",
email="weiclub@example.com", email="weiclub@example.com",
year=0, year=0,
@ -112,7 +112,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
club = context["club"] club = context["club"]
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) \ club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) \
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")) \ .filter(PermissionBackend.filter_queryset(self.request, Transaction, "view")) \
.order_by('-created_at', '-id') .order_by('-created_at', '-id')
history_table = HistoryTable(club_transactions, prefix="history-") history_table = HistoryTable(club_transactions, prefix="history-")
history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1)) history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
@ -121,13 +121,13 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
club_member = WEIMembership.objects.filter( club_member = WEIMembership.objects.filter(
club=club, club=club,
date_end__gte=date.today(), date_end__gte=date.today(),
).filter(PermissionBackend.filter_queryset(self.request.user, WEIMembership, "view")) ).filter(PermissionBackend.filter_queryset(self.request, WEIMembership, "view"))
membership_table = WEIMembershipTable(data=club_member, prefix="membership-") membership_table = WEIMembershipTable(data=club_member, prefix="membership-")
membership_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1)) membership_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1))
context['member_list'] = membership_table context['member_list'] = membership_table
pre_registrations = WEIRegistration.objects.filter( pre_registrations = WEIRegistration.objects.filter(
PermissionBackend.filter_queryset(self.request.user, WEIRegistration, "view")).filter( PermissionBackend.filter_queryset(self.request, WEIRegistration, "view")).filter(
membership=None, membership=None,
wei=club wei=club
) )
@ -142,7 +142,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
my_registration = None my_registration = None
context["my_registration"] = my_registration context["my_registration"] = my_registration
buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request.user, Bus, "view")) \ buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) \
.filter(wei=self.object).annotate(count=Count("memberships")).order_by("name") .filter(wei=self.object).annotate(count=Count("memberships")).order_by("name")
bus_table = BusTable(data=buses, prefix="bus-") bus_table = BusTable(data=buses, prefix="bus-")
context['buses'] = bus_table context['buses'] = bus_table
@ -167,7 +167,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
emergency_contact_phone="No", emergency_contact_phone="No",
) )
context["can_add_first_year_member"] = PermissionBackend \ context["can_add_first_year_member"] = PermissionBackend \
.check_perm(self.request.user, "wei.add_weiregistration", empty_fy_registration) .check_perm(self.request, "wei.add_weiregistration", empty_fy_registration)
# Check if the user has the right to create a registration of a random old member. # Check if the user has the right to create a registration of a random old member.
empty_old_registration = WEIRegistration( empty_old_registration = WEIRegistration(
@ -180,13 +180,13 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
emergency_contact_phone="No", emergency_contact_phone="No",
) )
context["can_add_any_member"] = PermissionBackend \ context["can_add_any_member"] = PermissionBackend \
.check_perm(self.request.user, "wei.add_weiregistration", empty_old_registration) .check_perm(self.request, "wei.add_weiregistration", empty_old_registration)
empty_bus = Bus( empty_bus = Bus(
wei=club, wei=club,
name="", name="",
) )
context["can_add_bus"] = PermissionBackend.check_perm(self.request.user, "wei.add_bus", empty_bus) context["can_add_bus"] = PermissionBackend.check_perm(self.request, "wei.add_bus", empty_bus)
context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists() context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists()
@ -370,13 +370,13 @@ class BusManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context["club"] = self.object.wei context["club"] = self.object.wei
bus = self.object bus = self.object
teams = BusTeam.objects.filter(PermissionBackend.filter_queryset(self.request.user, BusTeam, "view")) \ teams = BusTeam.objects.filter(PermissionBackend.filter_queryset(self.request, BusTeam, "view")) \
.filter(bus=bus).annotate(count=Count("memberships")).order_by("name") .filter(bus=bus).annotate(count=Count("memberships")).order_by("name")
teams_table = BusTeamTable(data=teams, prefix="team-") teams_table = BusTeamTable(data=teams, prefix="team-")
context["teams"] = teams_table context["teams"] = teams_table
memberships = WEIMembership.objects.filter(PermissionBackend.filter_queryset( memberships = WEIMembership.objects.filter(PermissionBackend.filter_queryset(
self.request.user, WEIMembership, "view")).filter(bus=bus) self.request, WEIMembership, "view")).filter(bus=bus)
memberships_table = WEIMembershipTable(data=memberships, prefix="membership-") memberships_table = WEIMembershipTable(data=memberships, prefix="membership-")
memberships_table.paginate(per_page=20, page=self.request.GET.get("membership-page", 1)) memberships_table.paginate(per_page=20, page=self.request.GET.get("membership-page", 1))
context["memberships"] = memberships_table context["memberships"] = memberships_table
@ -469,7 +469,7 @@ class BusTeamManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context["club"] = self.object.bus.wei context["club"] = self.object.bus.wei
memberships = WEIMembership.objects.filter(PermissionBackend.filter_queryset( memberships = WEIMembership.objects.filter(PermissionBackend.filter_queryset(
self.request.user, WEIMembership, "view")).filter(team=self.object) self.request, WEIMembership, "view")).filter(team=self.object)
memberships_table = WEIMembershipTable(data=memberships, prefix="membership-") memberships_table = WEIMembershipTable(data=memberships, prefix="membership-")
memberships_table.paginate(per_page=20, page=self.request.GET.get("membership-page", 1)) memberships_table.paginate(per_page=20, page=self.request.GET.get("membership-page", 1))
context["memberships"] = memberships_table context["memberships"] = memberships_table
@ -659,7 +659,7 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
data=self.request.POST if self.request.POST else None) data=self.request.POST if self.request.POST else None)
for field_name, field in membership_form.fields.items(): for field_name, field in membership_form.fields.items():
if not PermissionBackend.check_perm( if not PermissionBackend.check_perm(
self.request.user, "wei.change_membership_" + field_name, self.object.membership): self.request, "wei.change_membership_" + field_name, self.object.membership):
field.widget = HiddenInput() field.widget = HiddenInput()
del membership_form.fields["credit_type"] del membership_form.fields["credit_type"]
del membership_form.fields["credit_amount"] del membership_form.fields["credit_amount"]
@ -668,7 +668,7 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
del membership_form.fields["bank"] del membership_form.fields["bank"]
context["membership_form"] = membership_form context["membership_form"] = membership_form
elif not self.object.first_year and PermissionBackend.check_perm( elif not self.object.first_year and PermissionBackend.check_perm(
self.request.user, "wei.change_weiregistration_information_json", self.object): self.request, "wei.change_weiregistration_information_json", self.object):
choose_bus_form = WEIChooseBusForm( choose_bus_form = WEIChooseBusForm(
self.request.POST if self.request.POST else dict( self.request.POST if self.request.POST else dict(
bus=Bus.objects.filter(pk__in=self.object.information["preferred_bus_pk"]).all(), bus=Bus.objects.filter(pk__in=self.object.information["preferred_bus_pk"]).all(),
@ -704,7 +704,7 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
membership_form.save() membership_form.save()
# If it is not validated and if this is an old member, then we update the choices # If it is not validated and if this is an old member, then we update the choices
elif not form.instance.first_year and PermissionBackend.check_perm( elif not form.instance.first_year and PermissionBackend.check_perm(
self.request.user, "wei.change_weiregistration_information_json", self.object): self.request, "wei.change_weiregistration_information_json", self.object):
choose_bus_form = WEIChooseBusForm(self.request.POST) choose_bus_form = WEIChooseBusForm(self.request.POST)
if not choose_bus_form.is_valid(): if not choose_bus_form.is_valid():
return self.form_invalid(form) return self.form_invalid(form)
@ -726,7 +726,7 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
survey = CurrentSurvey(self.object) survey = CurrentSurvey(self.object)
if not survey.is_complete(): if not survey.is_complete():
return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk}) return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
if PermissionBackend.check_perm(self.request.user, "wei.add_weimembership", WEIMembership( if PermissionBackend.check_perm(self.request, "wei.add_weimembership", WEIMembership(
club=self.object.wei, club=self.object.wei,
user=self.object.user, user=self.object.user,
date_start=date.today(), date_start=date.today(),
@ -753,7 +753,7 @@ class WEIDeleteRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Delete
if today > wei.membership_end: if today > wei.membership_end:
return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
if not PermissionBackend.check_perm(self.request.user, "wei.delete_weiregistration", object): if not PermissionBackend.check_perm(self.request, "wei.delete_weiregistration", object):
raise PermissionDenied(_("You don't have the right to delete this WEI registration.")) raise PermissionDenied(_("You don't have the right to delete this WEI registration."))
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@ -1049,7 +1049,7 @@ class MemberListRenderView(LoginRequiredMixin, View):
""" """
def get_queryset(self, **kwargs): def get_queryset(self, **kwargs):
qs = WEIMembership.objects.filter(PermissionBackend.filter_queryset(self.request.user, WEIMembership, "view")) qs = WEIMembership.objects.filter(PermissionBackend.filter_queryset(self.request, WEIMembership, "view"))
qs = qs.filter(club__pk=self.kwargs["wei_pk"]).order_by( qs = qs.filter(club__pk=self.kwargs["wei_pk"]).order_by(
Lower('bus__name'), Lower('bus__name'),
Lower('team__name'), Lower('team__name'),

View File

@ -6,7 +6,6 @@ from django.contrib.admin import AdminSite
from django.contrib.sites.admin import Site, SiteAdmin from django.contrib.sites.admin import Site, SiteAdmin
from member.views import CustomLoginView from member.views import CustomLoginView
from .middlewares import get_current_session
class StrongAdminSite(AdminSite): class StrongAdminSite(AdminSite):
@ -14,8 +13,7 @@ class StrongAdminSite(AdminSite):
""" """
Authorize only staff that have the correct permission mask Authorize only staff that have the correct permission mask
""" """
session = get_current_session() return request.user.is_active and request.user.is_staff and request.session.get("permission_mask", -1) >= 42
return request.user.is_active and request.user.is_staff and session.get("permission_mask", -1) >= 42
def login(self, request, extra_context=None): def login(self, request, extra_context=None):
return CustomLoginView.as_view()(request) return CustomLoginView.as_view()(request)

View File

@ -2,13 +2,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from threading import local from threading import local
from typing import Optional
from django.conf import settings from django.conf import settings
from django.contrib.auth import login from django.contrib.auth import login
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sessions.backends.db import SessionStore
from django.http import HttpRequest
REQUEST_ATTR_NAME = getattr(settings, 'LOCAL_REQUEST_ATTR_NAME', '_current_request') REQUEST_ATTR_NAME = getattr(settings, 'LOCAL_REQUEST_ATTR_NAME', '_current_request')
@ -19,43 +16,10 @@ def _set_current_request(request=None):
setattr(_thread_locals, REQUEST_ATTR_NAME, request) setattr(_thread_locals, REQUEST_ATTR_NAME, request)
def get_current_request() -> Optional[HttpRequest]: def get_current_request():
return getattr(_thread_locals, REQUEST_ATTR_NAME, None) return getattr(_thread_locals, REQUEST_ATTR_NAME, None)
def get_current_user() -> Optional[User]:
request = get_current_request()
if request is None:
return None
return request.user
def get_current_session() -> Optional[SessionStore]:
request = get_current_request()
if request is None:
return None
return request.session
def get_current_ip() -> Optional[str]:
request = get_current_request()
if request is None:
return None
elif 'HTTP_X_REAL_IP' in request.META:
return request.META.get('HTTP_X_REAL_IP')
elif 'HTTP_X_FORWARDED_FOR' in request.META:
return request.META.get('HTTP_X_FORWARDED_FOR').split(', ')[0]
return request.META.get('REMOTE_ADDR')
def get_current_authenticated_user():
current_user = get_current_user()
if not current_user or not current_user.is_authenticated:
return None
return current_user
class SessionMiddleware(object): class SessionMiddleware(object):
""" """
This middleware get the current user with his or her IP address on each request. This middleware get the current user with his or her IP address on each request.

View File

@ -19,11 +19,11 @@ class IndexView(LoginRequiredMixin, RedirectView):
user = self.request.user user = self.request.user
# The account note will have the consumption page as default page # The account note will have the consumption page as default page
if not PermissionBackend.check_perm(user, "auth.view_user", user): if not PermissionBackend.check_perm(self.request, "auth.view_user", user):
return reverse("note:consos") return reverse("note:consos")
# People that can see the alias BDE are Kfet members # People that can see the alias BDE are Kfet members
if PermissionBackend.check_perm(user, "alias.view_alias", Alias.objects.get(name="BDE")): if PermissionBackend.check_perm(self.request, "alias.view_alias", Alias.objects.get(name="BDE")):
return reverse("note:transfer") return reverse("note:transfer")
# Non-Kfet members will don't see the transfer page, but their profile page # Non-Kfet members will don't see the transfer page, but their profile page