Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #4617 -- Added `raise_exception` option to `permission_required…

…` decorator to be able to raise a PermissionDenied exception instead of redirecting to the login page.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16607 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 351d5da69b994c2e390f8d33e66ce5836b0bf348 1 parent 1ca6e9b
Jannis Leidel authored August 12, 2011
18  django/contrib/auth/decorators.py
@@ -2,6 +2,7 @@
2 2
 from functools import wraps
3 3
 from django.conf import settings
4 4
 from django.contrib.auth import REDIRECT_FIELD_NAME
  5
+from django.core.exceptions import PermissionDenied
5 6
 from django.utils.decorators import available_attrs
6 7
 
7 8
 
@@ -47,9 +48,20 @@ def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login
47 48
     return actual_decorator
48 49
 
49 50
 
50  
-def permission_required(perm, login_url=None):
  51
+def permission_required(perm, login_url=None, raise_exception=False):
51 52
     """
52 53
     Decorator for views that checks whether a user has a particular permission
53  
-    enabled, redirecting to the log-in page if necessary.
  54
+    enabled, redirecting to the log-in page if neccesary.
  55
+    If the raise_exception parameter is given the PermissionDenied exception
  56
+    is raised.
54 57
     """
55  
-    return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
  58
+    def check_perms(user):
  59
+        # First check if the user has the permission (even anon users)
  60
+        if user.has_perm(perm):
  61
+            return True
  62
+        # In case the 403 handler should be called raise the exception
  63
+        if raise_exception:
  64
+            raise PermissionDenied
  65
+        # As the last resort, show the login form
  66
+        return False
  67
+    return user_passes_test(check_perms, login_url=login_url)
13  docs/topics/auth.txt
@@ -1167,7 +1167,7 @@ checks to make sure the user is logged in and has the permission
1167 1167
             return HttpResponse("You can't vote in this poll.")
1168 1168
         # ...
1169 1169
 
1170  
-.. function:: user_passes_test()
  1170
+.. function:: user_passes_test(func, [login_url=None])
1171 1171
 
1172 1172
     As a shortcut, you can use the convenient ``user_passes_test`` decorator::
1173 1173
 
@@ -1205,7 +1205,7 @@ checks to make sure the user is logged in and has the permission
1205 1205
 The permission_required decorator
1206 1206
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1207 1207
 
1208  
-.. function:: permission_required()
  1208
+.. function:: permission_required([login_url=None, raise_exception=False])
1209 1209
 
1210 1210
     It's a relatively common task to check whether a user has a particular
1211 1211
     permission. For that reason, Django provides a shortcut for that case: the
@@ -1234,6 +1234,13 @@ The permission_required decorator
1234 1234
     As in the :func:`~decorators.login_required` decorator, ``login_url``
1235 1235
     defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>`.
1236 1236
 
  1237
+    .. versionchanged:: 1.4
  1238
+
  1239
+    Added ``raise_exception`` parameter. If given, the decorator will raise
  1240
+    :exc:`~django.core.exceptions.PermissionDenied`, prompting
  1241
+    :ref:`the 403 (HTTP Forbidden) view<http_forbidden_view>` instead of
  1242
+    redirecting to the login page.
  1243
+
1237 1244
 .. currentmodule:: django.contrib.auth
1238 1245
 
1239 1246
 Limiting access to generic views
@@ -1632,6 +1639,8 @@ the ``auth_permission`` table most of the time.
1632 1639
 
1633 1640
 .. _django/contrib/auth/backends.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py
1634 1641
 
  1642
+.. _anonymous_auth:
  1643
+
1635 1644
 Authorization for anonymous users
1636 1645
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1637 1646
 
15  tests/modeltests/test_client/models.py
@@ -370,6 +370,21 @@ def test_view_with_permissions(self):
370 370
 
371 371
         # TODO: Log in with right permissions and request the page again
372 372
 
  373
+    def test_view_with_permissions_exception(self):
  374
+        "Request a page that is protected with @permission_required but raises a exception"
  375
+
  376
+        # Get the page without logging in. Should result in 403.
  377
+        response = self.client.get('/test_client/permission_protected_view_exception/')
  378
+        self.assertEquals(response.status_code, 403)
  379
+
  380
+        # Log in
  381
+        login = self.client.login(username='testclient', password='password')
  382
+        self.assertTrue(login, 'Could not log in')
  383
+
  384
+        # Log in with wrong permissions. Should result in 403.
  385
+        response = self.client.get('/test_client/permission_protected_view_exception/')
  386
+        self.assertEquals(response.status_code, 403)
  387
+
373 388
     def test_view_with_method_permissions(self):
374 389
         "Request a page that is protected with a @permission_required method"
375 390
 
1  tests/modeltests/test_client/urls.py
@@ -21,6 +21,7 @@
21 21
     (r'^login_protected_method_view/$', views.login_protected_method_view),
22 22
     (r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
23 23
     (r'^permission_protected_view/$', views.permission_protected_view),
  24
+    (r'^permission_protected_view_exception/$', views.permission_protected_view_exception),
24 25
     (r'^permission_protected_method_view/$', views.permission_protected_method_view),
25 26
     (r'^session_view/$', views.session_view),
26 27
     (r'^broken_view/$', views.broken_view),
5  tests/modeltests/test_client/views.py
@@ -143,7 +143,7 @@ def login_protected_view_changed_redirect(request):
143 143
     return HttpResponse(t.render(c))
144 144
 login_protected_view_changed_redirect = login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect)
145 145
 
146  
-def permission_protected_view(request):
  146
+def _permission_protected_view(request):
147 147
     "A simple view that is permission protected."
148 148
     t = Template('This is a permission protected test. '
149 149
                  'Username is {{ user.username }}. '
@@ -151,7 +151,8 @@ def permission_protected_view(request):
151 151
                  name='Permissions Template')
152 152
     c = Context({'user': request.user})
153 153
     return HttpResponse(t.render(c))
154  
-permission_protected_view = permission_required('modeltests.test_perm')(permission_protected_view)
  154
+permission_protected_view = permission_required('modeltests.test_perm')(_permission_protected_view)
  155
+permission_protected_view_exception = permission_required('modeltests.test_perm', raise_exception=True)(_permission_protected_view)
155 156
 
156 157
 class _ViewManager(object):
157 158
     @method_decorator(login_required)

0 notes on commit 351d5da

Please sign in to comment.
Something went wrong with that request. Please try again.