From 08488af7184384c268a42d9591fc910fcebd1b6a Mon Sep 17 00:00:00 2001 From: Christopher Charbonneau Wells Date: Sun, 20 May 2018 14:40:09 -0700 Subject: [PATCH] Raise permission denied instead of redirecting to login (#49). --- babybuddy/mixins.py | 9 ++++++++- babybuddy/templates/403.html | 15 +++++++++++++++ babybuddy/views.py | 18 +++++++----------- core/views.py | 37 ++++++++++++++++++------------------ dashboard/views.py | 7 ++++--- reports/views.py | 14 +++++++------- 6 files changed, 60 insertions(+), 40 deletions(-) create mode 100644 babybuddy/templates/403.html diff --git a/babybuddy/mixins.py b/babybuddy/mixins.py index 46007335..3d3b7812 100644 --- a/babybuddy/mixins.py +++ b/babybuddy/mixins.py @@ -1,5 +1,12 @@ # -*- coding: utf-8 -*- -from django.contrib.auth.mixins import AccessMixin +from django.contrib.auth.mixins import AccessMixin, PermissionRequiredMixin + + +class PermissionRequired403Mixin(PermissionRequiredMixin): + """ + Raise an exception instead of redirecting to login. + """ + raise_exception = True class StaffOnlyMixin(AccessMixin): diff --git a/babybuddy/templates/403.html b/babybuddy/templates/403.html new file mode 100644 index 00000000..0eb0d4d5 --- /dev/null +++ b/babybuddy/templates/403.html @@ -0,0 +1,15 @@ +{% extends 'babybuddy/page.html' %} +{% load widget_tweaks %} + +{% block title %}403 Permission Denied{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} + +{% endblock %} \ No newline at end of file diff --git a/babybuddy/views.py b/babybuddy/views.py index e138db64..39234da4 100644 --- a/babybuddy/views.py +++ b/babybuddy/views.py @@ -2,8 +2,7 @@ from django.contrib import messages from django.contrib.auth import update_session_auth_hash from django.contrib.auth.forms import PasswordChangeForm -from django.contrib.auth.mixins import (LoginRequiredMixin, - PermissionRequiredMixin) +from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.contrib.messages.views import SuccessMessageMixin from django.shortcuts import redirect, render @@ -15,15 +14,12 @@ from django.views.generic.edit import CreateView, UpdateView, DeleteView from django_filters.views import FilterView from babybuddy import forms -from babybuddy.mixins import StaffOnlyMixin -from core import models +from babybuddy.mixins import PermissionRequired403Mixin, StaffOnlyMixin class RootRouter(LoginRequiredMixin, RedirectView): """ - Redirects to the welcome page if no children are in the database, a child - dashboard if only one child is in the database, and the dashboard page if - more than one child is in the database. + Redirects to the site dashboard. """ def get_redirect_url(self, *args, **kwargs): self.url = reverse('dashboard:dashboard') @@ -38,7 +34,7 @@ class UserList(StaffOnlyMixin, FilterView): filter_fields = ('username', 'first_name', 'last_name', 'email') -class UserAdd(StaffOnlyMixin, PermissionRequiredMixin, SuccessMessageMixin, +class UserAdd(StaffOnlyMixin, PermissionRequired403Mixin, SuccessMessageMixin, CreateView): model = User template_name = 'babybuddy/user_form.html' @@ -48,8 +44,8 @@ class UserAdd(StaffOnlyMixin, PermissionRequiredMixin, SuccessMessageMixin, success_message = 'User %(username)s added!' -class UserUpdate(StaffOnlyMixin, PermissionRequiredMixin, SuccessMessageMixin, - UpdateView): +class UserUpdate(StaffOnlyMixin, PermissionRequired403Mixin, + SuccessMessageMixin, UpdateView): model = User template_name = 'babybuddy/user_form.html' permission_required = ('admin.change_user',) @@ -58,7 +54,7 @@ class UserUpdate(StaffOnlyMixin, PermissionRequiredMixin, SuccessMessageMixin, success_message = 'User %(username)s updated.' -class UserDelete(StaffOnlyMixin, PermissionRequiredMixin, +class UserDelete(StaffOnlyMixin, PermissionRequired403Mixin, DeleteView): model = User template_name = 'babybuddy/user_confirm_delete.html' diff --git a/core/views.py b/core/views.py index b117e2f2..8a5b5388 100644 --- a/core/views.py +++ b/core/views.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from django.contrib import messages -from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.urls import reverse, reverse_lazy from django.utils import timezone @@ -10,10 +9,11 @@ from django.views.generic.edit import CreateView, UpdateView, DeleteView from django_filters.views import FilterView +from babybuddy.mixins import PermissionRequired403Mixin from core import forms, models, timeline -class CoreAddView(PermissionRequiredMixin, SuccessMessageMixin, CreateView): +class CoreAddView(PermissionRequired403Mixin, SuccessMessageMixin, CreateView): def get_success_message(self, cleaned_data): cleaned_data['model'] = self.model._meta.verbose_name.title() if 'child' in cleaned_data: @@ -23,7 +23,8 @@ class CoreAddView(PermissionRequiredMixin, SuccessMessageMixin, CreateView): return self.success_message % cleaned_data -class CoreUpdateView(PermissionRequiredMixin, SuccessMessageMixin, UpdateView): +class CoreUpdateView(PermissionRequired403Mixin, SuccessMessageMixin, + UpdateView): def get_success_message(self, cleaned_data): cleaned_data['model'] = self.model._meta.verbose_name.title() if 'child' in cleaned_data: @@ -33,7 +34,7 @@ class CoreUpdateView(PermissionRequiredMixin, SuccessMessageMixin, UpdateView): return self.success_message % cleaned_data -class CoreDeleteView(PermissionRequiredMixin, DeleteView): +class CoreDeleteView(PermissionRequired403Mixin, DeleteView): """ SuccessMessageMixin is not compatible DeleteView. See: https://code.djangoproject.com/ticket/21936 @@ -45,7 +46,7 @@ class CoreDeleteView(PermissionRequiredMixin, DeleteView): return super(CoreDeleteView, self).delete(request, *args, **kwargs) -class ChildList(PermissionRequiredMixin, FilterView): +class ChildList(PermissionRequired403Mixin, FilterView): model = models.Child template_name = 'core/child_list.html' permission_required = ('core.view_child',) @@ -61,7 +62,7 @@ class ChildAdd(CoreAddView): success_message = '%(first_name)s %(last_name)s added!' -class ChildDetail(PermissionRequiredMixin, DetailView): +class ChildDetail(PermissionRequired403Mixin, DetailView): model = models.Child permission_required = ('core.view_child',) @@ -93,7 +94,7 @@ class ChildDelete(CoreUpdateView): success_url = reverse_lazy('core:child-list') -class DiaperChangeList(PermissionRequiredMixin, FilterView): +class DiaperChangeList(PermissionRequired403Mixin, FilterView): model = models.DiaperChange template_name = 'core/diaperchange_list.html' permission_required = ('core.view_diaperchange',) @@ -121,7 +122,7 @@ class DiaperChangeDelete(CoreDeleteView): success_url = reverse_lazy('core:diaperchange-list') -class FeedingList(PermissionRequiredMixin, FilterView): +class FeedingList(PermissionRequired403Mixin, FilterView): model = models.Feeding template_name = 'core/feeding_list.html' permission_required = ('core.view_feeding',) @@ -155,7 +156,7 @@ class FeedingDelete(CoreDeleteView): success_url = reverse_lazy('core:feeding-list') -class NoteList(PermissionRequiredMixin, FilterView): +class NoteList(PermissionRequired403Mixin, FilterView): model = models.Note template_name = 'core/note_list.html' permission_required = ('core.view_note',) @@ -183,7 +184,7 @@ class NoteDelete(CoreDeleteView): success_url = reverse_lazy('core:note-list') -class SleepList(PermissionRequiredMixin, FilterView): +class SleepList(PermissionRequired403Mixin, FilterView): model = models.Sleep template_name = 'core/sleep_list.html' permission_required = ('core.view_sleep',) @@ -217,7 +218,7 @@ class SleepDelete(CoreDeleteView): success_url = reverse_lazy('core:sleep-list') -class TimerList(PermissionRequiredMixin, FilterView): +class TimerList(PermissionRequired403Mixin, FilterView): model = models.Timer template_name = 'core/timer_list.html' permission_required = ('core.view_timer',) @@ -225,12 +226,12 @@ class TimerList(PermissionRequiredMixin, FilterView): filter_fields = ('active', 'user') -class TimerDetail(PermissionRequiredMixin, DetailView): +class TimerDetail(PermissionRequired403Mixin, DetailView): model = models.Timer permission_required = ('core.view_timer',) -class TimerAdd(PermissionRequiredMixin, CreateView): +class TimerAdd(PermissionRequired403Mixin, CreateView): model = models.Timer permission_required = ('core.add_timer',) form_class = forms.TimerForm @@ -260,7 +261,7 @@ class TimerUpdate(CoreUpdateView): return reverse('core:timer-detail', kwargs={'pk': instance.pk}) -class TimerAddQuick(PermissionRequiredMixin, RedirectView): +class TimerAddQuick(PermissionRequired403Mixin, RedirectView): permission_required = ('core.add_timer',) def get(self, request, *args, **kwargs): @@ -271,7 +272,7 @@ class TimerAddQuick(PermissionRequiredMixin, RedirectView): return super(TimerAddQuick, self).get(request, *args, **kwargs) -class TimerRestart(PermissionRequiredMixin, RedirectView): +class TimerRestart(PermissionRequired403Mixin, RedirectView): permission_required = ('core.change_timer',) def get(self, request, *args, **kwargs): @@ -284,7 +285,7 @@ class TimerRestart(PermissionRequiredMixin, RedirectView): return reverse('core:timer-detail', kwargs={'pk': kwargs['pk']}) -class TimerStop(PermissionRequiredMixin, SuccessMessageMixin, RedirectView): +class TimerStop(PermissionRequired403Mixin, SuccessMessageMixin, RedirectView): permission_required = ('core.change_timer',) success_message = '%(timer)s stopped.' @@ -304,7 +305,7 @@ class TimerDelete(CoreDeleteView): success_url = reverse_lazy('core:timer-list') -class TummyTimeList(PermissionRequiredMixin, FilterView): +class TummyTimeList(PermissionRequired403Mixin, FilterView): model = models.TummyTime template_name = 'core/tummytime_list.html' permission_required = ('core.view_tummytime',) @@ -338,7 +339,7 @@ class TummyTimeDelete(CoreDeleteView): success_url = reverse_lazy('core:tummytime-list') -class WeightList(PermissionRequiredMixin, FilterView): +class WeightList(PermissionRequired403Mixin, FilterView): model = models.Weight template_name = 'core/weight_list.html' permission_required = ('core.view_weight',) diff --git a/dashboard/views.py b/dashboard/views.py index 83000a45..5a713099 100644 --- a/dashboard/views.py +++ b/dashboard/views.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -from django.contrib.auth.mixins import (LoginRequiredMixin, - PermissionRequiredMixin) +from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpResponseRedirect from django.urls import reverse from django.views.generic.base import TemplateView from django.views.generic.detail import DetailView +from babybuddy.mixins import PermissionRequired403Mixin from core.models import Child @@ -33,7 +33,8 @@ class Dashboard(LoginRequiredMixin, TemplateView): return context -class ChildDashboard(PermissionRequiredMixin, DetailView): +class ChildDashboard(PermissionRequired403Mixin, DetailView): model = Child permission_required = ('core.view_child',) + raise_exception = True template_name = 'dashboard/child.html' diff --git a/reports/views.py b/reports/views.py index 797d8b60..1c710031 100644 --- a/reports/views.py +++ b/reports/views.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -from django.contrib.auth.mixins import PermissionRequiredMixin from django.views.generic.detail import DetailView +from babybuddy.mixins import PermissionRequired403Mixin from core import models from . import graphs -class DiaperChangeLifetimesChildReport(PermissionRequiredMixin, DetailView): +class DiaperChangeLifetimesChildReport(PermissionRequired403Mixin, DetailView): """ Graph of diaper "lifetimes" - time between diaper changes. """ @@ -26,7 +26,7 @@ class DiaperChangeLifetimesChildReport(PermissionRequiredMixin, DetailView): return context -class DiaperChangeTypesChildReport(PermissionRequiredMixin, DetailView): +class DiaperChangeTypesChildReport(PermissionRequired403Mixin, DetailView): """ Graph of diaper changes by day and type. """ @@ -45,7 +45,7 @@ class DiaperChangeTypesChildReport(PermissionRequiredMixin, DetailView): return context -class FeedingDurationChildReport(PermissionRequiredMixin, DetailView): +class FeedingDurationChildReport(PermissionRequired403Mixin, DetailView): """ Graph of feeding durations over time. """ @@ -69,7 +69,7 @@ class FeedingDurationChildReport(PermissionRequiredMixin, DetailView): return context -class SleepPatternChildReport(PermissionRequiredMixin, DetailView): +class SleepPatternChildReport(PermissionRequired403Mixin, DetailView): """ Graph of sleep pattern comparing sleep to wake times by day. """ @@ -93,7 +93,7 @@ class SleepPatternChildReport(PermissionRequiredMixin, DetailView): return context -class SleepTotalsChildReport(PermissionRequiredMixin, DetailView): +class SleepTotalsChildReport(PermissionRequired403Mixin, DetailView): """ Graph of total sleep by day. """ @@ -117,7 +117,7 @@ class SleepTotalsChildReport(PermissionRequiredMixin, DetailView): return context -class WeightWeightChildReoport(PermissionRequiredMixin, DetailView): +class WeightWeightChildReoport(PermissionRequired403Mixin, DetailView): """ Graph of weight change over time. """