diff --git a/poradnia/advicer/views.py b/poradnia/advicer/views.py index 9965ab0d2..8ef65ce99 100644 --- a/poradnia/advicer/views.py +++ b/poradnia/advicer/views.py @@ -1,16 +1,13 @@ - from atom.ext.crispy_forms.views import FormSetMixin from atom.views import ActionMessageMixin, ActionView, FormInitialMixin -from braces.views import (FormValidMessageMixin, LoginRequiredMixin, - SelectRelatedMixin, StaffuserRequiredMixin, +from braces.views import (FormValidMessageMixin, SelectRelatedMixin, UserFormKwargsMixin) from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from django.views.generic import CreateView, DetailView, UpdateView from django_filters.views import FilterView -from users.utils import PermissionMixin - +from users.utils import PermissionMixin, StaffuserRequiredMixin from .filters import AdviceFilter from .forms import AdviceForm, AttachmentForm from .models import Advice, Attachment @@ -49,7 +46,7 @@ def get_instance(self): class AdviceCreate(StaffuserRequiredMixin, FormSetMixin, FormInitialMixin, UserFormKwargsMixin, - LoginRequiredMixin, CreateView): + CreateView): model = Advice form_class = AdviceForm inline_model = Attachment diff --git a/poradnia/cases/views/cases.py b/poradnia/cases/views/cases.py index 98e688758..cc024d24b 100644 --- a/poradnia/cases/views/cases.py +++ b/poradnia/cases/views/cases.py @@ -1,7 +1,8 @@ from __future__ import absolute_import -from braces.views import LoginRequiredMixin, UserFormKwargsMixin +from braces.views import UserFormKwargsMixin from django.contrib import messages +from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import get_object_or_404, redirect from django.utils.translation import ugettext as _ from django.views.generic import TemplateView, UpdateView diff --git a/poradnia/events/views.py b/poradnia/events/views.py index 5e1d9b684..27f0e1ebf 100644 --- a/poradnia/events/views.py +++ b/poradnia/events/views.py @@ -1,7 +1,7 @@ import locale -from braces.views import (FormValidMessageMixin, LoginRequiredMixin, - SelectRelatedMixin, UserFormKwargsMixin) +from braces.views import (FormValidMessageMixin, SelectRelatedMixin, + UserFormKwargsMixin) from dateutil.relativedelta import relativedelta from django.conf import settings from django.http import HttpResponse @@ -16,7 +16,6 @@ from cases.models import Case from keys.mixins import KeyAuthMixin from users.utils import PermissionMixin - from .forms import EventForm from .models import Event from .utils import EventCalendar @@ -64,14 +63,14 @@ def dismiss(request, pk): # TODO pass -class CalendarListView(PermissionMixin, LoginRequiredMixin, ArchiveIndexView): +class CalendarListView(PermissionMixin, ArchiveIndexView): model = Event date_field = 'time' allow_future = True date_list_period = 'month' -class CalendarEventView(PermissionMixin, SelectRelatedMixin, LoginRequiredMixin, MonthArchiveView): +class CalendarEventView(PermissionMixin, SelectRelatedMixin, MonthArchiveView): model = Event date_field = "time" allow_future = True diff --git a/poradnia/notifications_custom/views.py b/poradnia/notifications_custom/views.py index 3f6489fd8..45a4c4b91 100644 --- a/poradnia/notifications_custom/views.py +++ b/poradnia/notifications_custom/views.py @@ -1,5 +1,5 @@ -from braces.views import (LoginRequiredMixin, PrefetchRelatedMixin, - SelectRelatedMixin) +from braces.views import PrefetchRelatedMixin, SelectRelatedMixin +from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic import TemplateView diff --git a/poradnia/stats/tests.py b/poradnia/stats/tests.py index b536c7421..84868b37c 100644 --- a/poradnia/stats/tests.py +++ b/poradnia/stats/tests.py @@ -1,7 +1,7 @@ from datetime import datetime from unittest import skipUnless -from dateutil.rrule import MONTHLY, WEEKLY, DAILY +from dateutil.rrule import MONTHLY, WEEKLY from django.core.urlresolvers import reverse from django.db import connection from django.http.response import HttpResponse @@ -11,8 +11,8 @@ from cases.models import Case from letters.factories import LetterFactory from letters.models import Letter -from users.factories import UserFactory from stats.utils import GapFiller, DATE_FORMAT_MONTHLY, DATE_FORMAT_WEEKLY +from users.factories import UserFactory def polyfill_http_response_json(): @@ -37,7 +37,6 @@ def test_permission_forbidden(self): self.assertEqual(resp.status_code, 403) def test_permission_not_logged_in(self): - user = UserFactory(is_superuser=False) for url in[self.api_url, self.render_url, self.main_url]: resp = self.client.get(url) self.assertEqual(resp.status_code, 302) diff --git a/poradnia/stats/utils.py b/poradnia/stats/utils.py index e749be7eb..43f066399 100644 --- a/poradnia/stats/utils.py +++ b/poradnia/stats/utils.py @@ -1,19 +1,43 @@ from datetime import datetime from dateutil.rrule import rrule, MONTHLY, WEEKLY - -from django.shortcuts import redirect +from django.contrib.auth.mixins import AccessMixin +from django.contrib.auth.views import redirect_to_login +from django.core.exceptions import PermissionDenied SECONDS_IN_A_DAY = 60 * 60 * 24 DATE_FORMAT_MONTHLY = "%Y-%m" DATE_FORMAT_WEEKLY = "%Y-%W" -def raise_unless_unauthenticated(view, request): - # Hack from SO due to https://github.com/brack3t/django-braces/issues/181 bug - if not request.user.is_authenticated(): - return redirect('/konta/login/?next=%s' % request.path) - return None + +class NoPermissionHandlerMixin(AccessMixin): + """ + Mixin borrowed from Braces. If user is authenticated and does not have + permissions, it's redirected to 403 page. Anonymous user is redirected to + log in page + """ + def handle_no_permission(self): + if self.raise_exception: + if (self.redirect_unauthenticated_users + and not self.request.user.is_authenticated()): + return self.no_permissions_fail() + else: + raise PermissionDenied + + return self.no_permissions_fail() + + def no_permissions_fail(self): + """ + Called when the user has no permissions and no exception was raised. + This should only return a valid HTTP response. + + By default we redirect to login. + """ + return redirect_to_login(self.request.get_full_path(), + self.get_login_url(), + self.get_redirect_field_name()) + class GapFiller(object): def __init__(self, qs, freq, date_key, date_format): diff --git a/poradnia/stats/views.py b/poradnia/stats/views.py index 41e00b0bd..4efccf441 100644 --- a/poradnia/stats/views.py +++ b/poradnia/stats/views.py @@ -1,15 +1,13 @@ -from datetime import datetime +from braces.views import JSONResponseMixin from dateutil.rrule import MONTHLY - -from django.db.models import F, Func, IntegerField, Case, Sum, When, Min, Count -from django.shortcuts import redirect -from braces.views import JSONResponseMixin, LoginRequiredMixin, SuperuserRequiredMixin +from django.db.models import F, IntegerField, Case, Sum, When, Min, Count from django.views.generic import TemplateView from django.views.generic import View from cases.models import Case as CaseModel from letters.models import Letter as LetterModel -from .utils import raise_unless_unauthenticated, GapFiller, SECONDS_IN_A_DAY, DATE_FORMAT_MONTHLY +from users.utils import SuperuserRequiredMixin +from .utils import GapFiller, SECONDS_IN_A_DAY, DATE_FORMAT_MONTHLY, NoPermissionHandlerMixin class ApiListViewMixin(JSONResponseMixin): @@ -21,18 +19,24 @@ class StatsIndexView(TemplateView): template_name = 'stats/index.html' -class StatsCaseCreatedView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsCaseCreatedView(NoPermissionHandlerMixin, SuperuserRequiredMixin, + TemplateView): template_name = 'stats/cases/created.html' - raise_exception = raise_unless_unauthenticated + raise_exception = True + redirect_unauthenticated_users = True -class StatsCaseCreatedRenderView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsCaseCreatedRenderView(NoPermissionHandlerMixin, + SuperuserRequiredMixin, TemplateView): template_name = 'stats/render/cases/created.html' - raise_exception = raise_unless_unauthenticated + raise_exception = True + redirect_unauthenticated_users = True -class StatsCaseCreatedApiView(LoginRequiredMixin, SuperuserRequiredMixin, ApiListViewMixin, View): - raise_exception = raise_unless_unauthenticated +class StatsCaseCreatedApiView(NoPermissionHandlerMixin, SuperuserRequiredMixin, + ApiListViewMixin, View): + raise_exception = True + redirect_unauthenticated_users = True def get_object_list(self): qs = CaseModel.objects.with_month_year().values( @@ -80,18 +84,15 @@ def get_object_list(self): ).fill_gaps() -class StatsCaseReactionView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsCaseReactionView(SuperuserRequiredMixin, TemplateView): template_name = 'stats/cases/reaction.html' - raise_exception = raise_unless_unauthenticated -class StatsCaseReactionRenderView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsCaseReactionRenderView(SuperuserRequiredMixin, TemplateView): template_name = 'stats/render/cases/reaction.html' - raise_exception = raise_unless_unauthenticated -class StatsCaseReactionApiView(LoginRequiredMixin, SuperuserRequiredMixin, ApiListViewMixin, View): - raise_exception = raise_unless_unauthenticated +class StatsCaseReactionApiView(SuperuserRequiredMixin, ApiListViewMixin, View): def get_object_list(self): qs = ( @@ -133,19 +134,15 @@ def get_object_list(self): ).fill_gaps() -class StatsCaseUnansweredView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsCaseUnansweredView(SuperuserRequiredMixin, TemplateView): template_name = 'stats/cases/unanswered.html' - raise_exception = raise_unless_unauthenticated -class StatsCaseUnansweredRenderView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsCaseUnansweredRenderView(SuperuserRequiredMixin, TemplateView): template_name = 'stats/render/cases/unanswered.html' - raise_exception = raise_unless_unauthenticated -class StatsCaseUnansweredApiView(LoginRequiredMixin, SuperuserRequiredMixin, ApiListViewMixin, - View): - raise_exception = raise_unless_unauthenticated +class StatsCaseUnansweredApiView(SuperuserRequiredMixin, ApiListViewMixin, View): def get_object_list(self): qs = CaseModel.objects.filter( @@ -171,18 +168,15 @@ def get_object_list(self): ).fill_gaps() -class StatsLetterCreatedView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsLetterCreatedView(SuperuserRequiredMixin, TemplateView): template_name = 'stats/letters/created.html' - raise_exception = raise_unless_unauthenticated -class StatsLetterCreatedRenderView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): +class StatsLetterCreatedRenderView(SuperuserRequiredMixin, TemplateView): template_name = 'stats/render/letters/created.html' - raise_exception = raise_unless_unauthenticated -class StatsLetterCreatedApiView(LoginRequiredMixin, SuperuserRequiredMixin, ApiListViewMixin, View): - raise_exception = raise_unless_unauthenticated +class StatsLetterCreatedApiView(SuperuserRequiredMixin, ApiListViewMixin, View): def get_object_list(self): qs = LetterModel.objects.with_month_year().values( diff --git a/poradnia/tasty_feedback/views.py b/poradnia/tasty_feedback/views.py index 3e9cd40a8..9c54c1054 100644 --- a/poradnia/tasty_feedback/views.py +++ b/poradnia/tasty_feedback/views.py @@ -1,7 +1,7 @@ -from braces.views import (FormValidMessageMixin, LoginRequiredMixin, - PermissionRequiredMixin, SelectRelatedMixin, +from braces.views import (FormValidMessageMixin, SelectRelatedMixin, UserFormKwargsMixin) from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.urlresolvers import reverse_lazy from django.http import HttpResponseRedirect from django.utils.translation import ugettext as _ @@ -12,7 +12,7 @@ from .utils import get_filter, get_form -class FeedbackListView(LoginRequiredMixin, PermissionRequiredMixin, SelectRelatedMixin, FilterView): +class FeedbackListView(PermissionRequiredMixin, SelectRelatedMixin, FilterView): permission_required = "tasty_feedback.view_feedback" filterset_class = get_filter() select_related = ["user", ] @@ -29,12 +29,12 @@ def get_form_valid_message(self): return _("Feedback saved.") -class FeedbackDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): +class FeedbackDetailView(PermissionRequiredMixin, DetailView): permission_required = "tasty_feedback.view_feedback" model = Feedback -class FeedbackStatusView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): +class FeedbackStatusView(PermissionRequiredMixin, DeleteView): permission_required = "tasty_feedback.change_feedback" model = Feedback template_name_suffix = '_switch_status' diff --git a/poradnia/users/utils.py b/poradnia/users/utils.py index be88657ad..587b8e511 100644 --- a/poradnia/users/utils.py +++ b/poradnia/users/utils.py @@ -1,4 +1,4 @@ -from braces.views import LoginRequiredMixin +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.core.exceptions import PermissionDenied from guardian.shortcuts import get_perms @@ -16,6 +16,18 @@ def has_perms(user, perms, obj=None, required_all=True): raise PermissionDenied +class SuperuserRequiredMixin(UserPassesTestMixin): + + def test_func(self): + return self.request.user.is_superuser + + +class StaffuserRequiredMixin(UserPassesTestMixin): + + def test_func(self): + return self.request.user.is_staff + + class PermissionMixin(LoginRequiredMixin, object): def get_queryset(self, *args, **kwargs): qs = super(PermissionMixin, self).get_queryset(*args, **kwargs) diff --git a/poradnia/users/views.py b/poradnia/users/views.py index 4dc2a3444..c7c591b6b 100644 --- a/poradnia/users/views.py +++ b/poradnia/users/views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from braces.views import LoginRequiredMixin, StaffuserRequiredMixin +from django.contrib.auth.mixins import (LoginRequiredMixin, + PermissionRequiredMixin) from django.core.urlresolvers import reverse -from django.contrib.auth.mixins import PermissionRequiredMixin from django.forms import modelform_factory from django.utils.translation import ugettext_lazy as _ from django.views.generic import DetailView, RedirectView, UpdateView @@ -10,7 +10,7 @@ from .filters import UserFilter from .forms import ProfileForm, UserForm from .models import Profile, User -from .utils import PermissionMixin +from .utils import PermissionMixin, StaffuserRequiredMixin class UserDetailView(PermissionRequiredMixin, DetailView):