mirror of
https://github.com/django/django.git
synced 2024-11-18 15:34:16 +00:00
e5cb4e1411
Added the mixins LoginRequiredMixin, PermissionRequiredMixin and UserPassesTestMixin to contrib.auth as counterparts to the respective view decorators. The authentication mixins UserPassesTestMixin, LoginRequiredMixin and PermissionRequiredMixin have been inspired by django-braces <https://github.com/brack3t/django-braces/> Thanks Raphael Michel for the initial patch, tests and docs on the PR and Ana Balica, Kenneth Love, Marc Tamlyn, and Tim Graham for the review.
252 lines
8.1 KiB
Python
252 lines
8.1 KiB
Python
from django.contrib.auth import models
|
|
from django.contrib.auth.mixins import (
|
|
LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin,
|
|
)
|
|
from django.contrib.auth.models import AnonymousUser
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import HttpResponse
|
|
from django.test import RequestFactory, TestCase
|
|
from django.views.generic import View
|
|
|
|
|
|
class AlwaysTrueMixin(UserPassesTestMixin):
|
|
|
|
def test_func(self):
|
|
return True
|
|
|
|
|
|
class AlwaysFalseMixin(UserPassesTestMixin):
|
|
|
|
def test_func(self):
|
|
return False
|
|
|
|
|
|
class EmptyResponseView(View):
|
|
def get(self, request, *args, **kwargs):
|
|
return HttpResponse()
|
|
|
|
|
|
class AlwaysTrueView(AlwaysTrueMixin, EmptyResponseView):
|
|
pass
|
|
|
|
|
|
class AlwaysFalseView(AlwaysFalseMixin, EmptyResponseView):
|
|
pass
|
|
|
|
|
|
class StackedMixinsView1(LoginRequiredMixin, PermissionRequiredMixin, EmptyResponseView):
|
|
permission_required = ['auth.add_customuser', 'auth.change_customuser']
|
|
raise_exception = True
|
|
|
|
|
|
class StackedMixinsView2(PermissionRequiredMixin, LoginRequiredMixin, EmptyResponseView):
|
|
permission_required = ['auth.add_customuser', 'auth.change_customuser']
|
|
raise_exception = True
|
|
|
|
|
|
class AccessMixinTests(TestCase):
|
|
|
|
factory = RequestFactory()
|
|
|
|
def test_stacked_mixins_success(self):
|
|
user = models.User.objects.create(username='joe', password='qwerty')
|
|
perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser'))
|
|
user.user_permissions.add(*perms)
|
|
request = self.factory.get('/rand')
|
|
request.user = user
|
|
|
|
view = StackedMixinsView1.as_view()
|
|
response = view(request)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
view = StackedMixinsView2.as_view()
|
|
response = view(request)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_stacked_mixins_missing_permission(self):
|
|
user = models.User.objects.create(username='joe', password='qwerty')
|
|
perms = models.Permission.objects.filter(codename__in=('add_customuser',))
|
|
user.user_permissions.add(*perms)
|
|
request = self.factory.get('/rand')
|
|
request.user = user
|
|
|
|
view = StackedMixinsView1.as_view()
|
|
with self.assertRaises(PermissionDenied):
|
|
view(request)
|
|
|
|
view = StackedMixinsView2.as_view()
|
|
with self.assertRaises(PermissionDenied):
|
|
view(request)
|
|
|
|
def test_stacked_mixins_not_logged_in(self):
|
|
user = models.User.objects.create(username='joe', password='qwerty')
|
|
user.is_authenticated = lambda: False
|
|
perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser'))
|
|
user.user_permissions.add(*perms)
|
|
request = self.factory.get('/rand')
|
|
request.user = user
|
|
|
|
view = StackedMixinsView1.as_view()
|
|
with self.assertRaises(PermissionDenied):
|
|
view(request)
|
|
|
|
view = StackedMixinsView2.as_view()
|
|
with self.assertRaises(PermissionDenied):
|
|
view(request)
|
|
|
|
|
|
class UserPassesTestTests(TestCase):
|
|
|
|
factory = RequestFactory()
|
|
|
|
def _test_redirect(self, view=None, url='/accounts/login/?next=/rand'):
|
|
if not view:
|
|
view = AlwaysFalseView.as_view()
|
|
request = self.factory.get('/rand')
|
|
request.user = AnonymousUser()
|
|
response = view(request)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(response.url, url)
|
|
|
|
def test_default(self):
|
|
self._test_redirect()
|
|
|
|
def test_custom_redirect_url(self):
|
|
class AView(AlwaysFalseView):
|
|
login_url = '/login/'
|
|
|
|
self._test_redirect(AView.as_view(), '/login/?next=/rand')
|
|
|
|
def test_custom_redirect_parameter(self):
|
|
class AView(AlwaysFalseView):
|
|
redirect_field_name = 'goto'
|
|
|
|
self._test_redirect(AView.as_view(), '/accounts/login/?goto=/rand')
|
|
|
|
def test_no_redirect_parameter(self):
|
|
class AView(AlwaysFalseView):
|
|
redirect_field_name = None
|
|
|
|
self._test_redirect(AView.as_view(), '/accounts/login/')
|
|
|
|
def test_raise_exception(self):
|
|
class AView(AlwaysFalseView):
|
|
raise_exception = True
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = AnonymousUser()
|
|
self.assertRaises(PermissionDenied, AView.as_view(), request)
|
|
|
|
def test_raise_exception_custom_message(self):
|
|
msg = "You don't have access here"
|
|
|
|
class AView(AlwaysFalseView):
|
|
raise_exception = True
|
|
permission_denied_message = msg
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = AnonymousUser()
|
|
view = AView.as_view()
|
|
with self.assertRaises(PermissionDenied) as cm:
|
|
view(request)
|
|
self.assertEqual(cm.exception.args[0], msg)
|
|
|
|
def test_raise_exception_custom_message_function(self):
|
|
msg = "You don't have access here"
|
|
|
|
class AView(AlwaysFalseView):
|
|
raise_exception = True
|
|
|
|
def get_permission_denied_message(self):
|
|
return msg
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = AnonymousUser()
|
|
view = AView.as_view()
|
|
with self.assertRaises(PermissionDenied) as cm:
|
|
view(request)
|
|
self.assertEqual(cm.exception.args[0], msg)
|
|
|
|
def test_user_passes(self):
|
|
view = AlwaysTrueView.as_view()
|
|
request = self.factory.get('/rand')
|
|
request.user = AnonymousUser()
|
|
response = view(request)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
class LoginRequiredMixinTests(TestCase):
|
|
|
|
factory = RequestFactory()
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user = models.User.objects.create(username='joe', password='qwerty')
|
|
|
|
def test_login_required(self):
|
|
"""
|
|
Check that login_required works on a simple view wrapped in a
|
|
login_required decorator.
|
|
"""
|
|
class AView(LoginRequiredMixin, EmptyResponseView):
|
|
pass
|
|
|
|
view = AView.as_view()
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = AnonymousUser()
|
|
response = view(request)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual('/accounts/login/?next=/rand', response.url)
|
|
request = self.factory.get('/rand')
|
|
request.user = self.user
|
|
response = view(request)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
class PermissionsRequiredMixinTests(TestCase):
|
|
|
|
factory = RequestFactory()
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user = models.User.objects.create(username='joe', password='qwerty')
|
|
perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser'))
|
|
cls.user.user_permissions.add(*perms)
|
|
|
|
def test_many_permissions_pass(self):
|
|
class AView(PermissionRequiredMixin, EmptyResponseView):
|
|
permission_required = ['auth.add_customuser', 'auth.change_customuser']
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = self.user
|
|
resp = AView.as_view()(request)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
def test_single_permission_pass(self):
|
|
class AView(PermissionRequiredMixin, EmptyResponseView):
|
|
permission_required = 'auth.add_customuser'
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = self.user
|
|
resp = AView.as_view()(request)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
def test_permissioned_denied_redirect(self):
|
|
class AView(PermissionRequiredMixin, EmptyResponseView):
|
|
permission_required = ['auth.add_customuser', 'auth.change_customuser', 'non-existent-permission']
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = self.user
|
|
resp = AView.as_view()(request)
|
|
self.assertEqual(resp.status_code, 302)
|
|
|
|
def test_permissioned_denied_exception_raised(self):
|
|
class AView(PermissionRequiredMixin, EmptyResponseView):
|
|
permission_required = ['auth.add_customuser', 'auth.change_customuser', 'non-existent-permission']
|
|
raise_exception = True
|
|
|
|
request = self.factory.get('/rand')
|
|
request.user = self.user
|
|
self.assertRaises(PermissionDenied, AView.as_view(), request)
|