From 287d2eccd54dd7f80a750e3bd855da83c31ca693 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Fri, 3 Jul 2009 15:41:21 +0200 Subject: [PATCH] Fixed decorator to only check for permissions if user is logged in. Added permission_denied view to be used when permission check unsuccessful, e.g. with the new permission_required_or_403 decorator. The permission_required decorator will redirect to the login form by default. --- example/exampleapp/views.py | 3 +- src/authority/decorators.py | 59 ++++++++++++++++++++++--------------- src/authority/views.py | 19 ++++++++++++ 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/example/exampleapp/views.py b/example/exampleapp/views.py index 81522c8..b1b3a08 100644 --- a/example/exampleapp/views.py +++ b/example/exampleapp/views.py @@ -1,8 +1,9 @@ from django.contrib.flatpages.views import flatpage from django.contrib.flatpages.models import FlatPage -from authority.decorators import permission_required +from authority.decorators import permission_required, permission_required_or_403 +#@permission_required_or_403('flatpage_permission.top_secret', (FlatPage, 'url__contains')) # use this to return a 403 page @permission_required('flatpage_permission.top_secret', (FlatPage, 'url__contains')) def top_secret(request, url): """ diff --git a/src/authority/decorators.py b/src/authority/decorators.py index af00963..6693d3c 100644 --- a/src/authority/decorators.py +++ b/src/authority/decorators.py @@ -10,38 +10,49 @@ from django.contrib.auth.decorators import user_passes_test from django.contrib.auth import REDIRECT_FIELD_NAME from authority import permissions +from authority.views import permission_denied def permission_required(perm, *args, **kwargs): """ Decorator for views that checks whether a user has a particular permission enabled, redirecting to the log-in page if necessary. """ - login_url = kwargs.pop('login_url', None) - if not login_url: - login_url = getattr(settings, 'LOGIN_URL', '/') + login_url = kwargs.pop('login_url', settings.LOGIN_URL) redirect_field_name = kwargs.pop('redirect_field_name', REDIRECT_FIELD_NAME) + redirect_to_login = kwargs.pop('redirect_to_login', True) model_lookups = args def _permission_required(view_func, request, *args, **kwargs): objs = [] - # model_lookups = [('flatpages.flatpage', 'url__contains')] - for i, arguments in enumerate(model_lookups): - model, lookup = arguments - if isinstance(model, basestring): - model_class = get_model(*model.split(".")) - else: - model_class = model - if model_class is None: - raise ValueError( - "The given argument '%s' is not a valid model." % model) - if inspect.isclass(model_class) and \ - not issubclass(model_class, Model): - raise ValueError( - 'The argument %s needs to be a model.' % model) - objs.append(get_object_or_404(model_class, **{lookup: args[i]})) - check = permissions.registry.get_check(request.user, perm) - if check is not None: - if check(*objs): - return view_func(request, *args, **kwargs) - #return HttpResponseForbidden("Permission") - raise PermissionDenied() + if request.user.is_authenticated(): + for i, arguments in enumerate(model_lookups): + model, lookup = arguments + if isinstance(model, basestring): + model_class = get_model(*model.split(".")) + else: + model_class = model + if model_class is None: + raise ValueError( + "The given argument '%s' is not a valid model." % model) + if inspect.isclass(model_class) and \ + not issubclass(model_class, Model): + raise ValueError( + 'The argument %s needs to be a model.' % model) + objs.append(get_object_or_404(model_class, **{lookup: args[i]})) + check = permissions.registry.get_check(request.user, perm) + if check is not None: + if check(*objs): + return view_func(request, *args, **kwargs) + if redirect_to_login: + path = urlquote(request.get_full_path()) + tup = login_url, redirect_field_name, path + return HttpResponseRedirect('%s?%s=%s' % tup) + return permission_denied(request) return decorator(_permission_required) + +def permission_required_or_403(perm, *args, **kwargs): + """ + Decorator that wraps the permission_required decorator and returns a + permission denied (403) page instead of redirecting to the login URL. + """ + kwargs['redirect_to_login'] = False + return permission_required(perm, *args, **kwargs) diff --git a/src/authority/views.py b/src/authority/views.py index 2cac76b..b9e1029 100644 --- a/src/authority/views.py +++ b/src/authority/views.py @@ -5,6 +5,7 @@ from django.db.models.loading import get_model from django.core.urlresolvers import reverse from django.utils.translation import ugettext, ugettext_lazy as _ from django.template.context import RequestContext +from django.template import loader from django.contrib.auth.decorators import login_required from authority.models import Permission @@ -56,3 +57,21 @@ def delete_permission(request, permission_pk): message=ugettext('You removed the permission.')) next = request.REQUEST.get('next') or '/' return HttpResponseRedirect(next) + +def permission_denied(request, template_name=None, extra_context={}): + """ + Default 403 handler. + + Templates: `403.html` + Context: + request_path + The path of the requested URL (e.g., '/app/pages/bad_page/') + """ + if template_name is None: + template_name = ('authority/403.html', '403.html') + context = { + 'request_path': request.path, + } + context.update(extra_context) + return HttpResponseForbidden(loader.render_to_string(template_name, context, + context_instance=RequestContext(request)))