from unittest import mock 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, SimpleTestCase, 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_tests.add_customuser", "auth_tests.change_customuser"] raise_exception = True class StackedMixinsView2( PermissionRequiredMixin, LoginRequiredMixin, EmptyResponseView ): permission_required = ["auth_tests.add_customuser", "auth_tests.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_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") def test_access_mixin_permission_denied_remote_login_url(self): class AView(AlwaysFalseView): login_url = "https://www.remote.example.com/login" view = AView.as_view() request = self.factory.get("/rand") request.user = AnonymousUser() response = view(request) self.assertEqual(response.status_code, 302) self.assertEqual( response.url, "https://www.remote.example.com/login?next=http%3A//testserver/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") 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(SimpleTestCase): 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() with 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.assertRaisesMessage(PermissionDenied, msg): view(request) 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.assertRaisesMessage(PermissionDenied, msg): view(request) 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): """ 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_tests.add_customuser", "auth_tests.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_tests.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_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) def test_permissioned_denied_exception_raised(self): class AView(PermissionRequiredMixin, EmptyResponseView): permission_required = [ "auth_tests.add_customuser", "auth_tests.change_customuser", "nonexistent-permission", ] raise_exception = True request = self.factory.get("/rand") request.user = self.user with self.assertRaises(PermissionDenied): AView.as_view()(request)