summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMarkus Holtermann <info@markusholtermann.eu>2015-06-11 18:08:48 +0200
committerMarkus Holtermann <info@markusholtermann.eu>2015-06-17 23:19:10 +0200
commite5cb4e14118f3a508e3bc00ee7cd50bb0f18a61d (patch)
treecc079646b4db9f528366c59d67768584387531cd /tests
parent2f615b10e6330d27dccbd770a4628200044acf70 (diff)
Fixed #24914 -- Added authentication mixins for CBVs
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.
Diffstat (limited to 'tests')
-rw-r--r--tests/auth_tests/test_mixins.py251
1 files changed, 251 insertions, 0 deletions
diff --git a/tests/auth_tests/test_mixins.py b/tests/auth_tests/test_mixins.py
new file mode 100644
index 0000000000..c04715dc8f
--- /dev/null
+++ b/tests/auth_tests/test_mixins.py
@@ -0,0 +1,251 @@
+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)