Browse Source

Fixed #28379 -- Made AccessMixin raise Permissiondenied for authenticated users.

Dylan Verheul 7 years ago
parent
commit
9b1125bfc7
3 changed files with 34 additions and 5 deletions
  1. 1 1
      django/contrib/auth/mixins.py
  2. 14 4
      docs/topics/auth/default.txt
  3. 19 0
      tests/auth_tests/test_mixins.py

+ 1 - 1
django/contrib/auth/mixins.py

@@ -39,7 +39,7 @@ class AccessMixin:
         return self.redirect_field_name
 
     def handle_no_permission(self):
-        if self.raise_exception:
+        if self.raise_exception or self.request.user.is_authenticated:
             raise PermissionDenied(self.get_permission_denied_message())
         return redirect_to_login(self.request.get_full_path(), self.get_login_url(), self.get_redirect_field_name())
 

+ 14 - 4
docs/topics/auth/default.txt

@@ -757,8 +757,17 @@ Redirecting unauthorized requests in class-based views
 ------------------------------------------------------
 
 To ease the handling of access restrictions in :doc:`class-based views
-</ref/class-based-views/index>`, the ``AccessMixin`` can be used to redirect a
-user to the login page or issue an HTTP 403 Forbidden response.
+</ref/class-based-views/index>`, the ``AccessMixin`` can be used to configure
+the behavior of a view when access is denied. Authenticated users are denied
+access with an HTTP 403 Forbidden response. Anonymous users are redirected to
+the login page or shown an HTTP 403 Forbidden response, depending on the
+:attr:`~django.contrib.auth.mixins.AccessMixin.raise_exception` attribute.
+
+.. versionchanged:: 2.1
+
+    In older versions, authenticated users who lacked permissions were
+    redirected to the login page (which resulted in a loop) instead of
+    receiving an HTTP 403 Forbidden response.
 
 .. class:: AccessMixin
 
@@ -781,8 +790,9 @@ user to the login page or issue an HTTP 403 Forbidden response.
     .. attribute:: raise_exception
 
         If this attribute is set to ``True``, a
-        :class:`~django.core.exceptions.PermissionDenied` exception will be
-        raised instead of the redirect. Defaults to ``False``.
+        :class:`~django.core.exceptions.PermissionDenied` exception is raised
+        when the conditions are not met.  When ``False`` (the default),
+        anonymous users are redirected to the login page.
 
     .. method:: get_login_url()
 

+ 19 - 0
tests/auth_tests/test_mixins.py

@@ -80,6 +80,20 @@ class AccessMixinTests(TestCase):
         with self.assertRaises(PermissionDenied):
             view(request)
 
+    def test_access_mixin_permission_denied_response(self):
+        user = models.User.objects.create(username='joe', password='qwerty')
+        # Authenticated users receive PermissionDenied.
+        request = self.factory.get('/rand')
+        request.user = user
+        view = AlwaysFalseView.as_view()
+        with self.assertRaises(PermissionDenied):
+            view(request)
+        # Anonymous users are redirected to the login page.
+        request.user = AnonymousUser()
+        response = view(request)
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(response.url, '/accounts/login/?next=/rand')
+
     @mock.patch.object(models.User, 'is_authenticated', False)
     def test_stacked_mixins_not_logged_in(self):
         user = models.User.objects.create(username='joe', password='qwerty')
@@ -241,8 +255,13 @@ class PermissionsRequiredMixinTests(TestCase):
                 'auth_tests.add_customuser', 'auth_tests.change_customuser', 'nonexistent-permission',
             ]
 
+        # Authenticated users receive PermissionDenied.
         request = self.factory.get('/rand')
         request.user = self.user
+        with self.assertRaises(PermissionDenied):
+            AView.as_view()(request)
+        # Anonymous users are redirected to the login page.
+        request.user = AnonymousUser()
         resp = AView.as_view()(request)
         self.assertEqual(resp.status_code, 302)