|
@@ -2,6 +2,7 @@ from unittest import mock
|
|
|
|
|
|
from django.conf.global_settings import PASSWORD_HASHERS
|
|
|
from django.contrib.auth import get_user_model
|
|
|
+from django.contrib.auth.backends import ModelBackend
|
|
|
from django.contrib.auth.base_user import AbstractBaseUser
|
|
|
from django.contrib.auth.hashers import get_hasher
|
|
|
from django.contrib.auth.models import (
|
|
@@ -261,6 +262,142 @@ class AbstractUserTestCase(TestCase):
|
|
|
hasher.iterations = old_iterations
|
|
|
|
|
|
|
|
|
+class CustomModelBackend(ModelBackend):
|
|
|
+ def with_perm(self, perm, is_active=True, include_superusers=True, backend=None, obj=None):
|
|
|
+ if obj is not None and obj.username == 'charliebrown':
|
|
|
+ return User.objects.filter(pk=obj.pk)
|
|
|
+ return User.objects.filter(username__startswith='charlie')
|
|
|
+
|
|
|
+
|
|
|
+class UserWithPermTestCase(TestCase):
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def setUpTestData(cls):
|
|
|
+ content_type = ContentType.objects.get_for_model(Group)
|
|
|
+ cls.permission = Permission.objects.create(
|
|
|
+ name='test', content_type=content_type, codename='test',
|
|
|
+ )
|
|
|
+
|
|
|
+ cls.user1 = User.objects.create_user('user 1', 'foo@example.com')
|
|
|
+ cls.user1.user_permissions.add(cls.permission)
|
|
|
+
|
|
|
+ group1 = Group.objects.create(name='group 1')
|
|
|
+ group1.permissions.add(cls.permission)
|
|
|
+ group2 = Group.objects.create(name='group 2')
|
|
|
+ group2.permissions.add(cls.permission)
|
|
|
+ cls.user2 = User.objects.create_user('user 2', 'bar@example.com')
|
|
|
+ cls.user2.groups.add(group1, group2)
|
|
|
+
|
|
|
+ cls.user_charlie = User.objects.create_user('charlie', 'charlie@example.com')
|
|
|
+ cls.user_charlie_b = User.objects.create_user('charliebrown', 'charlie@brown.com')
|
|
|
+
|
|
|
+ cls.superuser = User.objects.create_superuser(
|
|
|
+ 'superuser', 'superuser@example.com', 'superpassword',
|
|
|
+ )
|
|
|
+
|
|
|
+ cls.inactive_user = User.objects.create_user(
|
|
|
+ 'inactive_user', 'baz@example.com', is_active=False,
|
|
|
+ )
|
|
|
+ cls.inactive_user.user_permissions.add(cls.permission)
|
|
|
+
|
|
|
+ def test_invalid_permission_name(self):
|
|
|
+ msg = 'Permission name should be in the form app_label.permission_codename.'
|
|
|
+ for perm in ('nodots', 'too.many.dots', '...', ''):
|
|
|
+ with self.subTest(perm), self.assertRaisesMessage(ValueError, msg):
|
|
|
+ User.objects.with_perm(perm)
|
|
|
+
|
|
|
+ def test_invalid_permission_type(self):
|
|
|
+ msg = 'The `perm` argument must be a string or a permission instance.'
|
|
|
+ for perm in (b'auth.test', object(), None):
|
|
|
+ with self.subTest(perm), self.assertRaisesMessage(TypeError, msg):
|
|
|
+ User.objects.with_perm(perm)
|
|
|
+
|
|
|
+ def test_invalid_backend_type(self):
|
|
|
+ msg = 'backend must be a dotted import path string (got %r).'
|
|
|
+ for backend in (b'auth_tests.CustomModelBackend', object()):
|
|
|
+ with self.subTest(backend):
|
|
|
+ with self.assertRaisesMessage(TypeError, msg % backend):
|
|
|
+ User.objects.with_perm('auth.test', backend=backend)
|
|
|
+
|
|
|
+ def test_basic(self):
|
|
|
+ active_users = [self.user1, self.user2]
|
|
|
+ tests = [
|
|
|
+ ({}, [*active_users, self.superuser]),
|
|
|
+ ({'obj': self.user1}, []),
|
|
|
+
|
|
|
+ ({'is_active': False}, [self.inactive_user]),
|
|
|
+
|
|
|
+ ({'is_active': None}, [*active_users, self.superuser, self.inactive_user]),
|
|
|
+
|
|
|
+ ({'include_superusers': False}, active_users),
|
|
|
+ (
|
|
|
+ {'include_superusers': False, 'is_active': False},
|
|
|
+ [self.inactive_user],
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {'include_superusers': False, 'is_active': None},
|
|
|
+ [*active_users, self.inactive_user],
|
|
|
+ ),
|
|
|
+ ]
|
|
|
+ for kwargs, expected_users in tests:
|
|
|
+ for perm in ('auth.test', self.permission):
|
|
|
+ with self.subTest(perm=perm, **kwargs):
|
|
|
+ self.assertCountEqual(
|
|
|
+ User.objects.with_perm(perm, **kwargs),
|
|
|
+ expected_users,
|
|
|
+ )
|
|
|
+
|
|
|
+ @override_settings(AUTHENTICATION_BACKENDS=['django.contrib.auth.backends.BaseBackend'])
|
|
|
+ def test_backend_without_with_perm(self):
|
|
|
+ self.assertSequenceEqual(User.objects.with_perm('auth.test'), [])
|
|
|
+
|
|
|
+ def test_nonexistent_permission(self):
|
|
|
+ self.assertSequenceEqual(User.objects.with_perm('auth.perm'), [self.superuser])
|
|
|
+
|
|
|
+ def test_nonexistent_backend(self):
|
|
|
+ with self.assertRaises(ImportError):
|
|
|
+ User.objects.with_perm(
|
|
|
+ 'auth.test',
|
|
|
+ backend='invalid.backend.CustomModelBackend',
|
|
|
+ )
|
|
|
+
|
|
|
+ @override_settings(AUTHENTICATION_BACKENDS=['auth_tests.test_models.CustomModelBackend'])
|
|
|
+ def test_custom_backend(self):
|
|
|
+ for perm in ('auth.test', self.permission):
|
|
|
+ with self.subTest(perm):
|
|
|
+ self.assertCountEqual(
|
|
|
+ User.objects.with_perm(perm),
|
|
|
+ [self.user_charlie, self.user_charlie_b],
|
|
|
+ )
|
|
|
+
|
|
|
+ @override_settings(AUTHENTICATION_BACKENDS=['auth_tests.test_models.CustomModelBackend'])
|
|
|
+ def test_custom_backend_pass_obj(self):
|
|
|
+ for perm in ('auth.test', self.permission):
|
|
|
+ with self.subTest(perm):
|
|
|
+ self.assertSequenceEqual(
|
|
|
+ User.objects.with_perm(perm, obj=self.user_charlie_b),
|
|
|
+ [self.user_charlie_b],
|
|
|
+ )
|
|
|
+
|
|
|
+ @override_settings(AUTHENTICATION_BACKENDS=[
|
|
|
+ 'auth_tests.test_models.CustomModelBackend',
|
|
|
+ 'django.contrib.auth.backends.ModelBackend',
|
|
|
+ ])
|
|
|
+ def test_multiple_backends(self):
|
|
|
+ msg = (
|
|
|
+ 'You have multiple authentication backends configured and '
|
|
|
+ 'therefore must provide the `backend` argument.'
|
|
|
+ )
|
|
|
+ with self.assertRaisesMessage(ValueError, msg):
|
|
|
+ User.objects.with_perm('auth.test')
|
|
|
+
|
|
|
+ backend = 'auth_tests.test_models.CustomModelBackend'
|
|
|
+ self.assertCountEqual(
|
|
|
+ User.objects.with_perm('auth.test', backend=backend),
|
|
|
+ [self.user_charlie, self.user_charlie_b],
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
class IsActiveTestCase(TestCase):
|
|
|
"""
|
|
|
Tests the behavior of the guaranteed is_active attribute
|