From 51e25b6b0eba6bb8afc1c0e149983db94612bb70 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Tue, 16 Apr 2019 10:55:11 +0200 Subject: [PATCH 4/4] allow checking for authentication level and request increase --- chrono/agendas/models.py | 31 +++++++++++++++++++++++++------ chrono/exceptions.py | 4 ++++ chrono/manager/views.py | 2 +- chrono/urls_utils.py | 17 ++++++++++++++++- 4 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 chrono/exceptions.py diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index 3eea87a..a752f2d 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -36,6 +36,7 @@ from django.utils.translation import ugettext_lazy as _ from jsonfield import JSONField from ..interval import Intervals +from chrono.exceptions import InsufficientAuthLevel AGENDA_KINDS = ( @@ -93,17 +94,35 @@ class Agenda(models.Model): def get_absolute_url(self): return reverse('chrono-manager-agenda-view', kwargs={'pk': self.id}) - def can_be_managed(self, user): + @staticmethod + def user_has_sufficient_auth_level(user, role, can_raise): + if hasattr(role, 'auth_level') and user.auth_level < role.auth_level.value: + if can_raise: + raise InsufficientAuthLevel(role.auth_level.value) + return False + return True + + def can_be_managed(self, user, can_raise=True): if user.is_staff: return True group_ids = [x.id for x in user.groups.all()] - return bool(self.edit_role_id in group_ids) + if self.edit_role_id in group_ids: + return self.user_has_sufficient_auth_level(user, self.edit_role, can_raise) + return False - def can_be_viewed(self, user): - if self.can_be_managed(user): - return True + def can_be_viewed(self, user, can_raise=True): + would_have_raised = False + try: + if self.can_be_managed(user, can_raise): + return True + except InsufficientAuthLevel as e: + would_have_raised = e.auth_level.value group_ids = [x.id for x in user.groups.all()] - return bool(self.view_role_id in group_ids) + if self.view_role_id in group_ids: + return self.user_has_sufficient_auth_level(user, self.view_role, can_raise) + if would_have_raised: + raise InsufficientAuthLevel(would_have_raised) + return False def get_base_meeting_duration(self): durations = [x.duration for x in MeetingType.objects.filter(agenda=self)] diff --git a/chrono/exceptions.py b/chrono/exceptions.py new file mode 100644 index 0000000..b9750a2 --- /dev/null +++ b/chrono/exceptions.py @@ -0,0 +1,4 @@ +class InsufficientAuthLevel(Exception): + + def __init__(self, level): + self.required_level = level diff --git a/chrono/manager/views.py b/chrono/manager/views.py index 80a59e4..5bf1c17 100644 --- a/chrono/manager/views.py +++ b/chrono/manager/views.py @@ -534,7 +534,7 @@ class AgendaSettings(ManagedAgendaMixin, DetailView): self.agenda = Agenda.objects.get(id=kwargs.get('pk')) except Agenda.DoesNotExist: raise Http404() - if not self.agenda.can_be_managed(request.user): + if not self.agenda.can_be_managed(request.user, can_raise=False): # "events" agendas settings page can be access by user with the # view permission as there are no other "view" page for this type # of agenda. diff --git a/chrono/urls_utils.py b/chrono/urls_utils.py index 2fc535d..5dd8441 100644 --- a/chrono/urls_utils.py +++ b/chrono/urls_utils.py @@ -17,11 +17,13 @@ # Decorating URL includes, from django.contrib.auth.decorators import user_passes_test +from django.contrib.auth.views import redirect_to_login from django.core.exceptions import PermissionDenied from django.core.urlresolvers import RegexURLPattern, RegexURLResolver from django.db.models import Q from .agendas.models import Agenda +from .exceptions import InsufficientAuthLevel class DecoratedURLPattern(RegexURLPattern): @@ -53,6 +55,19 @@ def decorated_includes(func, includes, *args, **kwargs): return urlconf_module, app_name, namespace def manager_required(function=None, login_url=None): + + def check_auth_level(func): + def wrapped(request, *args, **kwargs): + try: + request.user.auth_level = request.session.get('auth_level', 1) + return func(request, *args, **kwargs) + except InsufficientAuthLevel as e: + required_auth_level = e.required_level + next_field_value = request.get_full_path() + login_url = '/login/?auth_level=%s' % required_auth_level + return redirect_to_login(next_field_value, login_url) + return wrapped + def check_manager(user): if user and user.is_staff: return True @@ -66,5 +81,5 @@ def manager_required(function=None, login_url=None): return False actual_decorator = user_passes_test(check_manager, login_url=login_url) if function: - return actual_decorator(function) + return check_auth_level(actual_decorator(function)) return actual_decorator -- 2.20.1