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

This commit is contained in:
Dylan Verheul 2017-07-12 12:07:06 +02:00 committed by Tim Graham
parent e307ff29d2
commit 9b1125bfc7
3 changed files with 34 additions and 5 deletions

View File

@ -39,7 +39,7 @@ class AccessMixin:
return self.redirect_field_name return self.redirect_field_name
def handle_no_permission(self): 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()) 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()) return redirect_to_login(self.request.get_full_path(), self.get_login_url(), self.get_redirect_field_name())

View File

@ -757,8 +757,17 @@ Redirecting unauthorized requests in class-based views
------------------------------------------------------ ------------------------------------------------------
To ease the handling of access restrictions in :doc:`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 </ref/class-based-views/index>`, the ``AccessMixin`` can be used to configure
user to the login page or issue an HTTP 403 Forbidden response. 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 .. class:: AccessMixin
@ -781,8 +790,9 @@ user to the login page or issue an HTTP 403 Forbidden response.
.. attribute:: raise_exception .. attribute:: raise_exception
If this attribute is set to ``True``, a If this attribute is set to ``True``, a
:class:`~django.core.exceptions.PermissionDenied` exception will be :class:`~django.core.exceptions.PermissionDenied` exception is raised
raised instead of the redirect. Defaults to ``False``. when the conditions are not met. When ``False`` (the default),
anonymous users are redirected to the login page.
.. method:: get_login_url() .. method:: get_login_url()

View File

@ -80,6 +80,20 @@ class AccessMixinTests(TestCase):
with self.assertRaises(PermissionDenied): with self.assertRaises(PermissionDenied):
view(request) 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) @mock.patch.object(models.User, 'is_authenticated', False)
def test_stacked_mixins_not_logged_in(self): def test_stacked_mixins_not_logged_in(self):
user = models.User.objects.create(username='joe', password='qwerty') 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', 'auth_tests.add_customuser', 'auth_tests.change_customuser', 'nonexistent-permission',
] ]
# Authenticated users receive PermissionDenied.
request = self.factory.get('/rand') request = self.factory.get('/rand')
request.user = self.user 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) resp = AView.as_view()(request)
self.assertEqual(resp.status_code, 302) self.assertEqual(resp.status_code, 302)