mirror of https://gitlab.crans.org/bde/nk20
Implements permission masks
This commit is contained in:
parent
d083894e9b
commit
95315cdbe2
|
@ -1,55 +0,0 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
||||
from threading import local
|
||||
|
||||
|
||||
USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
|
||||
IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
|
||||
|
||||
_thread_locals = local()
|
||||
|
||||
|
||||
def _set_current_user_and_ip(user=None, ip=None):
|
||||
setattr(_thread_locals, USER_ATTR_NAME, user)
|
||||
setattr(_thread_locals, IP_ATTR_NAME, ip)
|
||||
|
||||
|
||||
def get_current_user():
|
||||
return getattr(_thread_locals, USER_ATTR_NAME, None)
|
||||
|
||||
|
||||
def get_current_ip():
|
||||
return getattr(_thread_locals, IP_ATTR_NAME, None)
|
||||
|
||||
|
||||
def get_current_authenticated_user():
|
||||
current_user = get_current_user()
|
||||
if isinstance(current_user, AnonymousUser):
|
||||
return None
|
||||
return current_user
|
||||
|
||||
|
||||
class LogsMiddleware(object):
|
||||
"""
|
||||
This middleware get the current user with his or her IP address on each request.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
user = request.user
|
||||
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||
ip = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
|
||||
_set_current_user_and_ip(user, ip)
|
||||
response = self.get_response(request)
|
||||
_set_current_user_and_ip(None, None)
|
||||
|
||||
return response
|
|
@ -9,7 +9,7 @@ import getpass
|
|||
|
||||
from note.models import NoteUser, Alias
|
||||
|
||||
from .middlewares import get_current_authenticated_user, get_current_ip
|
||||
from note_kfet.middlewares import get_current_authenticated_user, get_current_ip
|
||||
from .models import Changelog
|
||||
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models import Q, F
|
||||
|
||||
from note.models import Note, NoteUser, NoteClub, NoteSpecial
|
||||
from note_kfet.middlewares import get_current_session
|
||||
from .models import Membership, RolePermissions, Club
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
|
||||
|
@ -37,6 +37,7 @@ class PermissionBackend(ModelBackend):
|
|||
F=F,
|
||||
Q=Q
|
||||
)
|
||||
if permission.mask.rank <= get_current_session().get("permission_mask", 0):
|
||||
yield permission
|
||||
|
||||
@staticmethod
|
||||
|
@ -50,7 +51,7 @@ class PermissionBackend(ModelBackend):
|
|||
:return: A query that corresponds to the filter to give to a queryset
|
||||
"""
|
||||
|
||||
if user.is_superuser:
|
||||
if user.is_superuser and get_current_session().get("permission_mask", 0) >= 42:
|
||||
# Superusers have all rights
|
||||
return Q()
|
||||
|
||||
|
@ -68,7 +69,7 @@ class PermissionBackend(ModelBackend):
|
|||
return query
|
||||
|
||||
def has_perm(self, user_obj, perm, obj=None):
|
||||
if user_obj.is_superuser:
|
||||
if user_obj.is_superuser and get_current_session().get("permission_mask", 0) >= 42:
|
||||
return True
|
||||
|
||||
if obj is None:
|
||||
|
|
|
@ -6,12 +6,21 @@ from crispy_forms.helper import FormHelper
|
|||
from crispy_forms.layout import Layout
|
||||
from dal import autocomplete
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from permission.models import PermissionMask
|
||||
from .models import Profile, Club, Membership
|
||||
|
||||
|
||||
class CustomAuthenticationForm(AuthenticationForm):
|
||||
permission_mask = forms.ModelChoiceField(
|
||||
label="Masque de permissions",
|
||||
queryset=PermissionMask.objects.order_by("rank"),
|
||||
empty_label=None,
|
||||
)
|
||||
|
||||
|
||||
class SignUpForm(UserCreationForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
|
@ -9,6 +9,7 @@ from django.conf import settings
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect
|
||||
|
@ -26,11 +27,20 @@ from note.tables import HistoryTable, AliasTable
|
|||
from .backends import PermissionBackend
|
||||
|
||||
from .filters import UserFilter, UserFilterFormHelper
|
||||
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
|
||||
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper, \
|
||||
CustomAuthenticationForm
|
||||
from .models import Club, Membership
|
||||
from .tables import ClubTable, UserTable
|
||||
|
||||
|
||||
class CustomLoginView(LoginView):
|
||||
form_class = CustomAuthenticationForm
|
||||
|
||||
def form_valid(self, form):
|
||||
self.request.session['permission_mask'] = form.cleaned_data['permission_mask'].rank
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class UserCreateView(CreateView):
|
||||
"""
|
||||
Une vue pour inscrire un utilisateur et lui créer un profile
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from rest_framework import serializers
|
||||
from rest_polymorphic.serializers import PolymorphicSerializer
|
||||
|
||||
from logs.middlewares import get_current_authenticated_user
|
||||
from note_kfet.middlewares import get_current_authenticated_user
|
||||
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
||||
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
|
||||
TemplateTransaction, SpecialTransaction
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"model": "note.note",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"polymorphic_ctype": 40,
|
||||
"polymorphic_ctype": 41,
|
||||
"balance": 0,
|
||||
"is_active": true,
|
||||
"display_image": "",
|
||||
|
@ -14,7 +14,7 @@
|
|||
"model": "note.note",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"polymorphic_ctype": 40,
|
||||
"polymorphic_ctype": 41,
|
||||
"balance": 0,
|
||||
"is_active": true,
|
||||
"display_image": "",
|
||||
|
@ -25,7 +25,7 @@
|
|||
"model": "note.note",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"polymorphic_ctype": 40,
|
||||
"polymorphic_ctype": 41,
|
||||
"balance": 0,
|
||||
"is_active": true,
|
||||
"display_image": "",
|
||||
|
@ -36,7 +36,7 @@
|
|||
"model": "note.note",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"polymorphic_ctype": 40,
|
||||
"polymorphic_ctype": 41,
|
||||
"balance": 0,
|
||||
"is_active": true,
|
||||
"display_image": "",
|
||||
|
@ -47,7 +47,7 @@
|
|||
"model": "note.note",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"polymorphic_ctype": 39,
|
||||
"polymorphic_ctype": 40,
|
||||
"balance": 0,
|
||||
"is_active": true,
|
||||
"display_image": "",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"model": "note.note",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"polymorphic_ctype": 39,
|
||||
"polymorphic_ctype": 40,
|
||||
"balance": 0,
|
||||
"is_active": true,
|
||||
"display_image": "",
|
||||
|
|
|
@ -3,7 +3,15 @@
|
|||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Permission
|
||||
from .models import Permission, PermissionMask
|
||||
|
||||
|
||||
@admin.register(PermissionMask)
|
||||
class PermissionMaskAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Admin customisation for Permission
|
||||
"""
|
||||
list_display = ('rank', 'description')
|
||||
|
||||
|
||||
@admin.register(Permission)
|
||||
|
|
|
@ -50,6 +50,20 @@ class InstancedPermission:
|
|||
return self.__repr__()
|
||||
|
||||
|
||||
class PermissionMask(models.Model):
|
||||
rank = models.PositiveSmallIntegerField(
|
||||
verbose_name=_('rank'),
|
||||
)
|
||||
|
||||
description = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('description'),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.description
|
||||
|
||||
|
||||
class Permission(models.Model):
|
||||
|
||||
PERMISSION_TYPES = [
|
||||
|
@ -85,6 +99,11 @@ class Permission(models.Model):
|
|||
|
||||
type = models.CharField(max_length=15, choices=PERMISSION_TYPES)
|
||||
|
||||
mask = models.ForeignKey(
|
||||
PermissionMask,
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
field = models.CharField(max_length=255, blank=True)
|
||||
|
||||
description = models.CharField(max_length=255, blank=True)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from logs.middlewares import get_current_authenticated_user
|
||||
from note_kfet.middlewares import get_current_authenticated_user
|
||||
|
||||
|
||||
EXCLUDED = [
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.template.defaultfilters import stringfilter
|
||||
|
||||
from logs.middlewares import get_current_authenticated_user
|
||||
from note_kfet.middlewares import get_current_authenticated_user, get_current_session
|
||||
from django import template
|
||||
|
||||
from member.backends import PermissionBackend
|
||||
|
@ -19,7 +19,7 @@ def not_empty_model_list(model_name):
|
|||
user = get_current_authenticated_user()
|
||||
if user is None:
|
||||
return False
|
||||
elif user.is_superuser:
|
||||
elif user.is_superuser and get_current_session().get("permission_mask", 0) >= 42:
|
||||
return True
|
||||
spl = model_name.split(".")
|
||||
ct = ContentType.objects.get(app_label=spl[0], model=spl[1])
|
||||
|
@ -32,7 +32,7 @@ def not_empty_model_change_list(model_name):
|
|||
user = get_current_authenticated_user()
|
||||
if user is None:
|
||||
return False
|
||||
elif user.is_superuser:
|
||||
elif user.is_superuser and get_current_session().get("permission_mask", 0) >= 42:
|
||||
return True
|
||||
spl = model_name.split(".")
|
||||
ct = ContentType.objects.get(app_label=spl[0], model=spl[1])
|
||||
|
|
|
@ -7,7 +7,7 @@ if [ -z ${NOTE_URL+x} ]; then
|
|||
else
|
||||
sed -i -e "s/example.com/$DOMAIN/g" /code/apps/member/fixtures/initial.json
|
||||
sed -i -e "s/localhost/$NOTE_URL/g" /code/note_kfet/fixtures/initial.json
|
||||
sed -i -e "s/\.\*/https?:\/\/$NOTE_URL\/.*/g" /code/note_kfet/fixtures/cas.json
|
||||
sed -i -e "s/\"\.\*\"/\"https?:\/\/$NOTE_URL\/.*\"/g" /code/note_kfet/fixtures/cas.json
|
||||
sed -i -e "s/REPLACEME/La Note Kfet \\\\ud83c\\\\udf7b/g" /code/note_kfet/fixtures/cas.json
|
||||
fi
|
||||
|
||||
|
|
|
@ -1,6 +1,66 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
|
||||
from threading import local
|
||||
|
||||
from django.contrib.sessions.backends.db import SessionStore
|
||||
|
||||
USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
|
||||
SESSION_ATTR_NAME = getattr(settings, 'LOCAL_SESSION_ATTR_NAME', '_current_session')
|
||||
IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
|
||||
|
||||
_thread_locals = local()
|
||||
|
||||
|
||||
def _set_current_user_and_ip(user=None, session=None, ip=None):
|
||||
setattr(_thread_locals, USER_ATTR_NAME, user)
|
||||
setattr(_thread_locals, SESSION_ATTR_NAME, session)
|
||||
setattr(_thread_locals, IP_ATTR_NAME, ip)
|
||||
|
||||
|
||||
def get_current_user() -> User:
|
||||
return getattr(_thread_locals, USER_ATTR_NAME, None)
|
||||
|
||||
|
||||
def get_current_session() -> SessionStore:
|
||||
return getattr(_thread_locals, SESSION_ATTR_NAME, None)
|
||||
|
||||
|
||||
def get_current_ip() -> str:
|
||||
return getattr(_thread_locals, IP_ATTR_NAME, None)
|
||||
|
||||
|
||||
def get_current_authenticated_user():
|
||||
current_user = get_current_user()
|
||||
if isinstance(current_user, AnonymousUser):
|
||||
return None
|
||||
return current_user
|
||||
|
||||
|
||||
class SessionMiddleware(object):
|
||||
"""
|
||||
This middleware get the current user with his or her IP address on each request.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
user = request.user
|
||||
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||
ip = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
|
||||
_set_current_user_and_ip(user, request.session, ip)
|
||||
response = self.get_response(request)
|
||||
_set_current_user_and_ip(None, None, None)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class TurbolinksMiddleware(object):
|
||||
"""
|
||||
|
|
|
@ -74,7 +74,7 @@ if "cas" in INSTALLED_APPS:
|
|||
|
||||
|
||||
if "logs" in INSTALLED_APPS:
|
||||
MIDDLEWARE += ('logs.middlewares.LogsMiddleware',)
|
||||
MIDDLEWARE += ('note_kfet.middlewares.SessionMiddleware',)
|
||||
|
||||
if "debug_toolbar" in INSTALLED_APPS:
|
||||
MIDDLEWARE.insert(1, "debug_toolbar.middleware.DebugToolbarMiddleware")
|
||||
|
|
|
@ -7,6 +7,8 @@ from django.contrib import admin
|
|||
from django.urls import path, include
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
from member.views import CustomLoginView
|
||||
|
||||
urlpatterns = [
|
||||
# Dev so redirect to something random
|
||||
path('', RedirectView.as_view(pattern_name='note:transfer'), name='index'),
|
||||
|
@ -16,10 +18,11 @@ urlpatterns = [
|
|||
|
||||
# Include Django Contrib and Core routers
|
||||
path('i18n/', include('django.conf.urls.i18n')),
|
||||
path('accounts/', include('member.urls')),
|
||||
path('accounts/', include('django.contrib.auth.urls')),
|
||||
path('admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
path('accounts/', include('member.urls')),
|
||||
path('accounts/login/', CustomLoginView.as_view()),
|
||||
path('accounts/', include('django.contrib.auth.urls')),
|
||||
path('api/', include('api.urls')),
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue