Author: jezdez
Date: 2011-08-12 07:15:41 -0700 (Fri, 12 Aug 2011)
New Revision: 16607

Modified:
   django/trunk/django/contrib/auth/decorators.py
   django/trunk/docs/topics/auth.txt
   django/trunk/tests/modeltests/test_client/models.py
   django/trunk/tests/modeltests/test_client/urls.py
   django/trunk/tests/modeltests/test_client/views.py
Log:
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.

Modified: django/trunk/django/contrib/auth/decorators.py
===================================================================
--- django/trunk/django/contrib/auth/decorators.py      2011-08-12 14:15:31 UTC 
(rev 16606)
+++ django/trunk/django/contrib/auth/decorators.py      2011-08-12 14:15:41 UTC 
(rev 16607)
@@ -2,6 +2,7 @@
 from functools import wraps
 from django.conf import settings
 from django.contrib.auth import REDIRECT_FIELD_NAME
+from django.core.exceptions import PermissionDenied
 from django.utils.decorators import available_attrs
 
 
@@ -47,9 +48,20 @@
     return actual_decorator
 
 
-def permission_required(perm, login_url=None):
+def permission_required(perm, login_url=None, raise_exception=False):
     """
     Decorator for views that checks whether a user has a particular permission
-    enabled, redirecting to the log-in page if necessary.
+    enabled, redirecting to the log-in page if neccesary.
+    If the raise_exception parameter is given the PermissionDenied exception
+    is raised.
     """
-    return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
+    def check_perms(user):
+        # First check if the user has the permission (even anon users)
+        if user.has_perm(perm):
+            return True
+        # In case the 403 handler should be called raise the exception
+        if raise_exception:
+            raise PermissionDenied
+        # As the last resort, show the login form
+        return False
+    return user_passes_test(check_perms, login_url=login_url)

Modified: django/trunk/docs/topics/auth.txt
===================================================================
--- django/trunk/docs/topics/auth.txt   2011-08-12 14:15:31 UTC (rev 16606)
+++ django/trunk/docs/topics/auth.txt   2011-08-12 14:15:41 UTC (rev 16607)
@@ -1167,7 +1167,7 @@
             return HttpResponse("You can't vote in this poll.")
         # ...
 
-.. function:: user_passes_test()
+.. function:: user_passes_test(func, [login_url=None])
 
     As a shortcut, you can use the convenient ``user_passes_test`` decorator::
 
@@ -1205,7 +1205,7 @@
 The permission_required decorator
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. function:: permission_required()
+.. function:: permission_required([login_url=None, raise_exception=False])
 
     It's a relatively common task to check whether a user has a particular
     permission. For that reason, Django provides a shortcut for that case: the
@@ -1234,6 +1234,13 @@
     As in the :func:`~decorators.login_required` decorator, ``login_url``
     defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>`.
 
+    .. versionchanged:: 1.4
+
+    Added ``raise_exception`` parameter. If given, the decorator will raise
+    :exc:`~django.core.exceptions.PermissionDenied`, prompting
+    :ref:`the 403 (HTTP Forbidden) view<http_forbidden_view>` instead of
+    redirecting to the login page.
+
 .. currentmodule:: django.contrib.auth
 
 Limiting access to generic views
@@ -1632,6 +1639,8 @@
 
 .. _django/contrib/auth/backends.py: 
http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py
 
+.. _anonymous_auth:
+
 Authorization for anonymous users
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

Modified: django/trunk/tests/modeltests/test_client/models.py
===================================================================
--- django/trunk/tests/modeltests/test_client/models.py 2011-08-12 14:15:31 UTC 
(rev 16606)
+++ django/trunk/tests/modeltests/test_client/models.py 2011-08-12 14:15:41 UTC 
(rev 16607)
@@ -370,6 +370,21 @@
 
         # TODO: Log in with right permissions and request the page again
 
+    def test_view_with_permissions_exception(self):
+        "Request a page that is protected with @permission_required but raises 
a exception"
+
+        # Get the page without logging in. Should result in 403.
+        response = 
self.client.get('/test_client/permission_protected_view_exception/')
+        self.assertEquals(response.status_code, 403)
+
+        # Log in
+        login = self.client.login(username='testclient', password='password')
+        self.assertTrue(login, 'Could not log in')
+
+        # Log in with wrong permissions. Should result in 403.
+        response = 
self.client.get('/test_client/permission_protected_view_exception/')
+        self.assertEquals(response.status_code, 403)
+
     def test_view_with_method_permissions(self):
         "Request a page that is protected with a @permission_required method"
 

Modified: django/trunk/tests/modeltests/test_client/urls.py
===================================================================
--- django/trunk/tests/modeltests/test_client/urls.py   2011-08-12 14:15:31 UTC 
(rev 16606)
+++ django/trunk/tests/modeltests/test_client/urls.py   2011-08-12 14:15:41 UTC 
(rev 16607)
@@ -21,6 +21,7 @@
     (r'^login_protected_method_view/$', views.login_protected_method_view),
     (r'^login_protected_view_custom_redirect/$', 
views.login_protected_view_changed_redirect),
     (r'^permission_protected_view/$', views.permission_protected_view),
+    (r'^permission_protected_view_exception/$', 
views.permission_protected_view_exception),
     (r'^permission_protected_method_view/$', 
views.permission_protected_method_view),
     (r'^session_view/$', views.session_view),
     (r'^broken_view/$', views.broken_view),

Modified: django/trunk/tests/modeltests/test_client/views.py
===================================================================
--- django/trunk/tests/modeltests/test_client/views.py  2011-08-12 14:15:31 UTC 
(rev 16606)
+++ django/trunk/tests/modeltests/test_client/views.py  2011-08-12 14:15:41 UTC 
(rev 16607)
@@ -143,7 +143,7 @@
     return HttpResponse(t.render(c))
 login_protected_view_changed_redirect = 
login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect)
 
-def permission_protected_view(request):
+def _permission_protected_view(request):
     "A simple view that is permission protected."
     t = Template('This is a permission protected test. '
                  'Username is {{ user.username }}. '
@@ -151,7 +151,8 @@
                  name='Permissions Template')
     c = Context({'user': request.user})
     return HttpResponse(t.render(c))
-permission_protected_view = 
permission_required('modeltests.test_perm')(permission_protected_view)
+permission_protected_view = 
permission_required('modeltests.test_perm')(_permission_protected_view)
+permission_protected_view_exception = 
permission_required('modeltests.test_perm', 
raise_exception=True)(_permission_protected_view)
 
 class _ViewManager(object):
     @method_decorator(login_required)

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to