mirror of
https://github.com/django/django.git
synced 2024-12-23 17:46:27 +00:00
4eb756793b
Co-authored-by: Collin Anderson <collin@onetencommunications.com>
850 lines
32 KiB
Python
850 lines
32 KiB
Python
import sys
|
|
from datetime import date
|
|
from unittest import mock
|
|
|
|
from django.contrib.auth import (
|
|
BACKEND_SESSION_KEY, SESSION_KEY, _clean_credentials, authenticate,
|
|
get_user, signals,
|
|
)
|
|
from django.contrib.auth.backends import BaseBackend, ModelBackend
|
|
from django.contrib.auth.hashers import MD5PasswordHasher
|
|
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
|
from django.http import HttpRequest
|
|
from django.test import (
|
|
RequestFactory, SimpleTestCase, TestCase, modify_settings,
|
|
override_settings,
|
|
)
|
|
from django.views.debug import technical_500_response
|
|
from django.views.decorators.debug import sensitive_variables
|
|
|
|
from .models import (
|
|
CustomPermissionsUser, CustomUser, CustomUserWithoutIsActiveField,
|
|
ExtensionUser, UUIDUser,
|
|
)
|
|
|
|
|
|
class SimpleBackend(BaseBackend):
|
|
def get_user_permissions(self, user_obj, obj=None):
|
|
return ['user_perm']
|
|
|
|
def get_group_permissions(self, user_obj, obj=None):
|
|
return ['group_perm']
|
|
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=['auth_tests.test_auth_backends.SimpleBackend'])
|
|
class BaseBackendTest(TestCase):
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user = User.objects.create_user('test', 'test@example.com', 'test')
|
|
|
|
def test_get_user_permissions(self):
|
|
self.assertEqual(self.user.get_user_permissions(), {'user_perm'})
|
|
|
|
def test_get_group_permissions(self):
|
|
self.assertEqual(self.user.get_group_permissions(), {'group_perm'})
|
|
|
|
def test_get_all_permissions(self):
|
|
self.assertEqual(self.user.get_all_permissions(), {'user_perm', 'group_perm'})
|
|
|
|
def test_has_perm(self):
|
|
self.assertIs(self.user.has_perm('user_perm'), True)
|
|
self.assertIs(self.user.has_perm('group_perm'), True)
|
|
self.assertIs(self.user.has_perm('other_perm', TestObj()), False)
|
|
|
|
|
|
class CountingMD5PasswordHasher(MD5PasswordHasher):
|
|
"""Hasher that counts how many times it computes a hash."""
|
|
|
|
calls = 0
|
|
|
|
def encode(self, *args, **kwargs):
|
|
type(self).calls += 1
|
|
return super().encode(*args, **kwargs)
|
|
|
|
|
|
class BaseModelBackendTest:
|
|
"""
|
|
A base class for tests that need to validate the ModelBackend
|
|
with different User models. Subclasses should define a class
|
|
level UserModel attribute, and a create_users() method to
|
|
construct two users for test purposes.
|
|
"""
|
|
backend = 'django.contrib.auth.backends.ModelBackend'
|
|
|
|
def setUp(self):
|
|
self.patched_settings = modify_settings(
|
|
AUTHENTICATION_BACKENDS={'append': self.backend},
|
|
)
|
|
self.patched_settings.enable()
|
|
self.create_users()
|
|
|
|
def tearDown(self):
|
|
self.patched_settings.disable()
|
|
# The custom_perms test messes with ContentTypes, which will
|
|
# be cached; flush the cache to ensure there are no side effects
|
|
# Refs #14975, #14925
|
|
ContentType.objects.clear_cache()
|
|
|
|
def test_has_perm(self):
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
self.assertIs(user.has_perm('auth.test'), False)
|
|
|
|
user.is_staff = True
|
|
user.save()
|
|
self.assertIs(user.has_perm('auth.test'), False)
|
|
|
|
user.is_superuser = True
|
|
user.save()
|
|
self.assertIs(user.has_perm('auth.test'), True)
|
|
|
|
user.is_staff = True
|
|
user.is_superuser = True
|
|
user.is_active = False
|
|
user.save()
|
|
self.assertIs(user.has_perm('auth.test'), False)
|
|
|
|
def test_custom_perms(self):
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
content_type = ContentType.objects.get_for_model(Group)
|
|
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
|
user.user_permissions.add(perm)
|
|
|
|
# reloading user to purge the _perm_cache
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
self.assertEqual(user.get_all_permissions(), {'auth.test'})
|
|
self.assertEqual(user.get_user_permissions(), {'auth.test'})
|
|
self.assertEqual(user.get_group_permissions(), set())
|
|
self.assertIs(user.has_module_perms('Group'), False)
|
|
self.assertIs(user.has_module_perms('auth'), True)
|
|
|
|
perm = Permission.objects.create(name='test2', content_type=content_type, codename='test2')
|
|
user.user_permissions.add(perm)
|
|
perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3')
|
|
user.user_permissions.add(perm)
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
expected_user_perms = {'auth.test2', 'auth.test', 'auth.test3'}
|
|
self.assertEqual(user.get_all_permissions(), expected_user_perms)
|
|
self.assertIs(user.has_perm('test'), False)
|
|
self.assertIs(user.has_perm('auth.test'), True)
|
|
self.assertIs(user.has_perms(['auth.test2', 'auth.test3']), True)
|
|
|
|
perm = Permission.objects.create(name='test_group', content_type=content_type, codename='test_group')
|
|
group = Group.objects.create(name='test_group')
|
|
group.permissions.add(perm)
|
|
user.groups.add(group)
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
self.assertEqual(user.get_all_permissions(), {*expected_user_perms, 'auth.test_group'})
|
|
self.assertEqual(user.get_user_permissions(), expected_user_perms)
|
|
self.assertEqual(user.get_group_permissions(), {'auth.test_group'})
|
|
self.assertIs(user.has_perms(['auth.test3', 'auth.test_group']), True)
|
|
|
|
user = AnonymousUser()
|
|
self.assertIs(user.has_perm('test'), False)
|
|
self.assertIs(user.has_perms(['auth.test2', 'auth.test3']), False)
|
|
|
|
def test_has_no_object_perm(self):
|
|
"""Regressiontest for #12462"""
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
content_type = ContentType.objects.get_for_model(Group)
|
|
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
|
user.user_permissions.add(perm)
|
|
|
|
self.assertIs(user.has_perm('auth.test', 'object'), False)
|
|
self.assertEqual(user.get_all_permissions('object'), set())
|
|
self.assertIs(user.has_perm('auth.test'), True)
|
|
self.assertEqual(user.get_all_permissions(), {'auth.test'})
|
|
|
|
def test_anonymous_has_no_permissions(self):
|
|
"""
|
|
#17903 -- Anonymous users shouldn't have permissions in
|
|
ModelBackend.get_(all|user|group)_permissions().
|
|
"""
|
|
backend = ModelBackend()
|
|
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
content_type = ContentType.objects.get_for_model(Group)
|
|
user_perm = Permission.objects.create(name='test', content_type=content_type, codename='test_user')
|
|
group_perm = Permission.objects.create(name='test2', content_type=content_type, codename='test_group')
|
|
user.user_permissions.add(user_perm)
|
|
|
|
group = Group.objects.create(name='test_group')
|
|
user.groups.add(group)
|
|
group.permissions.add(group_perm)
|
|
|
|
self.assertEqual(backend.get_all_permissions(user), {'auth.test_user', 'auth.test_group'})
|
|
self.assertEqual(backend.get_user_permissions(user), {'auth.test_user'})
|
|
self.assertEqual(backend.get_group_permissions(user), {'auth.test_group'})
|
|
|
|
with mock.patch.object(self.UserModel, 'is_anonymous', True):
|
|
self.assertEqual(backend.get_all_permissions(user), set())
|
|
self.assertEqual(backend.get_user_permissions(user), set())
|
|
self.assertEqual(backend.get_group_permissions(user), set())
|
|
|
|
def test_inactive_has_no_permissions(self):
|
|
"""
|
|
#17903 -- Inactive users shouldn't have permissions in
|
|
ModelBackend.get_(all|user|group)_permissions().
|
|
"""
|
|
backend = ModelBackend()
|
|
|
|
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
|
content_type = ContentType.objects.get_for_model(Group)
|
|
user_perm = Permission.objects.create(name='test', content_type=content_type, codename='test_user')
|
|
group_perm = Permission.objects.create(name='test2', content_type=content_type, codename='test_group')
|
|
user.user_permissions.add(user_perm)
|
|
|
|
group = Group.objects.create(name='test_group')
|
|
user.groups.add(group)
|
|
group.permissions.add(group_perm)
|
|
|
|
self.assertEqual(backend.get_all_permissions(user), {'auth.test_user', 'auth.test_group'})
|
|
self.assertEqual(backend.get_user_permissions(user), {'auth.test_user'})
|
|
self.assertEqual(backend.get_group_permissions(user), {'auth.test_group'})
|
|
|
|
user.is_active = False
|
|
user.save()
|
|
|
|
self.assertEqual(backend.get_all_permissions(user), set())
|
|
self.assertEqual(backend.get_user_permissions(user), set())
|
|
self.assertEqual(backend.get_group_permissions(user), set())
|
|
|
|
def test_get_all_superuser_permissions(self):
|
|
"""A superuser has all permissions. Refs #14795."""
|
|
user = self.UserModel._default_manager.get(pk=self.superuser.pk)
|
|
self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all()))
|
|
|
|
@override_settings(PASSWORD_HASHERS=['auth_tests.test_auth_backends.CountingMD5PasswordHasher'])
|
|
def test_authentication_timing(self):
|
|
"""Hasher is run once regardless of whether the user exists. Refs #20760."""
|
|
# Re-set the password, because this tests overrides PASSWORD_HASHERS
|
|
self.user.set_password('test')
|
|
self.user.save()
|
|
|
|
CountingMD5PasswordHasher.calls = 0
|
|
username = getattr(self.user, self.UserModel.USERNAME_FIELD)
|
|
authenticate(username=username, password='test')
|
|
self.assertEqual(CountingMD5PasswordHasher.calls, 1)
|
|
|
|
CountingMD5PasswordHasher.calls = 0
|
|
authenticate(username='no_such_user', password='test')
|
|
self.assertEqual(CountingMD5PasswordHasher.calls, 1)
|
|
|
|
@override_settings(PASSWORD_HASHERS=['auth_tests.test_auth_backends.CountingMD5PasswordHasher'])
|
|
def test_authentication_without_credentials(self):
|
|
CountingMD5PasswordHasher.calls = 0
|
|
for credentials in (
|
|
{},
|
|
{'username': getattr(self.user, self.UserModel.USERNAME_FIELD)},
|
|
{'password': 'test'},
|
|
):
|
|
with self.subTest(credentials=credentials):
|
|
with self.assertNumQueries(0):
|
|
authenticate(**credentials)
|
|
self.assertEqual(CountingMD5PasswordHasher.calls, 0)
|
|
|
|
|
|
class ModelBackendTest(BaseModelBackendTest, TestCase):
|
|
"""
|
|
Tests for the ModelBackend using the default User model.
|
|
"""
|
|
UserModel = User
|
|
user_credentials = {'username': 'test', 'password': 'test'}
|
|
|
|
def create_users(self):
|
|
self.user = User.objects.create_user(email='test@example.com', **self.user_credentials)
|
|
self.superuser = User.objects.create_superuser(
|
|
username='test2',
|
|
email='test2@example.com',
|
|
password='test',
|
|
)
|
|
|
|
def test_authenticate_inactive(self):
|
|
"""
|
|
An inactive user can't authenticate.
|
|
"""
|
|
self.assertEqual(authenticate(**self.user_credentials), self.user)
|
|
self.user.is_active = False
|
|
self.user.save()
|
|
self.assertIsNone(authenticate(**self.user_credentials))
|
|
|
|
@override_settings(AUTH_USER_MODEL='auth_tests.CustomUserWithoutIsActiveField')
|
|
def test_authenticate_user_without_is_active_field(self):
|
|
"""
|
|
A custom user without an `is_active` field is allowed to authenticate.
|
|
"""
|
|
user = CustomUserWithoutIsActiveField.objects._create_user(
|
|
username='test', email='test@example.com', password='test',
|
|
)
|
|
self.assertEqual(authenticate(username='test', password='test'), user)
|
|
|
|
|
|
@override_settings(AUTH_USER_MODEL='auth_tests.ExtensionUser')
|
|
class ExtensionUserModelBackendTest(BaseModelBackendTest, TestCase):
|
|
"""
|
|
Tests for the ModelBackend using the custom ExtensionUser model.
|
|
|
|
This isn't a perfect test, because both the User and ExtensionUser are
|
|
synchronized to the database, which wouldn't ordinary happen in
|
|
production. As a result, it doesn't catch errors caused by the non-
|
|
existence of the User table.
|
|
|
|
The specific problem is queries on .filter(groups__user) et al, which
|
|
makes an implicit assumption that the user model is called 'User'. In
|
|
production, the auth.User table won't exist, so the requested join
|
|
won't exist either; in testing, the auth.User *does* exist, and
|
|
so does the join. However, the join table won't contain any useful
|
|
data; for testing, we check that the data we expect actually does exist.
|
|
"""
|
|
|
|
UserModel = ExtensionUser
|
|
|
|
def create_users(self):
|
|
self.user = ExtensionUser._default_manager.create_user(
|
|
username='test',
|
|
email='test@example.com',
|
|
password='test',
|
|
date_of_birth=date(2006, 4, 25)
|
|
)
|
|
self.superuser = ExtensionUser._default_manager.create_superuser(
|
|
username='test2',
|
|
email='test2@example.com',
|
|
password='test',
|
|
date_of_birth=date(1976, 11, 8)
|
|
)
|
|
|
|
|
|
@override_settings(AUTH_USER_MODEL='auth_tests.CustomPermissionsUser')
|
|
class CustomPermissionsUserModelBackendTest(BaseModelBackendTest, TestCase):
|
|
"""
|
|
Tests for the ModelBackend using the CustomPermissionsUser model.
|
|
|
|
As with the ExtensionUser test, this isn't a perfect test, because both
|
|
the User and CustomPermissionsUser are synchronized to the database,
|
|
which wouldn't ordinary happen in production.
|
|
"""
|
|
|
|
UserModel = CustomPermissionsUser
|
|
|
|
def create_users(self):
|
|
self.user = CustomPermissionsUser._default_manager.create_user(
|
|
email='test@example.com',
|
|
password='test',
|
|
date_of_birth=date(2006, 4, 25)
|
|
)
|
|
self.superuser = CustomPermissionsUser._default_manager.create_superuser(
|
|
email='test2@example.com',
|
|
password='test',
|
|
date_of_birth=date(1976, 11, 8)
|
|
)
|
|
|
|
|
|
@override_settings(AUTH_USER_MODEL='auth_tests.CustomUser')
|
|
class CustomUserModelBackendAuthenticateTest(TestCase):
|
|
"""
|
|
The model backend can accept a credentials kwarg labeled with
|
|
custom user model's USERNAME_FIELD.
|
|
"""
|
|
|
|
def test_authenticate(self):
|
|
test_user = CustomUser._default_manager.create_user(
|
|
email='test@example.com',
|
|
password='test',
|
|
date_of_birth=date(2006, 4, 25)
|
|
)
|
|
authenticated_user = authenticate(email='test@example.com', password='test')
|
|
self.assertEqual(test_user, authenticated_user)
|
|
|
|
|
|
@override_settings(AUTH_USER_MODEL='auth_tests.UUIDUser')
|
|
class UUIDUserTests(TestCase):
|
|
|
|
def test_login(self):
|
|
"""
|
|
A custom user with a UUID primary key should be able to login.
|
|
"""
|
|
user = UUIDUser.objects.create_user(username='uuid', password='test')
|
|
self.assertTrue(self.client.login(username='uuid', password='test'))
|
|
self.assertEqual(UUIDUser.objects.get(pk=self.client.session[SESSION_KEY]), user)
|
|
|
|
|
|
class TestObj:
|
|
pass
|
|
|
|
|
|
class SimpleRowlevelBackend:
|
|
def has_perm(self, user, perm, obj=None):
|
|
if not obj:
|
|
return # We only support row level perms
|
|
|
|
if isinstance(obj, TestObj):
|
|
if user.username == 'test2':
|
|
return True
|
|
elif user.is_anonymous and perm == 'anon':
|
|
return True
|
|
elif not user.is_active and perm == 'inactive':
|
|
return True
|
|
return False
|
|
|
|
def has_module_perms(self, user, app_label):
|
|
return (user.is_anonymous or user.is_active) and app_label == 'app1'
|
|
|
|
def get_all_permissions(self, user, obj=None):
|
|
if not obj:
|
|
return [] # We only support row level perms
|
|
|
|
if not isinstance(obj, TestObj):
|
|
return ['none']
|
|
|
|
if user.is_anonymous:
|
|
return ['anon']
|
|
if user.username == 'test2':
|
|
return ['simple', 'advanced']
|
|
else:
|
|
return ['simple']
|
|
|
|
def get_group_permissions(self, user, obj=None):
|
|
if not obj:
|
|
return # We only support row level perms
|
|
|
|
if not isinstance(obj, TestObj):
|
|
return ['none']
|
|
|
|
if 'test_group' in [group.name for group in user.groups.all()]:
|
|
return ['group_perm']
|
|
else:
|
|
return ['none']
|
|
|
|
|
|
@modify_settings(AUTHENTICATION_BACKENDS={
|
|
'append': 'auth_tests.test_auth_backends.SimpleRowlevelBackend',
|
|
})
|
|
class RowlevelBackendTest(TestCase):
|
|
"""
|
|
Tests for auth backend that supports object level permissions
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user1 = User.objects.create_user('test', 'test@example.com', 'test')
|
|
cls.user2 = User.objects.create_user('test2', 'test2@example.com', 'test')
|
|
cls.user3 = User.objects.create_user('test3', 'test3@example.com', 'test')
|
|
|
|
def tearDown(self):
|
|
# The get_group_permissions test messes with ContentTypes, which will
|
|
# be cached; flush the cache to ensure there are no side effects
|
|
# Refs #14975, #14925
|
|
ContentType.objects.clear_cache()
|
|
|
|
def test_has_perm(self):
|
|
self.assertIs(self.user1.has_perm('perm', TestObj()), False)
|
|
self.assertIs(self.user2.has_perm('perm', TestObj()), True)
|
|
self.assertIs(self.user2.has_perm('perm'), False)
|
|
self.assertIs(self.user2.has_perms(['simple', 'advanced'], TestObj()), True)
|
|
self.assertIs(self.user3.has_perm('perm', TestObj()), False)
|
|
self.assertIs(self.user3.has_perm('anon', TestObj()), False)
|
|
self.assertIs(self.user3.has_perms(['simple', 'advanced'], TestObj()), False)
|
|
|
|
def test_get_all_permissions(self):
|
|
self.assertEqual(self.user1.get_all_permissions(TestObj()), {'simple'})
|
|
self.assertEqual(self.user2.get_all_permissions(TestObj()), {'simple', 'advanced'})
|
|
self.assertEqual(self.user2.get_all_permissions(), set())
|
|
|
|
def test_get_group_permissions(self):
|
|
group = Group.objects.create(name='test_group')
|
|
self.user3.groups.add(group)
|
|
self.assertEqual(self.user3.get_group_permissions(TestObj()), {'group_perm'})
|
|
|
|
|
|
@override_settings(
|
|
AUTHENTICATION_BACKENDS=['auth_tests.test_auth_backends.SimpleRowlevelBackend'],
|
|
)
|
|
class AnonymousUserBackendTest(SimpleTestCase):
|
|
"""
|
|
Tests for AnonymousUser delegating to backend.
|
|
"""
|
|
|
|
def setUp(self):
|
|
self.user1 = AnonymousUser()
|
|
|
|
def test_has_perm(self):
|
|
self.assertIs(self.user1.has_perm('perm', TestObj()), False)
|
|
self.assertIs(self.user1.has_perm('anon', TestObj()), True)
|
|
|
|
def test_has_perms(self):
|
|
self.assertIs(self.user1.has_perms(['anon'], TestObj()), True)
|
|
self.assertIs(self.user1.has_perms(['anon', 'perm'], TestObj()), False)
|
|
|
|
def test_has_module_perms(self):
|
|
self.assertIs(self.user1.has_module_perms("app1"), True)
|
|
self.assertIs(self.user1.has_module_perms("app2"), False)
|
|
|
|
def test_get_all_permissions(self):
|
|
self.assertEqual(self.user1.get_all_permissions(TestObj()), {'anon'})
|
|
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=[])
|
|
class NoBackendsTest(TestCase):
|
|
"""
|
|
An appropriate error is raised if no auth backends are provided.
|
|
"""
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user = User.objects.create_user('test', 'test@example.com', 'test')
|
|
|
|
def test_raises_exception(self):
|
|
msg = (
|
|
'No authentication backends have been defined. '
|
|
'Does AUTHENTICATION_BACKENDS contain anything?'
|
|
)
|
|
with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
|
self.user.has_perm(('perm', TestObj()))
|
|
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=['auth_tests.test_auth_backends.SimpleRowlevelBackend'])
|
|
class InActiveUserBackendTest(TestCase):
|
|
"""
|
|
Tests for an inactive user
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user1 = User.objects.create_user('test', 'test@example.com', 'test')
|
|
cls.user1.is_active = False
|
|
cls.user1.save()
|
|
|
|
def test_has_perm(self):
|
|
self.assertIs(self.user1.has_perm('perm', TestObj()), False)
|
|
self.assertIs(self.user1.has_perm('inactive', TestObj()), True)
|
|
|
|
def test_has_module_perms(self):
|
|
self.assertIs(self.user1.has_module_perms("app1"), False)
|
|
self.assertIs(self.user1.has_module_perms("app2"), False)
|
|
|
|
|
|
class PermissionDeniedBackend:
|
|
"""
|
|
Always raises PermissionDenied in `authenticate`, `has_perm` and `has_module_perms`.
|
|
"""
|
|
|
|
def authenticate(self, request, username=None, password=None):
|
|
raise PermissionDenied
|
|
|
|
def has_perm(self, user_obj, perm, obj=None):
|
|
raise PermissionDenied
|
|
|
|
def has_module_perms(self, user_obj, app_label):
|
|
raise PermissionDenied
|
|
|
|
|
|
class PermissionDeniedBackendTest(TestCase):
|
|
"""
|
|
Other backends are not checked once a backend raises PermissionDenied
|
|
"""
|
|
backend = 'auth_tests.test_auth_backends.PermissionDeniedBackend'
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user1 = User.objects.create_user('test', 'test@example.com', 'test')
|
|
|
|
def setUp(self):
|
|
self.user_login_failed = []
|
|
signals.user_login_failed.connect(self.user_login_failed_listener)
|
|
|
|
def tearDown(self):
|
|
signals.user_login_failed.disconnect(self.user_login_failed_listener)
|
|
|
|
def user_login_failed_listener(self, sender, credentials, **kwargs):
|
|
self.user_login_failed.append(credentials)
|
|
|
|
@modify_settings(AUTHENTICATION_BACKENDS={'prepend': backend})
|
|
def test_permission_denied(self):
|
|
"user is not authenticated after a backend raises permission denied #2550"
|
|
self.assertIsNone(authenticate(username='test', password='test'))
|
|
# user_login_failed signal is sent.
|
|
self.assertEqual(self.user_login_failed, [{'password': '********************', 'username': 'test'}])
|
|
|
|
@modify_settings(AUTHENTICATION_BACKENDS={'append': backend})
|
|
def test_authenticates(self):
|
|
self.assertEqual(authenticate(username='test', password='test'), self.user1)
|
|
|
|
@modify_settings(AUTHENTICATION_BACKENDS={'prepend': backend})
|
|
def test_has_perm_denied(self):
|
|
content_type = ContentType.objects.get_for_model(Group)
|
|
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
|
self.user1.user_permissions.add(perm)
|
|
|
|
self.assertIs(self.user1.has_perm('auth.test'), False)
|
|
self.assertIs(self.user1.has_module_perms('auth'), False)
|
|
|
|
@modify_settings(AUTHENTICATION_BACKENDS={'append': backend})
|
|
def test_has_perm(self):
|
|
content_type = ContentType.objects.get_for_model(Group)
|
|
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
|
self.user1.user_permissions.add(perm)
|
|
|
|
self.assertIs(self.user1.has_perm('auth.test'), True)
|
|
self.assertIs(self.user1.has_module_perms('auth'), True)
|
|
|
|
|
|
class NewModelBackend(ModelBackend):
|
|
pass
|
|
|
|
|
|
class ChangedBackendSettingsTest(TestCase):
|
|
"""
|
|
Tests for changes in the settings.AUTHENTICATION_BACKENDS
|
|
"""
|
|
backend = 'auth_tests.test_auth_backends.NewModelBackend'
|
|
|
|
TEST_USERNAME = 'test_user'
|
|
TEST_PASSWORD = 'test_password'
|
|
TEST_EMAIL = 'test@example.com'
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
User.objects.create_user(cls.TEST_USERNAME, cls.TEST_EMAIL, cls.TEST_PASSWORD)
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=[backend])
|
|
def test_changed_backend_settings(self):
|
|
"""
|
|
Removing a backend configured in AUTHENTICATION_BACKENDS makes already
|
|
logged-in users disconnect.
|
|
"""
|
|
# Get a session for the test user
|
|
self.assertTrue(self.client.login(
|
|
username=self.TEST_USERNAME,
|
|
password=self.TEST_PASSWORD,
|
|
))
|
|
# Prepare a request object
|
|
request = HttpRequest()
|
|
request.session = self.client.session
|
|
# Remove NewModelBackend
|
|
with self.settings(AUTHENTICATION_BACKENDS=[
|
|
'django.contrib.auth.backends.ModelBackend']):
|
|
# Get the user from the request
|
|
user = get_user(request)
|
|
|
|
# Assert that the user retrieval is successful and the user is
|
|
# anonymous as the backend is not longer available.
|
|
self.assertIsNotNone(user)
|
|
self.assertTrue(user.is_anonymous)
|
|
|
|
|
|
class TypeErrorBackend:
|
|
"""
|
|
Always raises TypeError.
|
|
"""
|
|
|
|
@sensitive_variables('password')
|
|
def authenticate(self, request, username=None, password=None):
|
|
raise TypeError
|
|
|
|
|
|
class SkippedBackend:
|
|
def authenticate(self):
|
|
# Doesn't accept any credentials so is skipped by authenticate().
|
|
pass
|
|
|
|
|
|
class SkippedBackendWithDecoratedMethod:
|
|
@sensitive_variables()
|
|
def authenticate(self):
|
|
pass
|
|
|
|
|
|
class AuthenticateTests(TestCase):
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user1 = User.objects.create_user('test', 'test@example.com', 'test')
|
|
|
|
def setUp(self):
|
|
self.sensitive_password = 'mypassword'
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=['auth_tests.test_auth_backends.TypeErrorBackend'])
|
|
def test_type_error_raised(self):
|
|
"""A TypeError within a backend is propagated properly (#18171)."""
|
|
with self.assertRaises(TypeError):
|
|
authenticate(username='test', password='test')
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=['auth_tests.test_auth_backends.TypeErrorBackend'])
|
|
def test_authenticate_sensitive_variables(self):
|
|
try:
|
|
authenticate(username='testusername', password=self.sensitive_password)
|
|
except TypeError:
|
|
exc_info = sys.exc_info()
|
|
rf = RequestFactory()
|
|
response = technical_500_response(rf.get('/'), *exc_info)
|
|
self.assertNotContains(response, self.sensitive_password, status_code=500)
|
|
self.assertContains(response, 'TypeErrorBackend', status_code=500)
|
|
self.assertContains(
|
|
response,
|
|
'<tr><td>credentials</td><td class="code">'
|
|
'<pre>'********************'</pre></td></tr>',
|
|
html=True,
|
|
status_code=500,
|
|
)
|
|
|
|
def test_clean_credentials_sensitive_variables(self):
|
|
try:
|
|
# Passing in a list to cause an exception
|
|
_clean_credentials([1, self.sensitive_password])
|
|
except TypeError:
|
|
exc_info = sys.exc_info()
|
|
rf = RequestFactory()
|
|
response = technical_500_response(rf.get('/'), *exc_info)
|
|
self.assertNotContains(response, self.sensitive_password, status_code=500)
|
|
self.assertContains(
|
|
response,
|
|
'<tr><td>credentials</td><td class="code">'
|
|
'<pre>'********************'</pre></td></tr>',
|
|
html=True,
|
|
status_code=500,
|
|
)
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=(
|
|
'auth_tests.test_auth_backends.SkippedBackend',
|
|
'django.contrib.auth.backends.ModelBackend',
|
|
))
|
|
def test_skips_backends_without_arguments(self):
|
|
"""
|
|
A backend (SkippedBackend) is ignored if it doesn't accept the
|
|
credentials as arguments.
|
|
"""
|
|
self.assertEqual(authenticate(username='test', password='test'), self.user1)
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=(
|
|
'auth_tests.test_auth_backends.SkippedBackendWithDecoratedMethod',
|
|
'django.contrib.auth.backends.ModelBackend',
|
|
))
|
|
def test_skips_backends_with_decorated_method(self):
|
|
self.assertEqual(authenticate(username='test', password='test'), self.user1)
|
|
|
|
|
|
class ImproperlyConfiguredUserModelTest(TestCase):
|
|
"""
|
|
An exception from within get_user_model() is propagated and doesn't
|
|
raise an UnboundLocalError (#21439).
|
|
"""
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user1 = User.objects.create_user('test', 'test@example.com', 'test')
|
|
|
|
def setUp(self):
|
|
self.client.login(username='test', password='test')
|
|
|
|
@override_settings(AUTH_USER_MODEL='thismodel.doesntexist')
|
|
def test_does_not_shadow_exception(self):
|
|
# Prepare a request object
|
|
request = HttpRequest()
|
|
request.session = self.client.session
|
|
|
|
msg = (
|
|
"AUTH_USER_MODEL refers to model 'thismodel.doesntexist' "
|
|
"that has not been installed"
|
|
)
|
|
with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
|
get_user(request)
|
|
|
|
|
|
class ImportedModelBackend(ModelBackend):
|
|
pass
|
|
|
|
|
|
class CustomModelBackend(ModelBackend):
|
|
pass
|
|
|
|
|
|
class OtherModelBackend(ModelBackend):
|
|
pass
|
|
|
|
|
|
class ImportedBackendTests(TestCase):
|
|
"""
|
|
#23925 - The backend path added to the session should be the same
|
|
as the one defined in AUTHENTICATION_BACKENDS setting.
|
|
"""
|
|
|
|
backend = 'auth_tests.backend_alias.ImportedModelBackend'
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=[backend])
|
|
def test_backend_path(self):
|
|
username = 'username'
|
|
password = 'password'
|
|
User.objects.create_user(username, 'email', password)
|
|
self.assertTrue(self.client.login(username=username, password=password))
|
|
request = HttpRequest()
|
|
request.session = self.client.session
|
|
self.assertEqual(request.session[BACKEND_SESSION_KEY], self.backend)
|
|
|
|
|
|
class SelectingBackendTests(TestCase):
|
|
backend = 'auth_tests.test_auth_backends.CustomModelBackend'
|
|
other_backend = 'auth_tests.test_auth_backends.OtherModelBackend'
|
|
username = 'username'
|
|
password = 'password'
|
|
|
|
def assertBackendInSession(self, backend):
|
|
request = HttpRequest()
|
|
request.session = self.client.session
|
|
self.assertEqual(request.session[BACKEND_SESSION_KEY], backend)
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=[backend])
|
|
def test_backend_path_login_without_authenticate_single_backend(self):
|
|
user = User.objects.create_user(self.username, 'email', self.password)
|
|
self.client._login(user)
|
|
self.assertBackendInSession(self.backend)
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=[backend, other_backend])
|
|
def test_backend_path_login_without_authenticate_multiple_backends(self):
|
|
user = User.objects.create_user(self.username, 'email', self.password)
|
|
expected_message = (
|
|
'You have multiple authentication backends configured and '
|
|
'therefore must provide the `backend` argument or set the '
|
|
'`backend` attribute on the user.'
|
|
)
|
|
with self.assertRaisesMessage(ValueError, expected_message):
|
|
self.client._login(user)
|
|
|
|
def test_non_string_backend(self):
|
|
user = User.objects.create_user(self.username, 'email', self.password)
|
|
expected_message = (
|
|
'backend must be a dotted import path string (got '
|
|
'<class \'django.contrib.auth.backends.ModelBackend\'>).'
|
|
)
|
|
with self.assertRaisesMessage(TypeError, expected_message):
|
|
self.client._login(user, backend=ModelBackend)
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=[backend, other_backend])
|
|
def test_backend_path_login_with_explicit_backends(self):
|
|
user = User.objects.create_user(self.username, 'email', self.password)
|
|
self.client._login(user, self.other_backend)
|
|
self.assertBackendInSession(self.other_backend)
|
|
|
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=['django.contrib.auth.backends.AllowAllUsersModelBackend'])
|
|
class AllowAllUsersModelBackendTest(TestCase):
|
|
"""
|
|
Inactive users may authenticate with the AllowAllUsersModelBackend.
|
|
"""
|
|
user_credentials = {'username': 'test', 'password': 'test'}
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.user = User.objects.create_user(
|
|
email='test@example.com', is_active=False,
|
|
**cls.user_credentials
|
|
)
|
|
|
|
def test_authenticate(self):
|
|
self.assertFalse(self.user.is_active)
|
|
self.assertEqual(authenticate(**self.user_credentials), self.user)
|
|
|
|
def test_get_user(self):
|
|
self.client.force_login(self.user)
|
|
request = HttpRequest()
|
|
request.session = self.client.session
|
|
user = get_user(request)
|
|
self.assertEqual(user, self.user)
|