forked from maraujop/django-rules
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdecorators.py
More file actions
93 lines (75 loc) · 4 KB
/
decorators.py
File metadata and controls
93 lines (75 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from django.urls import NoReverseMatch, reverse
from django.utils.functional import wraps
from django.utils.http import urlquote
from django_rules.backends import ObjectPermissionBackend, rule_cache
from django_rules.exceptions import NonexistentPermission, RulesError
from django_rules.models import RulePermission
def object_permission_required(perm, **kwargs):
"""
Decorator for views that checks whether a user has a particular permission
The view needs to have a parameter name that matches rule's view_param_pk.
The value of this parameter will be taken as the primary key of the model.
:param login_url: if denied, user would be redirected to location set by
this parameter. Defaults to ``django.conf.settings.LOGIN_URL``.
:param redirect_field_name: name of the parameter passed if redirected.
Defaults to ``django.contrib.auth.REDIRECT_FIELD_NAME``.
:param return_403: if set to ``True`` then instead of redirecting to the
login page, response with status code 403 is returned (
``django.http.HttpResponseForbidden`` instance). Defaults to ``False``.
Examples::
# RulePermission.objects.get_or_create(codename='can_ship',...,view_param_pk='paramView')
@permission_required('can_ship', return_403=True)
def my_view(request, paramView):
return HttpResponse('Hello')
"""
login_url = kwargs.pop("login_url", settings.LOGIN_URL)
redirect_url = kwargs.pop("redirect_url", "")
redirect_field_name = kwargs.pop("redirect_field_name", REDIRECT_FIELD_NAME)
return_403 = kwargs.pop("return_403", False)
# Check if perm is given as string in order to not decorate
# view function itself which makes debugging harder
if not isinstance(perm, str):
raise RulesError("First argument, permission, must be a string")
def decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
obj = None
try:
rule = rule_cache.get_rule_by_codename(perm)
if not rule:
rule = RulePermission.objects.get(codename=perm)
except RulePermission.DoesNotExist:
raise NonexistentPermission("Permission %s does not exist" % perm)
# Only look in kwargs, if the views are entry points through urls Django passes parameters as kwargs
# We could look in args using inspect.getcallargs in Python 2.7 or a custom function that
# imitates it, but if the view is internal, I think it's better to force the user to pass
# parameters as kwargs
if rule.view_param_pk not in kwargs:
raise RulesError(
"The view does not have a parameter called %s in kwargs"
% rule.view_param_pk
)
model_class = rule.content_type.model_class()
obj = get_object_or_404(model_class, pk=kwargs[rule.view_param_pk])
if not request.user.has_perm(perm, obj):
if return_403:
raise PermissionDenied
else:
if redirect_url:
try:
path = urlquote(request.get_full_path())
redirect_url_reversed = reverse(redirect_url)
tup = redirect_url_reversed, redirect_field_name, path
except NoReverseMatch:
tup = redirect_url, redirect_field_name, path
else:
path = urlquote(request.get_full_path())
tup = login_url, redirect_field_name, path
return HttpResponseRedirect("%s?%s=%s" % tup)
return view_func(request, *args, **kwargs)
return wraps(view_func)(_wrapped_view)
return decorator