From fde2e4fd6ecdf6aa9c31d004f91be2b8df76ddb9 Mon Sep 17 00:00:00 2001 From: Preston Timmons Date: Wed, 3 Apr 2013 03:22:07 +0000 Subject: Modified auth to work with unittest2 discovery. --- django/contrib/auth/tests/__init__.py | 41 +- django/contrib/auth/tests/auth_backends.py | 404 --------------- django/contrib/auth/tests/basic.py | 245 --------- django/contrib/auth/tests/context_processors.py | 170 ------- django/contrib/auth/tests/custom_user.py | 168 ------- django/contrib/auth/tests/decorators.py | 48 -- django/contrib/auth/tests/forms.py | 411 ---------------- django/contrib/auth/tests/handlers.py | 71 --- django/contrib/auth/tests/hashers.py | 180 ------- django/contrib/auth/tests/management.py | 228 --------- django/contrib/auth/tests/models.py | 142 ------ django/contrib/auth/tests/remote_user.py | 198 -------- django/contrib/auth/tests/signals.py | 80 --- django/contrib/auth/tests/test_auth_backends.py | 404 +++++++++++++++ django/contrib/auth/tests/test_basic.py | 257 ++++++++++ .../contrib/auth/tests/test_context_processors.py | 170 +++++++ django/contrib/auth/tests/test_custom_user.py | 168 +++++++ django/contrib/auth/tests/test_decorators.py | 48 ++ django/contrib/auth/tests/test_forms.py | 411 ++++++++++++++++ django/contrib/auth/tests/test_handlers.py | 71 +++ django/contrib/auth/tests/test_hashers.py | 180 +++++++ django/contrib/auth/tests/test_management.py | 228 +++++++++ django/contrib/auth/tests/test_models.py | 142 ++++++ django/contrib/auth/tests/test_remote_user.py | 198 ++++++++ django/contrib/auth/tests/test_signals.py | 80 +++ django/contrib/auth/tests/test_tokens.py | 69 +++ django/contrib/auth/tests/test_views.py | 545 +++++++++++++++++++++ django/contrib/auth/tests/tokens.py | 69 --- django/contrib/auth/tests/views.py | 545 --------------------- 29 files changed, 2985 insertions(+), 2986 deletions(-) delete mode 100644 django/contrib/auth/tests/auth_backends.py delete mode 100644 django/contrib/auth/tests/basic.py delete mode 100644 django/contrib/auth/tests/context_processors.py delete mode 100644 django/contrib/auth/tests/custom_user.py delete mode 100644 django/contrib/auth/tests/decorators.py delete mode 100644 django/contrib/auth/tests/forms.py delete mode 100644 django/contrib/auth/tests/handlers.py delete mode 100644 django/contrib/auth/tests/hashers.py delete mode 100644 django/contrib/auth/tests/management.py delete mode 100644 django/contrib/auth/tests/models.py delete mode 100644 django/contrib/auth/tests/remote_user.py delete mode 100644 django/contrib/auth/tests/signals.py create mode 100644 django/contrib/auth/tests/test_auth_backends.py create mode 100644 django/contrib/auth/tests/test_basic.py create mode 100644 django/contrib/auth/tests/test_context_processors.py create mode 100644 django/contrib/auth/tests/test_custom_user.py create mode 100644 django/contrib/auth/tests/test_decorators.py create mode 100644 django/contrib/auth/tests/test_forms.py create mode 100644 django/contrib/auth/tests/test_handlers.py create mode 100644 django/contrib/auth/tests/test_hashers.py create mode 100644 django/contrib/auth/tests/test_management.py create mode 100644 django/contrib/auth/tests/test_models.py create mode 100644 django/contrib/auth/tests/test_remote_user.py create mode 100644 django/contrib/auth/tests/test_signals.py create mode 100644 django/contrib/auth/tests/test_tokens.py create mode 100644 django/contrib/auth/tests/test_views.py delete mode 100644 django/contrib/auth/tests/tokens.py delete mode 100644 django/contrib/auth/tests/views.py diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py index 038c8980d9..2e35e33d7f 100644 --- a/django/contrib/auth/tests/__init__.py +++ b/django/contrib/auth/tests/__init__.py @@ -1,29 +1,16 @@ -from django.contrib.auth.tests.custom_user import * -from django.contrib.auth.tests.auth_backends import * -from django.contrib.auth.tests.basic import * -from django.contrib.auth.tests.context_processors import * -from django.contrib.auth.tests.decorators import * -from django.contrib.auth.tests.forms import * -from django.contrib.auth.tests.remote_user import * -from django.contrib.auth.tests.management import * -from django.contrib.auth.tests.models import * -from django.contrib.auth.tests.handlers import * -from django.contrib.auth.tests.hashers import * -from django.contrib.auth.tests.signals import * -from django.contrib.auth.tests.tokens import * -from django.contrib.auth.tests.views import * +from django.contrib.auth.tests.test_custom_user import * +from django.contrib.auth.tests.test_auth_backends import * +from django.contrib.auth.tests.test_basic import * +from django.contrib.auth.tests.test_context_processors import * +from django.contrib.auth.tests.test_decorators import * +from django.contrib.auth.tests.test_forms import * +from django.contrib.auth.tests.test_remote_user import * +from django.contrib.auth.tests.test_management import * +from django.contrib.auth.tests.test_models import * +from django.contrib.auth.tests.test_handlers import * +from django.contrib.auth.tests.test_hashers import * +from django.contrib.auth.tests.test_signals import * +from django.contrib.auth.tests.test_tokens import * +from django.contrib.auth.tests.test_views import * # The password for the fixture data users is 'password' - -from django.dispatch import receiver -from django.test.signals import setting_changed - - -@receiver(setting_changed) -def user_model_swapped(**kwargs): - if kwargs['setting'] == 'AUTH_USER_MODEL': - from django.db.models.manager import ensure_default_manager - from django.contrib.auth.models import User - # Reset User manager - setattr(User, 'objects', User._default_manager) - ensure_default_manager(User) diff --git a/django/contrib/auth/tests/auth_backends.py b/django/contrib/auth/tests/auth_backends.py deleted file mode 100644 index 20bbb4dbd4..0000000000 --- a/django/contrib/auth/tests/auth_backends.py +++ /dev/null @@ -1,404 +0,0 @@ -from __future__ import unicode_literals -from datetime import date - -from django.conf import settings -from django.contrib.auth.models import User, Group, Permission, AnonymousUser -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.contrib.auth.tests.custom_user import ExtensionUser, CustomPermissionsUser, CustomUser -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ImproperlyConfigured, PermissionDenied -from django.contrib.auth import authenticate -from django.test import TestCase -from django.test.utils import override_settings - - -class BaseModelBackendTest(object): - """ - 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.curr_auth = settings.AUTHENTICATION_BACKENDS - settings.AUTHENTICATION_BACKENDS = (self.backend,) - self.create_users() - - def tearDown(self): - settings.AUTHENTICATION_BACKENDS = self.curr_auth - # 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.assertEqual(user.has_perm('auth.test'), False) - user.is_staff = True - user.save() - self.assertEqual(user.has_perm('auth.test'), False) - user.is_superuser = True - user.save() - self.assertEqual(user.has_perm('auth.test'), True) - user.is_staff = False - user.is_superuser = False - user.save() - self.assertEqual(user.has_perm('auth.test'), False) - user.is_staff = True - user.is_superuser = True - user.is_active = False - user.save() - self.assertEqual(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) - user.save() - - # reloading user to purge the _perm_cache - user = self.UserModel._default_manager.get(pk=self.user.pk) - self.assertEqual(user.get_all_permissions() == set(['auth.test']), True) - self.assertEqual(user.get_group_permissions(), set([])) - self.assertEqual(user.has_module_perms('Group'), False) - self.assertEqual(user.has_module_perms('auth'), True) - perm = Permission.objects.create(name='test2', content_type=content_type, codename='test2') - user.user_permissions.add(perm) - user.save() - perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3') - user.user_permissions.add(perm) - user.save() - user = self.UserModel._default_manager.get(pk=self.user.pk) - self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3'])) - self.assertEqual(user.has_perm('test'), False) - self.assertEqual(user.has_perm('auth.test'), True) - self.assertEqual(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) - group.save() - user.groups.add(group) - user = self.UserModel._default_manager.get(pk=self.user.pk) - exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group']) - self.assertEqual(user.get_all_permissions(), exp) - self.assertEqual(user.get_group_permissions(), set(['auth.test_group'])) - self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True) - - user = AnonymousUser() - self.assertEqual(user.has_perm('test'), False) - self.assertEqual(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) - user.save() - - self.assertEqual(user.has_perm('auth.test', 'object'), False) - self.assertEqual(user.get_all_permissions('object'), set([])) - self.assertEqual(user.has_perm('auth.test'), True) - self.assertEqual(user.get_all_permissions(), set(['auth.test'])) - - 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())) - - -@skipIfCustomUser -class ModelBackendTest(BaseModelBackendTest, TestCase): - """ - Tests for the ModelBackend using the default User model. - """ - UserModel = User - - def create_users(self): - self.user = User.objects.create_user( - username='test', - email='test@example.com', - password='test', - ) - self.superuser = User.objects.create_superuser( - username='test2', - email='test2@example.com', - password='test', - ) - - -@override_settings(AUTH_USER_MODEL='auth.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.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.CustomUser') -class CustomUserModelBackendAuthenticateTest(TestCase): - """ - Tests that 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) - - - -class TestObj(object): - pass - - -class SimpleRowlevelBackend(object): - 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): - if not user.is_anonymous() and not user.is_active: - return False - return 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'] - - -@skipIfCustomUser -class RowlevelBackendTest(TestCase): - """ - Tests for auth backend that supports object level permissions - """ - backend = 'django.contrib.auth.tests.auth_backends.SimpleRowlevelBackend' - - def setUp(self): - self.curr_auth = settings.AUTHENTICATION_BACKENDS - settings.AUTHENTICATION_BACKENDS = tuple(self.curr_auth) + (self.backend,) - self.user1 = User.objects.create_user('test', 'test@example.com', 'test') - self.user2 = User.objects.create_user('test2', 'test2@example.com', 'test') - self.user3 = User.objects.create_user('test3', 'test3@example.com', 'test') - - def tearDown(self): - settings.AUTHENTICATION_BACKENDS = self.curr_auth - # 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.assertEqual(self.user1.has_perm('perm', TestObj()), False) - self.assertEqual(self.user2.has_perm('perm', TestObj()), True) - self.assertEqual(self.user2.has_perm('perm'), False) - self.assertEqual(self.user2.has_perms(['simple', 'advanced'], TestObj()), True) - self.assertEqual(self.user3.has_perm('perm', TestObj()), False) - self.assertEqual(self.user3.has_perm('anon', TestObj()), False) - self.assertEqual(self.user3.has_perms(['simple', 'advanced'], TestObj()), False) - - def test_get_all_permissions(self): - self.assertEqual(self.user1.get_all_permissions(TestObj()), set(['simple'])) - self.assertEqual(self.user2.get_all_permissions(TestObj()), set(['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()), set(['group_perm'])) - - -class AnonymousUserBackendTest(TestCase): - """ - Tests for AnonymousUser delegating to backend. - """ - - backend = 'django.contrib.auth.tests.auth_backends.SimpleRowlevelBackend' - - def setUp(self): - self.curr_auth = settings.AUTHENTICATION_BACKENDS - settings.AUTHENTICATION_BACKENDS = (self.backend,) - self.user1 = AnonymousUser() - - def tearDown(self): - settings.AUTHENTICATION_BACKENDS = self.curr_auth - - def test_has_perm(self): - self.assertEqual(self.user1.has_perm('perm', TestObj()), False) - self.assertEqual(self.user1.has_perm('anon', TestObj()), True) - - def test_has_perms(self): - self.assertEqual(self.user1.has_perms(['anon'], TestObj()), True) - self.assertEqual(self.user1.has_perms(['anon', 'perm'], TestObj()), False) - - def test_has_module_perms(self): - self.assertEqual(self.user1.has_module_perms("app1"), True) - self.assertEqual(self.user1.has_module_perms("app2"), False) - - def test_get_all_permissions(self): - self.assertEqual(self.user1.get_all_permissions(TestObj()), set(['anon'])) - - -@skipIfCustomUser -@override_settings(AUTHENTICATION_BACKENDS=[]) -class NoBackendsTest(TestCase): - """ - Tests that an appropriate error is raised if no auth backends are provided. - """ - def setUp(self): - self.user = User.objects.create_user('test', 'test@example.com', 'test') - - def test_raises_exception(self): - self.assertRaises(ImproperlyConfigured, self.user.has_perm, ('perm', TestObj(),)) - - -@skipIfCustomUser -class InActiveUserBackendTest(TestCase): - """ - Tests for a inactive user - """ - backend = 'django.contrib.auth.tests.auth_backends.SimpleRowlevelBackend' - - def setUp(self): - self.curr_auth = settings.AUTHENTICATION_BACKENDS - settings.AUTHENTICATION_BACKENDS = (self.backend,) - self.user1 = User.objects.create_user('test', 'test@example.com', 'test') - self.user1.is_active = False - self.user1.save() - - def tearDown(self): - settings.AUTHENTICATION_BACKENDS = self.curr_auth - - def test_has_perm(self): - self.assertEqual(self.user1.has_perm('perm', TestObj()), False) - self.assertEqual(self.user1.has_perm('inactive', TestObj()), True) - - def test_has_module_perms(self): - self.assertEqual(self.user1.has_module_perms("app1"), False) - self.assertEqual(self.user1.has_module_perms("app2"), False) - - -class PermissionDeniedBackend(object): - """ - Always raises PermissionDenied. - """ - supports_object_permissions = True - supports_anonymous_user = True - supports_inactive_user = True - - def authenticate(self, username=None, password=None): - raise PermissionDenied - - -@skipIfCustomUser -class PermissionDeniedBackendTest(TestCase): - """ - Tests that other backends are not checked once a backend raises PermissionDenied - """ - backend = 'django.contrib.auth.tests.auth_backends.PermissionDeniedBackend' - - def setUp(self): - self.user1 = User.objects.create_user('test', 'test@example.com', 'test') - self.user1.save() - - @override_settings(AUTHENTICATION_BACKENDS=(backend, ) + - tuple(settings.AUTHENTICATION_BACKENDS)) - def test_permission_denied(self): - "user is not authenticated after a backend raises permission denied #2550" - self.assertEqual(authenticate(username='test', password='test'), None) - - @override_settings(AUTHENTICATION_BACKENDS=tuple( - settings.AUTHENTICATION_BACKENDS) + (backend, )) - def test_authenticates(self): - self.assertEqual(authenticate(username='test', password='test'), self.user1) diff --git a/django/contrib/auth/tests/basic.py b/django/contrib/auth/tests/basic.py deleted file mode 100644 index ec94fe5b46..0000000000 --- a/django/contrib/auth/tests/basic.py +++ /dev/null @@ -1,245 +0,0 @@ -# -*- encoding: utf-8 -*- -from __future__ import unicode_literals - -import locale - -from django.contrib.auth import get_user_model -from django.contrib.auth.management.commands import createsuperuser -from django.contrib.auth.models import User, AnonymousUser -from django.contrib.auth.tests.custom_user import CustomUser -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.core.exceptions import ImproperlyConfigured -from django.core.management import call_command -from django.test import TestCase -from django.test.utils import override_settings -from django.utils import translation -from django.utils.encoding import force_str -from django.utils.six import binary_type, PY3, StringIO - - -def mock_inputs(inputs): - """ - Decorator to temporarily replace input/getpass to allow interactive - createsuperuser. - """ - def inner(test_func): - def wrapped(*args): - class mock_getpass: - @staticmethod - def getpass(prompt=b'Password: ', stream=None): - if not PY3: - # getpass on Windows only supports prompt as bytestring (#19807) - assert isinstance(prompt, binary_type) - return inputs['password'] - - def mock_input(prompt): - # prompt should be encoded in Python 2. This line will raise an - # Exception if prompt contains unencoded non-ascii on Python 2. - prompt = str(prompt) - assert str('__proxy__') not in prompt - response = '' - for key, val in inputs.items(): - if force_str(key) in prompt.lower(): - response = val - break - return response - - old_getpass = createsuperuser.getpass - old_input = createsuperuser.input - createsuperuser.getpass = mock_getpass - createsuperuser.input = mock_input - try: - test_func(*args) - finally: - createsuperuser.getpass = old_getpass - createsuperuser.input = old_input - return wrapped - return inner - - -@skipIfCustomUser -class BasicTestCase(TestCase): - def test_user(self): - "Check that users can be created and can set their password" - u = User.objects.create_user('testuser', 'test@example.com', 'testpw') - self.assertTrue(u.has_usable_password()) - self.assertFalse(u.check_password('bad')) - self.assertTrue(u.check_password('testpw')) - - # Check we can manually set an unusable password - u.set_unusable_password() - u.save() - self.assertFalse(u.check_password('testpw')) - self.assertFalse(u.has_usable_password()) - u.set_password('testpw') - self.assertTrue(u.check_password('testpw')) - u.set_password(None) - self.assertFalse(u.has_usable_password()) - - # Check authentication/permissions - self.assertTrue(u.is_authenticated()) - self.assertFalse(u.is_staff) - self.assertTrue(u.is_active) - self.assertFalse(u.is_superuser) - - # Check API-based user creation with no password - u2 = User.objects.create_user('testuser2', 'test2@example.com') - self.assertFalse(u2.has_usable_password()) - - def test_user_no_email(self): - "Check that users can be created without an email" - u = User.objects.create_user('testuser1') - self.assertEqual(u.email, '') - - u2 = User.objects.create_user('testuser2', email='') - self.assertEqual(u2.email, '') - - u3 = User.objects.create_user('testuser3', email=None) - self.assertEqual(u3.email, '') - - def test_anonymous_user(self): - "Check the properties of the anonymous user" - a = AnonymousUser() - self.assertEqual(a.pk, None) - self.assertFalse(a.is_authenticated()) - self.assertFalse(a.is_staff) - self.assertFalse(a.is_active) - self.assertFalse(a.is_superuser) - self.assertEqual(a.groups.all().count(), 0) - self.assertEqual(a.user_permissions.all().count(), 0) - - def test_superuser(self): - "Check the creation and properties of a superuser" - super = User.objects.create_superuser('super', 'super@example.com', 'super') - self.assertTrue(super.is_superuser) - self.assertTrue(super.is_active) - self.assertTrue(super.is_staff) - - def test_createsuperuser_management_command(self): - "Check the operation of the createsuperuser management command" - # We can use the management command to create a superuser - new_io = StringIO() - call_command("createsuperuser", - interactive=False, - username="joe", - email="joe@somewhere.org", - stdout=new_io - ) - command_output = new_io.getvalue().strip() - self.assertEqual(command_output, 'Superuser created successfully.') - u = User.objects.get(username="joe") - self.assertEqual(u.email, 'joe@somewhere.org') - - # created password should be unusable - self.assertFalse(u.has_usable_password()) - - # We can supress output on the management command - new_io = StringIO() - call_command("createsuperuser", - interactive=False, - username="joe2", - email="joe2@somewhere.org", - verbosity=0, - stdout=new_io - ) - command_output = new_io.getvalue().strip() - self.assertEqual(command_output, '') - u = User.objects.get(username="joe2") - self.assertEqual(u.email, 'joe2@somewhere.org') - self.assertFalse(u.has_usable_password()) - - call_command("createsuperuser", - interactive=False, - username="joe+admin@somewhere.org", - email="joe@somewhere.org", - verbosity=0 - ) - u = User.objects.get(username="joe+admin@somewhere.org") - self.assertEqual(u.email, 'joe@somewhere.org') - self.assertFalse(u.has_usable_password()) - - @mock_inputs({'password': "nopasswd"}) - def test_createsuperuser_nolocale(self): - """ - Check that createsuperuser does not break when no locale is set. See - ticket #16017. - """ - - old_getdefaultlocale = locale.getdefaultlocale - try: - # Temporarily remove locale information - locale.getdefaultlocale = lambda: (None, None) - - # Call the command in this new environment - call_command("createsuperuser", - interactive=True, - username="nolocale@somewhere.org", - email="nolocale@somewhere.org", - verbosity=0 - ) - - except TypeError: - self.fail("createsuperuser fails if the OS provides no information about the current locale") - - finally: - # Re-apply locale information - locale.getdefaultlocale = old_getdefaultlocale - - # If we were successful, a user should have been created - u = User.objects.get(username="nolocale@somewhere.org") - self.assertEqual(u.email, 'nolocale@somewhere.org') - - @mock_inputs({ - 'password': "nopasswd", - 'uživatel': 'foo', # username (cz) - 'email': 'nolocale@somewhere.org'}) - def test_createsuperuser_non_ascii_verbose_name(self): - # Aliased so the string doesn't get extracted - from django.utils.translation import ugettext_lazy as ulazy - username_field = User._meta.get_field('username') - old_verbose_name = username_field.verbose_name - username_field.verbose_name = ulazy('uživatel') - new_io = StringIO() - try: - call_command("createsuperuser", - interactive=True, - stdout=new_io - ) - finally: - username_field.verbose_name = old_verbose_name - - command_output = new_io.getvalue().strip() - self.assertEqual(command_output, 'Superuser created successfully.') - - def test_get_user_model(self): - "The current user model can be retrieved" - self.assertEqual(get_user_model(), User) - - @override_settings(AUTH_USER_MODEL='auth.CustomUser') - def test_swappable_user(self): - "The current user model can be swapped out for another" - self.assertEqual(get_user_model(), CustomUser) - with self.assertRaises(AttributeError): - User.objects.all() - - @override_settings(AUTH_USER_MODEL='badsetting') - def test_swappable_user_bad_setting(self): - "The alternate user setting must point to something in the format app.model" - with self.assertRaises(ImproperlyConfigured): - get_user_model() - - @override_settings(AUTH_USER_MODEL='thismodel.doesntexist') - def test_swappable_user_nonexistent_model(self): - "The current user model must point to an installed model" - with self.assertRaises(ImproperlyConfigured): - get_user_model() - - @skipIfCustomUser - def test_user_verbose_names_translatable(self): - "Default User model verbose names are translatable (#19945)" - with translation.override('en'): - self.assertEqual(User._meta.verbose_name, 'user') - self.assertEqual(User._meta.verbose_name_plural, 'users') - with translation.override('es'): - self.assertEqual(User._meta.verbose_name, 'usuario') - self.assertEqual(User._meta.verbose_name_plural, 'usuarios') diff --git a/django/contrib/auth/tests/context_processors.py b/django/contrib/auth/tests/context_processors.py deleted file mode 100644 index 9e56cfce85..0000000000 --- a/django/contrib/auth/tests/context_processors.py +++ /dev/null @@ -1,170 +0,0 @@ -import os - -from django.conf import global_settings -from django.contrib.auth import authenticate -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.contrib.auth.models import User, Permission -from django.contrib.contenttypes.models import ContentType -from django.contrib.auth.context_processors import PermWrapper, PermLookupDict -from django.db.models import Q -from django.test import TestCase -from django.test.utils import override_settings -from django.utils._os import upath - - -class MockUser(object): - def has_module_perms(self, perm): - if perm == 'mockapp': - return True - return False - - def has_perm(self, perm): - if perm == 'mockapp.someperm': - return True - return False - - -class PermWrapperTests(TestCase): - """ - Test some details of the PermWrapper implementation. - """ - class EQLimiterObject(object): - """ - This object makes sure __eq__ will not be called endlessly. - """ - def __init__(self): - self.eq_calls = 0 - - def __eq__(self, other): - if self.eq_calls > 0: - return True - self.eq_calls += 1 - return False - - def test_permwrapper_in(self): - """ - Test that 'something' in PermWrapper works as expected. - """ - perms = PermWrapper(MockUser()) - # Works for modules and full permissions. - self.assertTrue('mockapp' in perms) - self.assertFalse('nonexisting' in perms) - self.assertTrue('mockapp.someperm' in perms) - self.assertFalse('mockapp.nonexisting' in perms) - - def test_permlookupdict_in(self): - """ - No endless loops if accessed with 'in' - refs #18979. - """ - pldict = PermLookupDict(MockUser(), 'mockapp') - with self.assertRaises(TypeError): - self.EQLimiterObject() in pldict - - -@skipIfCustomUser -@override_settings( - TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',), - TEMPLATE_DIRS=( - os.path.join(os.path.dirname(upath(__file__)), 'templates'), - ), - USE_TZ=False, # required for loading the fixture - PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), -) -class AuthContextProcessorTests(TestCase): - """ - Tests for the ``django.contrib.auth.context_processors.auth`` processor - """ - urls = 'django.contrib.auth.tests.urls' - fixtures = ['context-processors-users.xml'] - - @override_settings( - MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES, - TEMPLATE_CONTEXT_PROCESSORS=global_settings.TEMPLATE_CONTEXT_PROCESSORS, - ) - def test_session_not_accessed(self): - """ - Tests that the session is not accessed simply by including - the auth context processor - """ - response = self.client.get('/auth_processor_no_attr_access/') - self.assertContains(response, "Session not accessed") - - @override_settings( - MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES, - TEMPLATE_CONTEXT_PROCESSORS=global_settings.TEMPLATE_CONTEXT_PROCESSORS, - ) - def test_session_is_accessed(self): - """ - Tests that the session is accessed if the auth context processor - is used and relevant attributes accessed. - """ - response = self.client.get('/auth_processor_attr_access/') - self.assertContains(response, "Session accessed") - - def test_perms_attrs(self): - u = User.objects.create_user(username='normal', password='secret') - u.user_permissions.add( - Permission.objects.get( - content_type=ContentType.objects.get_for_model(Permission), - codename='add_permission')) - self.client.login(username='normal', password='secret') - response = self.client.get('/auth_processor_perms/') - self.assertContains(response, "Has auth permissions") - self.assertContains(response, "Has auth.add_permission permissions") - self.assertNotContains(response, "nonexisting") - - def test_perm_in_perms_attrs(self): - u = User.objects.create_user(username='normal', password='secret') - u.user_permissions.add( - Permission.objects.get( - content_type=ContentType.objects.get_for_model(Permission), - codename='add_permission')) - self.client.login(username='normal', password='secret') - response = self.client.get('/auth_processor_perm_in_perms/') - self.assertContains(response, "Has auth permissions") - self.assertContains(response, "Has auth.add_permission permissions") - self.assertNotContains(response, "nonexisting") - - def test_message_attrs(self): - self.client.login(username='super', password='secret') - response = self.client.get('/auth_processor_messages/') - self.assertContains(response, "Message 1") - - def test_user_attrs(self): - """ - Test that the lazy objects returned behave just like the wrapped objects. - """ - # These are 'functional' level tests for common use cases. Direct - # testing of the implementation (SimpleLazyObject) is in the 'utils' - # tests. - self.client.login(username='super', password='secret') - user = authenticate(username='super', password='secret') - response = self.client.get('/auth_processor_user/') - self.assertContains(response, "unicode: super") - self.assertContains(response, "id: 100") - self.assertContains(response, "username: super") - # bug #12037 is tested by the {% url %} in the template: - self.assertContains(response, "url: /userpage/super/") - - # See if this object can be used for queries where a Q() comparing - # a user can be used with another Q() (in an AND or OR fashion). - # This simulates what a template tag might do with the user from the - # context. Note that we don't need to execute a query, just build it. - # - # The failure case (bug #12049) on Python 2.4 with a LazyObject-wrapped - # User is a fatal TypeError: "function() takes at least 2 arguments - # (0 given)" deep inside deepcopy(). - # - # Python 2.5 and 2.6 succeeded, but logged internally caught exception - # spew: - # - # Exception RuntimeError: 'maximum recursion depth exceeded while - # calling a Python object' in - # ignored" - query = Q(user=response.context['user']) & Q(someflag=True) - - # Tests for user equality. This is hard because User defines - # equality in a non-duck-typing way - # See bug #12060 - self.assertEqual(response.context['user'], user) - self.assertEqual(user, response.context['user']) diff --git a/django/contrib/auth/tests/custom_user.py b/django/contrib/auth/tests/custom_user.py deleted file mode 100644 index 0d324f0953..0000000000 --- a/django/contrib/auth/tests/custom_user.py +++ /dev/null @@ -1,168 +0,0 @@ -from django.db import models -from django.contrib.auth.models import ( - BaseUserManager, - AbstractBaseUser, - AbstractUser, - UserManager, - PermissionsMixin -) - - -# The custom User uses email as the unique identifier, and requires -# that every user provide a date of birth. This lets us test -# changes in username datatype, and non-text required fields. - -class CustomUserManager(BaseUserManager): - def create_user(self, email, date_of_birth, password=None): - """ - Creates and saves a User with the given email and password. - """ - if not email: - raise ValueError('Users must have an email address') - - user = self.model( - email=CustomUserManager.normalize_email(email), - date_of_birth=date_of_birth, - ) - - user.set_password(password) - user.save(using=self._db) - return user - - def create_superuser(self, email, password, date_of_birth): - u = self.create_user(email, password=password, date_of_birth=date_of_birth) - u.is_admin = True - u.save(using=self._db) - return u - - -class CustomUser(AbstractBaseUser): - email = models.EmailField(verbose_name='email address', max_length=255, unique=True) - is_active = models.BooleanField(default=True) - is_admin = models.BooleanField(default=False) - date_of_birth = models.DateField() - - custom_objects = CustomUserManager() - - USERNAME_FIELD = 'email' - REQUIRED_FIELDS = ['date_of_birth'] - - class Meta: - app_label = 'auth' - - def get_full_name(self): - return self.email - - def get_short_name(self): - return self.email - - def __unicode__(self): - return self.email - - # Maybe required? - def get_group_permissions(self, obj=None): - return set() - - def get_all_permissions(self, obj=None): - return set() - - def has_perm(self, perm, obj=None): - return True - - def has_perms(self, perm_list, obj=None): - return True - - def has_module_perms(self, app_label): - return True - - # Admin required fields - @property - def is_staff(self): - return self.is_admin - - -# The extension user is a simple extension of the built-in user class, -# adding a required date_of_birth field. This allows us to check for -# any hard references to the name "User" in forms/handlers etc. - -class ExtensionUser(AbstractUser): - date_of_birth = models.DateField() - - custom_objects = UserManager() - - REQUIRED_FIELDS = AbstractUser.REQUIRED_FIELDS + ['date_of_birth'] - - class Meta: - app_label = 'auth' - - -# The CustomPermissionsUser users email as the identifier, but uses the normal -# Django permissions model. This allows us to check that the PermissionsMixin -# includes everything that is needed to interact with the ModelBackend. - -class CustomPermissionsUserManager(CustomUserManager): - def create_superuser(self, email, password, date_of_birth): - u = self.create_user(email, password=password, date_of_birth=date_of_birth) - u.is_superuser = True - u.save(using=self._db) - return u - - -class CustomPermissionsUser(AbstractBaseUser, PermissionsMixin): - email = models.EmailField(verbose_name='email address', max_length=255, unique=True) - date_of_birth = models.DateField() - - custom_objects = CustomPermissionsUserManager() - - USERNAME_FIELD = 'email' - REQUIRED_FIELDS = ['date_of_birth'] - - class Meta: - app_label = 'auth' - - def get_full_name(self): - return self.email - - def get_short_name(self): - return self.email - - def __unicode__(self): - return self.email - - -class IsActiveTestUser1(AbstractBaseUser): - """ - This test user class and derivatives test the default is_active behavior - """ - username = models.CharField(max_length=30, unique=True) - - custom_objects = BaseUserManager() - - USERNAME_FIELD = 'username' - - class Meta: - app_label = 'auth' - - # the is_active attr is provided by AbstractBaseUser - - -class CustomUserNonUniqueUsername(AbstractBaseUser): - "A user with a non-unique username" - username = models.CharField(max_length=30) - - USERNAME_FIELD = 'username' - - class Meta: - app_label = 'auth' - - -class CustomUserBadRequiredFields(AbstractBaseUser): - "A user with a non-unique username" - username = models.CharField(max_length=30, unique=True) - date_of_birth = models.DateField() - - USERNAME_FIELD = 'username' - REQUIRED_FIELDS = ['username', 'date_of_birth'] - - class Meta: - app_label = 'auth' diff --git a/django/contrib/auth/tests/decorators.py b/django/contrib/auth/tests/decorators.py deleted file mode 100644 index 5aff375498..0000000000 --- a/django/contrib/auth/tests/decorators.py +++ /dev/null @@ -1,48 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.contrib.auth.tests.views import AuthViewsTestCase -from django.contrib.auth.tests.utils import skipIfCustomUser - - -@skipIfCustomUser -class LoginRequiredTestCase(AuthViewsTestCase): - """ - Tests the login_required decorators - """ - urls = 'django.contrib.auth.tests.urls' - - def testCallable(self): - """ - Check that login_required is assignable to callable objects. - """ - class CallableView(object): - def __call__(self, *args, **kwargs): - pass - login_required(CallableView()) - - def testView(self): - """ - Check that login_required is assignable to normal views. - """ - def normal_view(request): - pass - login_required(normal_view) - - def testLoginRequired(self, view_url='/login_required/', login_url='/login/'): - """ - Check that login_required works on a simple view wrapped in a - login_required decorator. - """ - response = self.client.get(view_url) - self.assertEqual(response.status_code, 302) - self.assertTrue(login_url in response.url) - self.login() - response = self.client.get(view_url) - self.assertEqual(response.status_code, 200) - - def testLoginRequiredNextUrl(self): - """ - Check that login_required works on a simple view wrapped in a - login_required decorator with a login_url set. - """ - self.testLoginRequired(view_url='/login_required_login_url/', - login_url='/somewhere/') diff --git a/django/contrib/auth/tests/forms.py b/django/contrib/auth/tests/forms.py deleted file mode 100644 index 2c8f6e4faf..0000000000 --- a/django/contrib/auth/tests/forms.py +++ /dev/null @@ -1,411 +0,0 @@ -from __future__ import unicode_literals - -import os -from django.contrib.auth.models import User -from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm, - PasswordChangeForm, SetPasswordForm, UserChangeForm, PasswordResetForm, - ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget) -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.core import mail -from django.forms.fields import Field, CharField -from django.test import TestCase -from django.test.utils import override_settings -from django.utils.encoding import force_text -from django.utils._os import upath -from django.utils import translation -from django.utils.translation import ugettext as _ - - -@skipIfCustomUser -@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class UserCreationFormTest(TestCase): - - fixtures = ['authtestdata.json'] - - def test_user_already_exists(self): - data = { - 'username': 'testclient', - 'password1': 'test123', - 'password2': 'test123', - } - form = UserCreationForm(data) - self.assertFalse(form.is_valid()) - self.assertEqual(form["username"].errors, - [force_text(form.error_messages['duplicate_username'])]) - - def test_invalid_data(self): - data = { - 'username': 'jsmith!', - 'password1': 'test123', - 'password2': 'test123', - } - form = UserCreationForm(data) - self.assertFalse(form.is_valid()) - self.assertEqual(form["username"].errors, - [force_text(form.fields['username'].error_messages['invalid'])]) - - def test_password_verification(self): - # The verification password is incorrect. - data = { - 'username': 'jsmith', - 'password1': 'test123', - 'password2': 'test', - } - form = UserCreationForm(data) - self.assertFalse(form.is_valid()) - self.assertEqual(form["password2"].errors, - [force_text(form.error_messages['password_mismatch'])]) - - def test_both_passwords(self): - # One (or both) passwords weren't given - data = {'username': 'jsmith'} - form = UserCreationForm(data) - required_error = [force_text(Field.default_error_messages['required'])] - self.assertFalse(form.is_valid()) - self.assertEqual(form['password1'].errors, required_error) - self.assertEqual(form['password2'].errors, required_error) - - data['password2'] = 'test123' - form = UserCreationForm(data) - self.assertFalse(form.is_valid()) - self.assertEqual(form['password1'].errors, required_error) - self.assertEqual(form['password2'].errors, []) - - def test_success(self): - # The success case. - data = { - 'username': 'jsmith@example.com', - 'password1': 'test123', - 'password2': 'test123', - } - form = UserCreationForm(data) - self.assertTrue(form.is_valid()) - u = form.save() - self.assertEqual(repr(u), '') - - -@skipIfCustomUser -@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class AuthenticationFormTest(TestCase): - - fixtures = ['authtestdata.json'] - - def test_invalid_username(self): - # The user submits an invalid username. - - data = { - 'username': 'jsmith_does_not_exist', - 'password': 'test123', - } - form = AuthenticationForm(None, data) - self.assertFalse(form.is_valid()) - self.assertEqual(form.non_field_errors(), - [force_text(form.error_messages['invalid_login'] % { - 'username': User._meta.get_field('username').verbose_name - })]) - - def test_inactive_user(self): - # The user is inactive. - data = { - 'username': 'inactive', - 'password': 'password', - } - form = AuthenticationForm(None, data) - self.assertFalse(form.is_valid()) - self.assertEqual(form.non_field_errors(), - [force_text(form.error_messages['inactive'])]) - - def test_inactive_user_i18n(self): - with self.settings(USE_I18N=True): - with translation.override('pt-br', deactivate=True): - # The user is inactive. - data = { - 'username': 'inactive', - 'password': 'password', - } - form = AuthenticationForm(None, data) - self.assertFalse(form.is_valid()) - self.assertEqual(form.non_field_errors(), - [force_text(form.error_messages['inactive'])]) - - def test_success(self): - # The success case - data = { - 'username': 'testclient', - 'password': 'password', - } - form = AuthenticationForm(None, data) - self.assertTrue(form.is_valid()) - self.assertEqual(form.non_field_errors(), []) - - def test_username_field_label(self): - - class CustomAuthenticationForm(AuthenticationForm): - username = CharField(label="Name", max_length=75) - - form = CustomAuthenticationForm() - self.assertEqual(form['username'].label, "Name") - - -@skipIfCustomUser -@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class SetPasswordFormTest(TestCase): - - fixtures = ['authtestdata.json'] - - def test_password_verification(self): - # The two new passwords do not match. - user = User.objects.get(username='testclient') - data = { - 'new_password1': 'abc123', - 'new_password2': 'abc', - } - form = SetPasswordForm(user, data) - self.assertFalse(form.is_valid()) - self.assertEqual(form["new_password2"].errors, - [force_text(form.error_messages['password_mismatch'])]) - - def test_success(self): - user = User.objects.get(username='testclient') - data = { - 'new_password1': 'abc123', - 'new_password2': 'abc123', - } - form = SetPasswordForm(user, data) - self.assertTrue(form.is_valid()) - - -@skipIfCustomUser -@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class PasswordChangeFormTest(TestCase): - - fixtures = ['authtestdata.json'] - - def test_incorrect_password(self): - user = User.objects.get(username='testclient') - data = { - 'old_password': 'test', - 'new_password1': 'abc123', - 'new_password2': 'abc123', - } - form = PasswordChangeForm(user, data) - self.assertFalse(form.is_valid()) - self.assertEqual(form["old_password"].errors, - [force_text(form.error_messages['password_incorrect'])]) - - def test_password_verification(self): - # The two new passwords do not match. - user = User.objects.get(username='testclient') - data = { - 'old_password': 'password', - 'new_password1': 'abc123', - 'new_password2': 'abc', - } - form = PasswordChangeForm(user, data) - self.assertFalse(form.is_valid()) - self.assertEqual(form["new_password2"].errors, - [force_text(form.error_messages['password_mismatch'])]) - - def test_success(self): - # The success case. - user = User.objects.get(username='testclient') - data = { - 'old_password': 'password', - 'new_password1': 'abc123', - 'new_password2': 'abc123', - } - form = PasswordChangeForm(user, data) - self.assertTrue(form.is_valid()) - - def test_field_order(self): - # Regression test - check the order of fields: - user = User.objects.get(username='testclient') - self.assertEqual(list(PasswordChangeForm(user, {}).fields), - ['old_password', 'new_password1', 'new_password2']) - - -@skipIfCustomUser -@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class UserChangeFormTest(TestCase): - - fixtures = ['authtestdata.json'] - - def test_username_validity(self): - user = User.objects.get(username='testclient') - data = {'username': 'not valid'} - form = UserChangeForm(data, instance=user) - self.assertFalse(form.is_valid()) - self.assertEqual(form['username'].errors, - [force_text(form.fields['username'].error_messages['invalid'])]) - - def test_bug_14242(self): - # A regression test, introduce by adding an optimization for the - # UserChangeForm. - - class MyUserForm(UserChangeForm): - def __init__(self, *args, **kwargs): - super(MyUserForm, self).__init__(*args, **kwargs) - self.fields['groups'].help_text = 'These groups give users different permissions' - - class Meta(UserChangeForm.Meta): - fields = ('groups',) - - # Just check we can create it - form = MyUserForm({}) - - def test_unsuable_password(self): - user = User.objects.get(username='empty_password') - user.set_unusable_password() - user.save() - form = UserChangeForm(instance=user) - self.assertIn(_("No password set."), form.as_table()) - - def test_bug_17944_empty_password(self): - user = User.objects.get(username='empty_password') - form = UserChangeForm(instance=user) - self.assertIn(_("No password set."), form.as_table()) - - def test_bug_17944_unmanageable_password(self): - user = User.objects.get(username='unmanageable_password') - form = UserChangeForm(instance=user) - self.assertIn(_("Invalid password format or unknown hashing algorithm."), - form.as_table()) - - def test_bug_17944_unknown_password_algorithm(self): - user = User.objects.get(username='unknown_password') - form = UserChangeForm(instance=user) - self.assertIn(_("Invalid password format or unknown hashing algorithm."), - form.as_table()) - - def test_bug_19133(self): - "The change form does not return the password value" - # Use the form to construct the POST data - user = User.objects.get(username='testclient') - form_for_data = UserChangeForm(instance=user) - post_data = form_for_data.initial - - # The password field should be readonly, so anything - # posted here should be ignored; the form will be - # valid, and give back the 'initial' value for the - # password field. - post_data['password'] = 'new password' - form = UserChangeForm(instance=user, data=post_data) - - self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['password'], 'sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161') - - def test_bug_19349_bound_password_field(self): - user = User.objects.get(username='testclient') - form = UserChangeForm(data={}, instance=user) - # When rendering the bound password field, - # ReadOnlyPasswordHashWidget needs the initial - # value to render correctly - self.assertEqual(form.initial['password'], form['password'].value()) - - -@skipIfCustomUser -@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class PasswordResetFormTest(TestCase): - - fixtures = ['authtestdata.json'] - - def create_dummy_user(self): - """creates a user and returns a tuple - (user_object, username, email) - """ - username = 'jsmith' - email = 'jsmith@example.com' - user = User.objects.create_user(username, email, 'test123') - return (user, username, email) - - def test_invalid_email(self): - data = {'email': 'not valid'} - form = PasswordResetForm(data) - self.assertFalse(form.is_valid()) - self.assertEqual(form['email'].errors, [_('Enter a valid email address.')]) - - def test_nonexistant_email(self): - # Test nonexistant email address. This should not fail because it would - # expose information about registered users. - data = {'email': 'foo@bar.com'} - form = PasswordResetForm(data) - self.assertTrue(form.is_valid()) - self.assertEqual(len(mail.outbox), 0) - - @override_settings( - TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',), - TEMPLATE_DIRS=( - os.path.join(os.path.dirname(upath(__file__)), 'templates'), - ), - ) - def test_cleaned_data(self): - # Regression test - (user, username, email) = self.create_dummy_user() - data = {'email': email} - form = PasswordResetForm(data) - self.assertTrue(form.is_valid()) - form.save(domain_override='example.com') - self.assertEqual(form.cleaned_data['email'], email) - self.assertEqual(len(mail.outbox), 1) - - @override_settings( - TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',), - TEMPLATE_DIRS=( - os.path.join(os.path.dirname(upath(__file__)), 'templates'), - ), - ) - def test_custom_email_subject(self): - data = {'email': 'testclient@example.com'} - form = PasswordResetForm(data) - self.assertTrue(form.is_valid()) - # Since we're not providing a request object, we must provide a - # domain_override to prevent the save operation from failing in the - # potential case where contrib.sites is not installed. Refs #16412. - form.save(domain_override='example.com') - self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, 'Custom password reset on example.com') - - def test_bug_5605(self): - # bug #5605, preserve the case of the user name (before the @ in the - # email address) when creating a user. - user = User.objects.create_user('forms_test2', 'tesT@EXAMple.com', 'test') - self.assertEqual(user.email, 'tesT@example.com') - user = User.objects.create_user('forms_test3', 'tesT', 'test') - self.assertEqual(user.email, 'tesT') - - def test_inactive_user(self): - #tests that inactive user cannot - #receive password reset email - (user, username, email) = self.create_dummy_user() - user.is_active = False - user.save() - form = PasswordResetForm({'email': email}) - self.assertTrue(form.is_valid()) - self.assertEqual(len(mail.outbox), 0) - - def test_unusable_password(self): - user = User.objects.create_user('testuser', 'test@example.com', 'test') - data = {"email": "test@example.com"} - form = PasswordResetForm(data) - self.assertTrue(form.is_valid()) - user.set_unusable_password() - user.save() - form = PasswordResetForm(data) - # The form itself is valid, but no email is sent - self.assertTrue(form.is_valid()) - form.save() - self.assertEqual(len(mail.outbox), 0) - - -class ReadOnlyPasswordHashTest(TestCase): - - def test_bug_19349_render_with_none_value(self): - # Rendering the widget with value set to None - # mustn't raise an exception. - widget = ReadOnlyPasswordHashWidget() - html = widget.render(name='password', value=None, attrs={}) - self.assertIn(_("No password set."), html) - - def test_readonly_field_has_changed(self): - field = ReadOnlyPasswordHashField() - self.assertFalse(field._has_changed('aaa', 'bbb')) diff --git a/django/contrib/auth/tests/handlers.py b/django/contrib/auth/tests/handlers.py deleted file mode 100644 index 41063aaf4a..0000000000 --- a/django/contrib/auth/tests/handlers.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import unicode_literals - -from django.contrib.auth.handlers.modwsgi import check_password, groups_for_user -from django.contrib.auth.models import User, Group -from django.contrib.auth.tests import CustomUser -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.test import TransactionTestCase -from django.test.utils import override_settings - - -class ModWsgiHandlerTestCase(TransactionTestCase): - """ - Tests for the mod_wsgi authentication handler - """ - @skipIfCustomUser - def test_check_password(self): - """ - Verify that check_password returns the correct values as per - http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Authentication_Provider - """ - User.objects.create_user('test', 'test@example.com', 'test') - - # User not in database - self.assertTrue(check_password({}, 'unknown', '') is None) - - # Valid user with correct password - self.assertTrue(check_password({}, 'test', 'test')) - - # correct password, but user is inactive - User.objects.filter(username='test').update(is_active=False) - self.assertFalse(check_password({}, 'test', 'test')) - - # Valid user with incorrect password - self.assertFalse(check_password({}, 'test', 'incorrect')) - - @override_settings(AUTH_USER_MODEL='auth.CustomUser') - def test_check_password_custom_user(self): - """ - Verify that check_password returns the correct values as per - http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Authentication_Provider - - with custom user installed - """ - - CustomUser._default_manager.create_user('test@example.com', '1990-01-01', 'test') - - # User not in database - self.assertTrue(check_password({}, 'unknown', '') is None) - - # Valid user with correct password' - self.assertTrue(check_password({}, 'test@example.com', 'test')) - - # Valid user with incorrect password - self.assertFalse(check_password({}, 'test@example.com', 'incorrect')) - - @skipIfCustomUser - def test_groups_for_user(self): - """ - Check that groups_for_user returns correct values as per - http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Group_Authorisation - """ - user1 = User.objects.create_user('test', 'test@example.com', 'test') - User.objects.create_user('test1', 'test1@example.com', 'test1') - group = Group.objects.create(name='test_group') - user1.groups.add(group) - - # User not in database - self.assertEqual(groups_for_user({}, 'unknown'), []) - - self.assertEqual(groups_for_user({}, 'test'), [b'test_group']) - self.assertEqual(groups_for_user({}, 'test1'), []) diff --git a/django/contrib/auth/tests/hashers.py b/django/contrib/auth/tests/hashers.py deleted file mode 100644 index 9253fcbc43..0000000000 --- a/django/contrib/auth/tests/hashers.py +++ /dev/null @@ -1,180 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.conf.global_settings import PASSWORD_HASHERS as default_hashers -from django.contrib.auth.hashers import (is_password_usable, - check_password, make_password, PBKDF2PasswordHasher, load_hashers, - PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD) -from django.utils import unittest -from django.utils.unittest import skipUnless - - -try: - import crypt -except ImportError: - crypt = None - -try: - import bcrypt -except ImportError: - bcrypt = None - - -class TestUtilsHashPass(unittest.TestCase): - - def setUp(self): - load_hashers(password_hashers=default_hashers) - - def test_simple(self): - encoded = make_password('lètmein') - self.assertTrue(encoded.startswith('pbkdf2_sha256$')) - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - - def test_pkbdf2(self): - encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256') - self.assertEqual(encoded, - 'pbkdf2_sha256$10000$seasalt$CWWFdHOWwPnki7HvkcqN9iA2T3KLW1cf2uZ5kvArtVY=') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256") - - def test_sha1(self): - encoded = make_password('lètmein', 'seasalt', 'sha1') - self.assertEqual(encoded, - 'sha1$seasalt$cff36ea83f5706ce9aa7454e63e431fc726b2dc8') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "sha1") - - def test_md5(self): - encoded = make_password('lètmein', 'seasalt', 'md5') - self.assertEqual(encoded, - 'md5$seasalt$3f86d0d3d465b7b458c231bf3555c0e3') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "md5") - - def test_unsalted_md5(self): - encoded = make_password('lètmein', '', 'unsalted_md5') - self.assertEqual(encoded, '88a434c88cca4e900f7874cd98123f43') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5") - # Alternate unsalted syntax - alt_encoded = "md5$$%s" % encoded - self.assertTrue(is_password_usable(alt_encoded)) - self.assertTrue(check_password('lètmein', alt_encoded)) - self.assertFalse(check_password('lètmeinz', alt_encoded)) - - def test_unsalted_sha1(self): - encoded = make_password('lètmein', '', 'unsalted_sha1') - self.assertEqual(encoded, 'sha1$$6d138ca3ae545631b3abd71a4f076ce759c5700b') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_sha1") - # Raw SHA1 isn't acceptable - alt_encoded = encoded[6:] - self.assertFalse(check_password('lètmein', alt_encoded)) - - @skipUnless(crypt, "no crypt module to generate password.") - def test_crypt(self): - encoded = make_password('lètmei', 'ab', 'crypt') - self.assertEqual(encoded, 'crypt$$ab1Hv2Lg7ltQo') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(check_password('lètmei', encoded)) - self.assertFalse(check_password('lètmeiz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "crypt") - - @skipUnless(bcrypt, "py-bcrypt not installed") - def test_bcrypt_sha256(self): - encoded = make_password('lètmein', hasher='bcrypt_sha256') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(encoded.startswith('bcrypt_sha256$')) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt_sha256") - - # Verify that password truncation no longer works - password = ('VSK0UYV6FFQVZ0KG88DYN9WADAADZO1CTSIVDJUNZSUML6IBX7LN7ZS3R5' - 'JGB3RGZ7VI7G7DJQ9NI8BQFSRPTG6UWTTVESA5ZPUN') - encoded = make_password(password, hasher='bcrypt_sha256') - self.assertTrue(check_password(password, encoded)) - self.assertFalse(check_password(password[:72], encoded)) - - @skipUnless(bcrypt, "py-bcrypt not installed") - def test_bcrypt(self): - encoded = make_password('lètmein', hasher='bcrypt') - self.assertTrue(is_password_usable(encoded)) - self.assertTrue(encoded.startswith('bcrypt$')) - self.assertTrue(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt") - - def test_unusable(self): - encoded = make_password(None) - self.assertFalse(is_password_usable(encoded)) - self.assertFalse(check_password(None, encoded)) - self.assertFalse(check_password(UNUSABLE_PASSWORD, encoded)) - self.assertFalse(check_password('', encoded)) - self.assertFalse(check_password('lètmein', encoded)) - self.assertFalse(check_password('lètmeinz', encoded)) - self.assertRaises(ValueError, identify_hasher, encoded) - - def test_bad_algorithm(self): - def doit(): - make_password('lètmein', hasher='lolcat') - self.assertRaises(ValueError, doit) - self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash") - - def test_bad_encoded(self): - self.assertFalse(is_password_usable('lètmein_badencoded')) - self.assertFalse(is_password_usable('')) - - def test_low_level_pkbdf2(self): - hasher = PBKDF2PasswordHasher() - encoded = hasher.encode('lètmein', 'seasalt') - self.assertEqual(encoded, - 'pbkdf2_sha256$10000$seasalt$CWWFdHOWwPnki7HvkcqN9iA2T3KLW1cf2uZ5kvArtVY=') - self.assertTrue(hasher.verify('lètmein', encoded)) - - def test_low_level_pbkdf2_sha1(self): - hasher = PBKDF2SHA1PasswordHasher() - encoded = hasher.encode('lètmein', 'seasalt') - self.assertEqual(encoded, - 'pbkdf2_sha1$10000$seasalt$oAfF6vgs95ncksAhGXOWf4Okq7o=') - self.assertTrue(hasher.verify('lètmein', encoded)) - - def test_upgrade(self): - self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) - for algo in ('sha1', 'md5'): - encoded = make_password('lètmein', hasher=algo) - state = {'upgraded': False} - def setter(password): - state['upgraded'] = True - self.assertTrue(check_password('lètmein', encoded, setter)) - self.assertTrue(state['upgraded']) - - def test_no_upgrade(self): - encoded = make_password('lètmein') - state = {'upgraded': False} - def setter(): - state['upgraded'] = True - self.assertFalse(check_password('WRONG', encoded, setter)) - self.assertFalse(state['upgraded']) - - def test_no_upgrade_on_incorrect_pass(self): - self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) - for algo in ('sha1', 'md5'): - encoded = make_password('lètmein', hasher=algo) - state = {'upgraded': False} - def setter(): - state['upgraded'] = True - self.assertFalse(check_password('WRONG', encoded, setter)) - self.assertFalse(state['upgraded']) diff --git a/django/contrib/auth/tests/management.py b/django/contrib/auth/tests/management.py deleted file mode 100644 index 687a5c31cb..0000000000 --- a/django/contrib/auth/tests/management.py +++ /dev/null @@ -1,228 +0,0 @@ -from __future__ import unicode_literals -from datetime import date - -from django.contrib.auth import models, management -from django.contrib.auth.management import create_permissions -from django.contrib.auth.management.commands import changepassword -from django.contrib.auth.models import User -from django.contrib.auth.tests import CustomUser -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.core.management import call_command -from django.core.management.base import CommandError -from django.core.management.validation import get_validation_errors -from django.db.models.loading import get_app -from django.test import TestCase -from django.test.utils import override_settings -from django.utils import six -from django.utils.six import StringIO - - -@skipIfCustomUser -class GetDefaultUsernameTestCase(TestCase): - - def setUp(self): - self.old_get_system_username = management.get_system_username - - def tearDown(self): - management.get_system_username = self.old_get_system_username - - def test_actual_implementation(self): - self.assertIsInstance(management.get_system_username(), six.text_type) - - def test_simple(self): - management.get_system_username = lambda: 'joe' - self.assertEqual(management.get_default_username(), 'joe') - - def test_existing(self): - models.User.objects.create(username='joe') - management.get_system_username = lambda: 'joe' - self.assertEqual(management.get_default_username(), '') - self.assertEqual( - management.get_default_username(check_db=False), 'joe') - - def test_i18n(self): - # 'Julia' with accented 'u': - management.get_system_username = lambda: 'J\xfalia' - self.assertEqual(management.get_default_username(), 'julia') - - -@skipIfCustomUser -class ChangepasswordManagementCommandTestCase(TestCase): - - def setUp(self): - self.user = models.User.objects.create_user(username='joe', password='qwerty') - self.stdout = StringIO() - self.stderr = StringIO() - - def tearDown(self): - self.stdout.close() - self.stderr.close() - - def test_that_changepassword_command_changes_joes_password(self): - "Executing the changepassword management command should change joe's password" - self.assertTrue(self.user.check_password('qwerty')) - command = changepassword.Command() - command._get_pass = lambda *args: 'not qwerty' - - command.execute("joe", stdout=self.stdout) - command_output = self.stdout.getvalue().strip() - - self.assertEqual(command_output, "Changing password for user 'joe'\nPassword changed successfully for user 'joe'") - self.assertTrue(models.User.objects.get(username="joe").check_password("not qwerty")) - - def test_that_max_tries_exits_1(self): - """ - A CommandError should be thrown by handle() if the user enters in - mismatched passwords three times. - """ - command = changepassword.Command() - command._get_pass = lambda *args: args or 'foo' - - with self.assertRaises(CommandError): - command.execute("joe", stdout=self.stdout, stderr=self.stderr) - - -@skipIfCustomUser -class CreatesuperuserManagementCommandTestCase(TestCase): - - def test_createsuperuser(self): - "Check the operation of the createsuperuser management command" - # We can use the management command to create a superuser - new_io = StringIO() - call_command("createsuperuser", - interactive=False, - username="joe", - email="joe@somewhere.org", - stdout=new_io - ) - command_output = new_io.getvalue().strip() - self.assertEqual(command_output, 'Superuser created successfully.') - u = User.objects.get(username="joe") - self.assertEqual(u.email, 'joe@somewhere.org') - - # created password should be unusable - self.assertFalse(u.has_usable_password()) - - def test_verbosity_zero(self): - # We can supress output on the management command - new_io = StringIO() - call_command("createsuperuser", - interactive=False, - username="joe2", - email="joe2@somewhere.org", - verbosity=0, - stdout=new_io - ) - command_output = new_io.getvalue().strip() - self.assertEqual(command_output, '') - u = User.objects.get(username="joe2") - self.assertEqual(u.email, 'joe2@somewhere.org') - self.assertFalse(u.has_usable_password()) - - def test_email_in_username(self): - new_io = StringIO() - call_command("createsuperuser", - interactive=False, - username="joe+admin@somewhere.org", - email="joe@somewhere.org", - stdout=new_io - ) - u = User._default_manager.get(username="joe+admin@somewhere.org") - self.assertEqual(u.email, 'joe@somewhere.org') - self.assertFalse(u.has_usable_password()) - - @override_settings(AUTH_USER_MODEL='auth.CustomUser') - def test_swappable_user(self): - "A superuser can be created when a custom User model is in use" - # We can use the management command to create a superuser - # We skip validation because the temporary substitution of the - # swappable User model messes with validation. - new_io = StringIO() - call_command("createsuperuser", - interactive=False, - email="joe@somewhere.org", - date_of_birth="1976-04-01", - stdout=new_io, - skip_validation=True - ) - command_output = new_io.getvalue().strip() - self.assertEqual(command_output, 'Superuser created successfully.') - u = CustomUser._default_manager.get(email="joe@somewhere.org") - self.assertEqual(u.date_of_birth, date(1976, 4, 1)) - - # created password should be unusable - self.assertFalse(u.has_usable_password()) - - @override_settings(AUTH_USER_MODEL='auth.CustomUser') - def test_swappable_user_missing_required_field(self): - "A Custom superuser won't be created when a required field isn't provided" - # We can use the management command to create a superuser - # We skip validation because the temporary substitution of the - # swappable User model messes with validation. - new_io = StringIO() - with self.assertRaises(CommandError): - call_command("createsuperuser", - interactive=False, - username="joe@somewhere.org", - stdout=new_io, - stderr=new_io, - skip_validation=True - ) - - self.assertEqual(CustomUser._default_manager.count(), 0) - - -class CustomUserModelValidationTestCase(TestCase): - @override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields') - def test_username_not_in_required_fields(self): - "USERNAME_FIELD should not appear in REQUIRED_FIELDS." - new_io = StringIO() - get_validation_errors(new_io, get_app('auth')) - self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue()) - - @override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername') - def test_username_non_unique(self): - "A non-unique USERNAME_FIELD should raise a model validation error." - new_io = StringIO() - get_validation_errors(new_io, get_app('auth')) - self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue()) - - -class PermissionDuplicationTestCase(TestCase): - - def setUp(self): - self._original_permissions = models.Permission._meta.permissions[:] - - def tearDown(self): - models.Permission._meta.permissions = self._original_permissions - - def test_duplicated_permissions(self): - """ - Test that we show proper error message if we are trying to create - duplicate permissions. - """ - # check duplicated default permission - models.Permission._meta.permissions = [ - ('change_permission', 'Can edit permission (duplicate)')] - six.assertRaisesRegex(self, CommandError, - "The permission codename 'change_permission' clashes with a " - "builtin permission for model 'auth.Permission'.", - create_permissions, models, [], verbosity=0) - - # check duplicated custom permissions - models.Permission._meta.permissions = [ - ('my_custom_permission', 'Some permission'), - ('other_one', 'Some other permission'), - ('my_custom_permission', 'Some permission with duplicate permission code'), - ] - six.assertRaisesRegex(self, CommandError, - "The permission codename 'my_custom_permission' is duplicated for model " - "'auth.Permission'.", - create_permissions, models, [], verbosity=0) - - # should not raise anything - models.Permission._meta.permissions = [ - ('my_custom_permission', 'Some permission'), - ('other_one', 'Some other permission'), - ] - create_permissions(models, [], verbosity=0) diff --git a/django/contrib/auth/tests/models.py b/django/contrib/auth/tests/models.py deleted file mode 100644 index 8ac0599e6b..0000000000 --- a/django/contrib/auth/tests/models.py +++ /dev/null @@ -1,142 +0,0 @@ -import warnings - -from django.conf import settings -from django.contrib.auth import get_user_model -from django.contrib.auth.models import (Group, User, SiteProfileNotAvailable, - UserManager) -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.test import TestCase -from django.test.utils import override_settings -from django.utils import six - - -@skipIfCustomUser -@override_settings(USE_TZ=False, AUTH_PROFILE_MODULE='') -class ProfileTestCase(TestCase): - - def test_site_profile_not_available(self): - user = User.objects.create(username='testclient') - - # calling get_profile without AUTH_PROFILE_MODULE set - del settings.AUTH_PROFILE_MODULE - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - with six.assertRaisesRegex(self, SiteProfileNotAvailable, - "You need to set AUTH_PROFILE_MODULE in your project"): - user.get_profile() - - # Bad syntax in AUTH_PROFILE_MODULE: - settings.AUTH_PROFILE_MODULE = 'foobar' - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - with six.assertRaisesRegex(self, SiteProfileNotAvailable, - "app_label and model_name should be separated by a dot"): - user.get_profile() - - # module that doesn't exist - settings.AUTH_PROFILE_MODULE = 'foo.bar' - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - with six.assertRaisesRegex(self, SiteProfileNotAvailable, - "Unable to load the profile model"): - user.get_profile() - - -@skipIfCustomUser -@override_settings(USE_TZ=False) -class NaturalKeysTestCase(TestCase): - fixtures = ['authtestdata.json'] - - def test_user_natural_key(self): - staff_user = User.objects.get(username='staff') - self.assertEqual(User.objects.get_by_natural_key('staff'), staff_user) - self.assertEqual(staff_user.natural_key(), ('staff',)) - - def test_group_natural_key(self): - users_group = Group.objects.create(name='users') - self.assertEqual(Group.objects.get_by_natural_key('users'), users_group) - - -@skipIfCustomUser -@override_settings(USE_TZ=False) -class LoadDataWithoutNaturalKeysTestCase(TestCase): - fixtures = ['regular.json'] - - def test_user_is_created_and_added_to_group(self): - user = User.objects.get(username='my_username') - group = Group.objects.get(name='my_group') - self.assertEqual(group, user.groups.get()) - - -@skipIfCustomUser -@override_settings(USE_TZ=False) -class LoadDataWithNaturalKeysTestCase(TestCase): - fixtures = ['natural.json'] - - def test_user_is_created_and_added_to_group(self): - user = User.objects.get(username='my_username') - group = Group.objects.get(name='my_group') - self.assertEqual(group, user.groups.get()) - - -@skipIfCustomUser -class UserManagerTestCase(TestCase): - - def test_create_user(self): - email_lowercase = 'normal@normal.com' - user = User.objects.create_user('user', email_lowercase) - self.assertEqual(user.email, email_lowercase) - self.assertEqual(user.username, 'user') - self.assertEqual(user.password, '!') - - def test_create_user_email_domain_normalize_rfc3696(self): - # According to http://tools.ietf.org/html/rfc3696#section-3 - # the "@" symbol can be part of the local part of an email address - returned = UserManager.normalize_email(r'Abc\@DEF@EXAMPLE.com') - self.assertEqual(returned, r'Abc\@DEF@example.com') - - def test_create_user_email_domain_normalize(self): - returned = UserManager.normalize_email('normal@DOMAIN.COM') - self.assertEqual(returned, 'normal@domain.com') - - def test_create_user_email_domain_normalize_with_whitespace(self): - returned = UserManager.normalize_email('email\ with_whitespace@D.COM') - self.assertEqual(returned, 'email\ with_whitespace@d.com') - - def test_empty_username(self): - self.assertRaisesMessage(ValueError, - 'The given username must be set', - User.objects.create_user, username='') - - -class IsActiveTestCase(TestCase): - """ - Tests the behavior of the guaranteed is_active attribute - """ - - @skipIfCustomUser - def test_builtin_user_isactive(self): - user = User.objects.create(username='foo', email='foo@bar.com') - # is_active is true by default - self.assertEqual(user.is_active, True) - user.is_active = False - user.save() - user_fetched = User.objects.get(pk=user.pk) - # the is_active flag is saved - self.assertFalse(user_fetched.is_active) - - @override_settings(AUTH_USER_MODEL='auth.IsActiveTestUser1') - def test_is_active_field_default(self): - """ - tests that the default value for is_active is provided - """ - UserModel = get_user_model() - user = UserModel(username='foo') - self.assertEqual(user.is_active, True) - # you can set the attribute - but it will not save - user.is_active = False - # there should be no problem saving - but the attribute is not saved - user.save() - user_fetched = UserModel._default_manager.get(pk=user.pk) - # the attribute is always true for newly retrieved instance - self.assertEqual(user_fetched.is_active, True) diff --git a/django/contrib/auth/tests/remote_user.py b/django/contrib/auth/tests/remote_user.py deleted file mode 100644 index 0e59b291a8..0000000000 --- a/django/contrib/auth/tests/remote_user.py +++ /dev/null @@ -1,198 +0,0 @@ -from datetime import datetime - -from django.conf import settings -from django.contrib.auth import authenticate -from django.contrib.auth.backends import RemoteUserBackend -from django.contrib.auth.models import User, AnonymousUser -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.test import TestCase -from django.utils import timezone - - -@skipIfCustomUser -class RemoteUserTest(TestCase): - - urls = 'django.contrib.auth.tests.urls' - middleware = 'django.contrib.auth.middleware.RemoteUserMiddleware' - backend = 'django.contrib.auth.backends.RemoteUserBackend' - - # Usernames to be passed in REMOTE_USER for the test_known_user test case. - known_user = 'knownuser' - known_user2 = 'knownuser2' - - def setUp(self): - self.curr_middleware = settings.MIDDLEWARE_CLASSES - self.curr_auth = settings.AUTHENTICATION_BACKENDS - settings.MIDDLEWARE_CLASSES += (self.middleware,) - settings.AUTHENTICATION_BACKENDS += (self.backend,) - - def test_no_remote_user(self): - """ - Tests requests where no remote user is specified and insures that no - users get created. - """ - num_users = User.objects.count() - - response = self.client.get('/remote_user/') - self.assertTrue(response.context['user'].is_anonymous()) - self.assertEqual(User.objects.count(), num_users) - - response = self.client.get('/remote_user/', REMOTE_USER=None) - self.assertTrue(response.context['user'].is_anonymous()) - self.assertEqual(User.objects.count(), num_users) - - response = self.client.get('/remote_user/', REMOTE_USER='') - self.assertTrue(response.context['user'].is_anonymous()) - self.assertEqual(User.objects.count(), num_users) - - def test_unknown_user(self): - """ - Tests the case where the username passed in the header does not exist - as a User. - """ - num_users = User.objects.count() - response = self.client.get('/remote_user/', REMOTE_USER='newuser') - self.assertEqual(response.context['user'].username, 'newuser') - self.assertEqual(User.objects.count(), num_users + 1) - User.objects.get(username='newuser') - - # Another request with same user should not create any new users. - response = self.client.get('/remote_user/', REMOTE_USER='newuser') - self.assertEqual(User.objects.count(), num_users + 1) - - def test_known_user(self): - """ - Tests the case where the username passed in the header is a valid User. - """ - User.objects.create(username='knownuser') - User.objects.create(username='knownuser2') - num_users = User.objects.count() - response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) - self.assertEqual(response.context['user'].username, 'knownuser') - self.assertEqual(User.objects.count(), num_users) - # Test that a different user passed in the headers causes the new user - # to be logged in. - response = self.client.get('/remote_user/', REMOTE_USER=self.known_user2) - self.assertEqual(response.context['user'].username, 'knownuser2') - self.assertEqual(User.objects.count(), num_users) - - def test_last_login(self): - """ - Tests that a user's last_login is set the first time they make a - request but not updated in subsequent requests with the same session. - """ - user = User.objects.create(username='knownuser') - # Set last_login to something so we can determine if it changes. - default_login = datetime(2000, 1, 1) - if settings.USE_TZ: - default_login = default_login.replace(tzinfo=timezone.utc) - user.last_login = default_login - user.save() - - response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) - self.assertNotEqual(default_login, response.context['user'].last_login) - - user = User.objects.get(username='knownuser') - user.last_login = default_login - user.save() - response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) - self.assertEqual(default_login, response.context['user'].last_login) - - def test_header_disappears(self): - """ - Tests that a logged in user is logged out automatically when - the REMOTE_USER header disappears during the same browser session. - """ - User.objects.create(username='knownuser') - # Known user authenticates - response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) - self.assertEqual(response.context['user'].username, 'knownuser') - # During the session, the REMOTE_USER header disappears. Should trigger logout. - response = self.client.get('/remote_user/') - self.assertEqual(response.context['user'].is_anonymous(), True) - # verify the remoteuser middleware will not remove a user - # authenticated via another backend - User.objects.create_user(username='modeluser', password='foo') - self.client.login(username='modeluser', password='foo') - authenticate(username='modeluser', password='foo') - response = self.client.get('/remote_user/') - self.assertEqual(response.context['user'].username, 'modeluser') - - def tearDown(self): - """Restores settings to avoid breaking other tests.""" - settings.MIDDLEWARE_CLASSES = self.curr_middleware - settings.AUTHENTICATION_BACKENDS = self.curr_auth - - -class RemoteUserNoCreateBackend(RemoteUserBackend): - """Backend that doesn't create unknown users.""" - create_unknown_user = False - - -@skipIfCustomUser -class RemoteUserNoCreateTest(RemoteUserTest): - """ - Contains the same tests as RemoteUserTest, but using a custom auth backend - class that doesn't create unknown users. - """ - - backend =\ - 'django.contrib.auth.tests.remote_user.RemoteUserNoCreateBackend' - - def test_unknown_user(self): - num_users = User.objects.count() - response = self.client.get('/remote_user/', REMOTE_USER='newuser') - self.assertTrue(response.context['user'].is_anonymous()) - self.assertEqual(User.objects.count(), num_users) - - -class CustomRemoteUserBackend(RemoteUserBackend): - """ - Backend that overrides RemoteUserBackend methods. - """ - - def clean_username(self, username): - """ - Grabs username before the @ character. - """ - return username.split('@')[0] - - def configure_user(self, user): - """ - Sets user's email address. - """ - user.email = 'user@example.com' - user.save() - return user - - -@skipIfCustomUser -class RemoteUserCustomTest(RemoteUserTest): - """ - Tests a custom RemoteUserBackend subclass that overrides the clean_username - and configure_user methods. - """ - - backend =\ - 'django.contrib.auth.tests.remote_user.CustomRemoteUserBackend' - # REMOTE_USER strings with email addresses for the custom backend to - # clean. - known_user = 'knownuser@example.com' - known_user2 = 'knownuser2@example.com' - - def test_known_user(self): - """ - The strings passed in REMOTE_USER should be cleaned and the known users - should not have been configured with an email address. - """ - super(RemoteUserCustomTest, self).test_known_user() - self.assertEqual(User.objects.get(username='knownuser').email, '') - self.assertEqual(User.objects.get(username='knownuser2').email, '') - - def test_unknown_user(self): - """ - The unknown user created should be configured with an email address. - """ - super(RemoteUserCustomTest, self).test_unknown_user() - newuser = User.objects.get(username='newuser') - self.assertEqual(newuser.email, 'user@example.com') diff --git a/django/contrib/auth/tests/signals.py b/django/contrib/auth/tests/signals.py deleted file mode 100644 index 024f44f547..0000000000 --- a/django/contrib/auth/tests/signals.py +++ /dev/null @@ -1,80 +0,0 @@ -from django.contrib.auth import signals -from django.contrib.auth.models import User -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.test import TestCase -from django.test.client import RequestFactory -from django.test.utils import override_settings - - -@skipIfCustomUser -@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class SignalTestCase(TestCase): - urls = 'django.contrib.auth.tests.urls' - fixtures = ['authtestdata.json'] - - def listener_login(self, user, **kwargs): - self.logged_in.append(user) - - def listener_logout(self, user, **kwargs): - self.logged_out.append(user) - - def listener_login_failed(self, sender, credentials, **kwargs): - self.login_failed.append(credentials) - - def setUp(self): - """Set up the listeners and reset the logged in/logged out counters""" - self.logged_in = [] - self.logged_out = [] - self.login_failed = [] - signals.user_logged_in.connect(self.listener_login) - signals.user_logged_out.connect(self.listener_logout) - signals.user_login_failed.connect(self.listener_login_failed) - - def tearDown(self): - """Disconnect the listeners""" - signals.user_logged_in.disconnect(self.listener_login) - signals.user_logged_out.disconnect(self.listener_logout) - signals.user_login_failed.disconnect(self.listener_login_failed) - - def test_login(self): - # Only a successful login will trigger the success signal. - self.client.login(username='testclient', password='bad') - self.assertEqual(len(self.logged_in), 0) - self.assertEqual(len(self.login_failed), 1) - self.assertEqual(self.login_failed[0]['username'], 'testclient') - # verify the password is cleansed - self.assertTrue('***' in self.login_failed[0]['password']) - - # Like this: - self.client.login(username='testclient', password='password') - self.assertEqual(len(self.logged_in), 1) - self.assertEqual(self.logged_in[0].username, 'testclient') - - # Ensure there were no more failures. - self.assertEqual(len(self.login_failed), 1) - - def test_logout_anonymous(self): - # The log_out function will still trigger the signal for anonymous - # users. - self.client.get('/logout/next_page/') - self.assertEqual(len(self.logged_out), 1) - self.assertEqual(self.logged_out[0], None) - - def test_logout(self): - self.client.login(username='testclient', password='password') - self.client.get('/logout/next_page/') - self.assertEqual(len(self.logged_out), 1) - self.assertEqual(self.logged_out[0].username, 'testclient') - - def test_update_last_login(self): - """Ensure that only `last_login` is updated in `update_last_login`""" - user = User.objects.get(pk=3) - old_last_login = user.last_login - - user.username = "This username shouldn't get saved" - request = RequestFactory().get('/login') - signals.user_logged_in.send(sender=user.__class__, request=request, - user=user) - user = User.objects.get(pk=3) - self.assertEqual(user.username, 'staff') - self.assertNotEqual(user.last_login, old_last_login) diff --git a/django/contrib/auth/tests/test_auth_backends.py b/django/contrib/auth/tests/test_auth_backends.py new file mode 100644 index 0000000000..bb97c54a11 --- /dev/null +++ b/django/contrib/auth/tests/test_auth_backends.py @@ -0,0 +1,404 @@ +from __future__ import unicode_literals +from datetime import date + +from django.conf import settings +from django.contrib.auth.models import User, Group, Permission, AnonymousUser +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.contrib.auth.tests.test_custom_user import ExtensionUser, CustomPermissionsUser, CustomUser +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ImproperlyConfigured, PermissionDenied +from django.contrib.auth import authenticate +from django.test import TestCase +from django.test.utils import override_settings + + +class BaseModelBackendTest(object): + """ + 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.curr_auth = settings.AUTHENTICATION_BACKENDS + settings.AUTHENTICATION_BACKENDS = (self.backend,) + self.create_users() + + def tearDown(self): + settings.AUTHENTICATION_BACKENDS = self.curr_auth + # 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.assertEqual(user.has_perm('auth.test'), False) + user.is_staff = True + user.save() + self.assertEqual(user.has_perm('auth.test'), False) + user.is_superuser = True + user.save() + self.assertEqual(user.has_perm('auth.test'), True) + user.is_staff = False + user.is_superuser = False + user.save() + self.assertEqual(user.has_perm('auth.test'), False) + user.is_staff = True + user.is_superuser = True + user.is_active = False + user.save() + self.assertEqual(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) + user.save() + + # reloading user to purge the _perm_cache + user = self.UserModel._default_manager.get(pk=self.user.pk) + self.assertEqual(user.get_all_permissions() == set(['auth.test']), True) + self.assertEqual(user.get_group_permissions(), set([])) + self.assertEqual(user.has_module_perms('Group'), False) + self.assertEqual(user.has_module_perms('auth'), True) + perm = Permission.objects.create(name='test2', content_type=content_type, codename='test2') + user.user_permissions.add(perm) + user.save() + perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3') + user.user_permissions.add(perm) + user.save() + user = self.UserModel._default_manager.get(pk=self.user.pk) + self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3'])) + self.assertEqual(user.has_perm('test'), False) + self.assertEqual(user.has_perm('auth.test'), True) + self.assertEqual(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) + group.save() + user.groups.add(group) + user = self.UserModel._default_manager.get(pk=self.user.pk) + exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group']) + self.assertEqual(user.get_all_permissions(), exp) + self.assertEqual(user.get_group_permissions(), set(['auth.test_group'])) + self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True) + + user = AnonymousUser() + self.assertEqual(user.has_perm('test'), False) + self.assertEqual(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) + user.save() + + self.assertEqual(user.has_perm('auth.test', 'object'), False) + self.assertEqual(user.get_all_permissions('object'), set([])) + self.assertEqual(user.has_perm('auth.test'), True) + self.assertEqual(user.get_all_permissions(), set(['auth.test'])) + + 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())) + + +@skipIfCustomUser +class ModelBackendTest(BaseModelBackendTest, TestCase): + """ + Tests for the ModelBackend using the default User model. + """ + UserModel = User + + def create_users(self): + self.user = User.objects.create_user( + username='test', + email='test@example.com', + password='test', + ) + self.superuser = User.objects.create_superuser( + username='test2', + email='test2@example.com', + password='test', + ) + + +@override_settings(AUTH_USER_MODEL='auth.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.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.CustomUser') +class CustomUserModelBackendAuthenticateTest(TestCase): + """ + Tests that 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) + + + +class TestObj(object): + pass + + +class SimpleRowlevelBackend(object): + 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): + if not user.is_anonymous() and not user.is_active: + return False + return 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'] + + +@skipIfCustomUser +class RowlevelBackendTest(TestCase): + """ + Tests for auth backend that supports object level permissions + """ + backend = 'django.contrib.auth.tests.test_auth_backends.SimpleRowlevelBackend' + + def setUp(self): + self.curr_auth = settings.AUTHENTICATION_BACKENDS + settings.AUTHENTICATION_BACKENDS = tuple(self.curr_auth) + (self.backend,) + self.user1 = User.objects.create_user('test', 'test@example.com', 'test') + self.user2 = User.objects.create_user('test2', 'test2@example.com', 'test') + self.user3 = User.objects.create_user('test3', 'test3@example.com', 'test') + + def tearDown(self): + settings.AUTHENTICATION_BACKENDS = self.curr_auth + # 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.assertEqual(self.user1.has_perm('perm', TestObj()), False) + self.assertEqual(self.user2.has_perm('perm', TestObj()), True) + self.assertEqual(self.user2.has_perm('perm'), False) + self.assertEqual(self.user2.has_perms(['simple', 'advanced'], TestObj()), True) + self.assertEqual(self.user3.has_perm('perm', TestObj()), False) + self.assertEqual(self.user3.has_perm('anon', TestObj()), False) + self.assertEqual(self.user3.has_perms(['simple', 'advanced'], TestObj()), False) + + def test_get_all_permissions(self): + self.assertEqual(self.user1.get_all_permissions(TestObj()), set(['simple'])) + self.assertEqual(self.user2.get_all_permissions(TestObj()), set(['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()), set(['group_perm'])) + + +class AnonymousUserBackendTest(TestCase): + """ + Tests for AnonymousUser delegating to backend. + """ + + backend = 'django.contrib.auth.tests.test_auth_backends.SimpleRowlevelBackend' + + def setUp(self): + self.curr_auth = settings.AUTHENTICATION_BACKENDS + settings.AUTHENTICATION_BACKENDS = (self.backend,) + self.user1 = AnonymousUser() + + def tearDown(self): + settings.AUTHENTICATION_BACKENDS = self.curr_auth + + def test_has_perm(self): + self.assertEqual(self.user1.has_perm('perm', TestObj()), False) + self.assertEqual(self.user1.has_perm('anon', TestObj()), True) + + def test_has_perms(self): + self.assertEqual(self.user1.has_perms(['anon'], TestObj()), True) + self.assertEqual(self.user1.has_perms(['anon', 'perm'], TestObj()), False) + + def test_has_module_perms(self): + self.assertEqual(self.user1.has_module_perms("app1"), True) + self.assertEqual(self.user1.has_module_perms("app2"), False) + + def test_get_all_permissions(self): + self.assertEqual(self.user1.get_all_permissions(TestObj()), set(['anon'])) + + +@skipIfCustomUser +@override_settings(AUTHENTICATION_BACKENDS=[]) +class NoBackendsTest(TestCase): + """ + Tests that an appropriate error is raised if no auth backends are provided. + """ + def setUp(self): + self.user = User.objects.create_user('test', 'test@example.com', 'test') + + def test_raises_exception(self): + self.assertRaises(ImproperlyConfigured, self.user.has_perm, ('perm', TestObj(),)) + + +@skipIfCustomUser +class InActiveUserBackendTest(TestCase): + """ + Tests for a inactive user + """ + backend = 'django.contrib.auth.tests.test_auth_backends.SimpleRowlevelBackend' + + def setUp(self): + self.curr_auth = settings.AUTHENTICATION_BACKENDS + settings.AUTHENTICATION_BACKENDS = (self.backend,) + self.user1 = User.objects.create_user('test', 'test@example.com', 'test') + self.user1.is_active = False + self.user1.save() + + def tearDown(self): + settings.AUTHENTICATION_BACKENDS = self.curr_auth + + def test_has_perm(self): + self.assertEqual(self.user1.has_perm('perm', TestObj()), False) + self.assertEqual(self.user1.has_perm('inactive', TestObj()), True) + + def test_has_module_perms(self): + self.assertEqual(self.user1.has_module_perms("app1"), False) + self.assertEqual(self.user1.has_module_perms("app2"), False) + + +class PermissionDeniedBackend(object): + """ + Always raises PermissionDenied. + """ + supports_object_permissions = True + supports_anonymous_user = True + supports_inactive_user = True + + def authenticate(self, username=None, password=None): + raise PermissionDenied + + +@skipIfCustomUser +class PermissionDeniedBackendTest(TestCase): + """ + Tests that other backends are not checked once a backend raises PermissionDenied + """ + backend = 'django.contrib.auth.tests.test_auth_backends.PermissionDeniedBackend' + + def setUp(self): + self.user1 = User.objects.create_user('test', 'test@example.com', 'test') + self.user1.save() + + @override_settings(AUTHENTICATION_BACKENDS=(backend, ) + + tuple(settings.AUTHENTICATION_BACKENDS)) + def test_permission_denied(self): + "user is not authenticated after a backend raises permission denied #2550" + self.assertEqual(authenticate(username='test', password='test'), None) + + @override_settings(AUTHENTICATION_BACKENDS=tuple( + settings.AUTHENTICATION_BACKENDS) + (backend, )) + def test_authenticates(self): + self.assertEqual(authenticate(username='test', password='test'), self.user1) diff --git a/django/contrib/auth/tests/test_basic.py b/django/contrib/auth/tests/test_basic.py new file mode 100644 index 0000000000..6b1dd3af05 --- /dev/null +++ b/django/contrib/auth/tests/test_basic.py @@ -0,0 +1,257 @@ +# -*- encoding: utf-8 -*- +from __future__ import unicode_literals + +import locale + +from django.contrib.auth import get_user_model +from django.contrib.auth.management.commands import createsuperuser +from django.contrib.auth.models import User, AnonymousUser +from django.contrib.auth.tests.test_custom_user import CustomUser +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.core.exceptions import ImproperlyConfigured +from django.core.management import call_command +from django.dispatch import receiver +from django.test import TestCase +from django.test.signals import setting_changed +from django.test.utils import override_settings +from django.utils import translation +from django.utils.encoding import force_str +from django.utils.six import binary_type, PY3, StringIO + + +@receiver(setting_changed) +def user_model_swapped(**kwargs): + if kwargs['setting'] == 'AUTH_USER_MODEL': + from django.db.models.manager import ensure_default_manager + from django.contrib.auth.models import User + # Reset User manager + setattr(User, 'objects', User._default_manager) + ensure_default_manager(User) + + +def mock_inputs(inputs): + """ + Decorator to temporarily replace input/getpass to allow interactive + createsuperuser. + """ + def inner(test_func): + def wrapped(*args): + class mock_getpass: + @staticmethod + def getpass(prompt=b'Password: ', stream=None): + if not PY3: + # getpass on Windows only supports prompt as bytestring (#19807) + assert isinstance(prompt, binary_type) + return inputs['password'] + + def mock_input(prompt): + # prompt should be encoded in Python 2. This line will raise an + # Exception if prompt contains unencoded non-ascii on Python 2. + prompt = str(prompt) + assert str('__proxy__') not in prompt + response = '' + for key, val in inputs.items(): + if force_str(key) in prompt.lower(): + response = val + break + return response + + old_getpass = createsuperuser.getpass + old_input = createsuperuser.input + createsuperuser.getpass = mock_getpass + createsuperuser.input = mock_input + try: + test_func(*args) + finally: + createsuperuser.getpass = old_getpass + createsuperuser.input = old_input + return wrapped + return inner + + +@skipIfCustomUser +class BasicTestCase(TestCase): + def test_user(self): + "Check that users can be created and can set their password" + u = User.objects.create_user('testuser', 'test@example.com', 'testpw') + self.assertTrue(u.has_usable_password()) + self.assertFalse(u.check_password('bad')) + self.assertTrue(u.check_password('testpw')) + + # Check we can manually set an unusable password + u.set_unusable_password() + u.save() + self.assertFalse(u.check_password('testpw')) + self.assertFalse(u.has_usable_password()) + u.set_password('testpw') + self.assertTrue(u.check_password('testpw')) + u.set_password(None) + self.assertFalse(u.has_usable_password()) + + # Check authentication/permissions + self.assertTrue(u.is_authenticated()) + self.assertFalse(u.is_staff) + self.assertTrue(u.is_active) + self.assertFalse(u.is_superuser) + + # Check API-based user creation with no password + u2 = User.objects.create_user('testuser2', 'test2@example.com') + self.assertFalse(u2.has_usable_password()) + + def test_user_no_email(self): + "Check that users can be created without an email" + u = User.objects.create_user('testuser1') + self.assertEqual(u.email, '') + + u2 = User.objects.create_user('testuser2', email='') + self.assertEqual(u2.email, '') + + u3 = User.objects.create_user('testuser3', email=None) + self.assertEqual(u3.email, '') + + def test_anonymous_user(self): + "Check the properties of the anonymous user" + a = AnonymousUser() + self.assertEqual(a.pk, None) + self.assertFalse(a.is_authenticated()) + self.assertFalse(a.is_staff) + self.assertFalse(a.is_active) + self.assertFalse(a.is_superuser) + self.assertEqual(a.groups.all().count(), 0) + self.assertEqual(a.user_permissions.all().count(), 0) + + def test_superuser(self): + "Check the creation and properties of a superuser" + super = User.objects.create_superuser('super', 'super@example.com', 'super') + self.assertTrue(super.is_superuser) + self.assertTrue(super.is_active) + self.assertTrue(super.is_staff) + + def test_createsuperuser_management_command(self): + "Check the operation of the createsuperuser management command" + # We can use the management command to create a superuser + new_io = StringIO() + call_command("createsuperuser", + interactive=False, + username="joe", + email="joe@somewhere.org", + stdout=new_io + ) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, 'Superuser created successfully.') + u = User.objects.get(username="joe") + self.assertEqual(u.email, 'joe@somewhere.org') + + # created password should be unusable + self.assertFalse(u.has_usable_password()) + + # We can supress output on the management command + new_io = StringIO() + call_command("createsuperuser", + interactive=False, + username="joe2", + email="joe2@somewhere.org", + verbosity=0, + stdout=new_io + ) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, '') + u = User.objects.get(username="joe2") + self.assertEqual(u.email, 'joe2@somewhere.org') + self.assertFalse(u.has_usable_password()) + + call_command("createsuperuser", + interactive=False, + username="joe+admin@somewhere.org", + email="joe@somewhere.org", + verbosity=0 + ) + u = User.objects.get(username="joe+admin@somewhere.org") + self.assertEqual(u.email, 'joe@somewhere.org') + self.assertFalse(u.has_usable_password()) + + @mock_inputs({'password': "nopasswd"}) + def test_createsuperuser_nolocale(self): + """ + Check that createsuperuser does not break when no locale is set. See + ticket #16017. + """ + + old_getdefaultlocale = locale.getdefaultlocale + try: + # Temporarily remove locale information + locale.getdefaultlocale = lambda: (None, None) + + # Call the command in this new environment + call_command("createsuperuser", + interactive=True, + username="nolocale@somewhere.org", + email="nolocale@somewhere.org", + verbosity=0 + ) + + except TypeError: + self.fail("createsuperuser fails if the OS provides no information about the current locale") + + finally: + # Re-apply locale information + locale.getdefaultlocale = old_getdefaultlocale + + # If we were successful, a user should have been created + u = User.objects.get(username="nolocale@somewhere.org") + self.assertEqual(u.email, 'nolocale@somewhere.org') + + @mock_inputs({ + 'password': "nopasswd", + 'uživatel': 'foo', # username (cz) + 'email': 'nolocale@somewhere.org'}) + def test_createsuperuser_non_ascii_verbose_name(self): + # Aliased so the string doesn't get extracted + from django.utils.translation import ugettext_lazy as ulazy + username_field = User._meta.get_field('username') + old_verbose_name = username_field.verbose_name + username_field.verbose_name = ulazy('uživatel') + new_io = StringIO() + try: + call_command("createsuperuser", + interactive=True, + stdout=new_io + ) + finally: + username_field.verbose_name = old_verbose_name + + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, 'Superuser created successfully.') + + def test_get_user_model(self): + "The current user model can be retrieved" + self.assertEqual(get_user_model(), User) + + @override_settings(AUTH_USER_MODEL='auth.CustomUser') + def test_swappable_user(self): + "The current user model can be swapped out for another" + self.assertEqual(get_user_model(), CustomUser) + with self.assertRaises(AttributeError): + User.objects.all() + + @override_settings(AUTH_USER_MODEL='badsetting') + def test_swappable_user_bad_setting(self): + "The alternate user setting must point to something in the format app.model" + with self.assertRaises(ImproperlyConfigured): + get_user_model() + + @override_settings(AUTH_USER_MODEL='thismodel.doesntexist') + def test_swappable_user_nonexistent_model(self): + "The current user model must point to an installed model" + with self.assertRaises(ImproperlyConfigured): + get_user_model() + + @skipIfCustomUser + def test_user_verbose_names_translatable(self): + "Default User model verbose names are translatable (#19945)" + with translation.override('en'): + self.assertEqual(User._meta.verbose_name, 'user') + self.assertEqual(User._meta.verbose_name_plural, 'users') + with translation.override('es'): + self.assertEqual(User._meta.verbose_name, 'usuario') + self.assertEqual(User._meta.verbose_name_plural, 'usuarios') diff --git a/django/contrib/auth/tests/test_context_processors.py b/django/contrib/auth/tests/test_context_processors.py new file mode 100644 index 0000000000..9e56cfce85 --- /dev/null +++ b/django/contrib/auth/tests/test_context_processors.py @@ -0,0 +1,170 @@ +import os + +from django.conf import global_settings +from django.contrib.auth import authenticate +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.contrib.auth.models import User, Permission +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth.context_processors import PermWrapper, PermLookupDict +from django.db.models import Q +from django.test import TestCase +from django.test.utils import override_settings +from django.utils._os import upath + + +class MockUser(object): + def has_module_perms(self, perm): + if perm == 'mockapp': + return True + return False + + def has_perm(self, perm): + if perm == 'mockapp.someperm': + return True + return False + + +class PermWrapperTests(TestCase): + """ + Test some details of the PermWrapper implementation. + """ + class EQLimiterObject(object): + """ + This object makes sure __eq__ will not be called endlessly. + """ + def __init__(self): + self.eq_calls = 0 + + def __eq__(self, other): + if self.eq_calls > 0: + return True + self.eq_calls += 1 + return False + + def test_permwrapper_in(self): + """ + Test that 'something' in PermWrapper works as expected. + """ + perms = PermWrapper(MockUser()) + # Works for modules and full permissions. + self.assertTrue('mockapp' in perms) + self.assertFalse('nonexisting' in perms) + self.assertTrue('mockapp.someperm' in perms) + self.assertFalse('mockapp.nonexisting' in perms) + + def test_permlookupdict_in(self): + """ + No endless loops if accessed with 'in' - refs #18979. + """ + pldict = PermLookupDict(MockUser(), 'mockapp') + with self.assertRaises(TypeError): + self.EQLimiterObject() in pldict + + +@skipIfCustomUser +@override_settings( + TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',), + TEMPLATE_DIRS=( + os.path.join(os.path.dirname(upath(__file__)), 'templates'), + ), + USE_TZ=False, # required for loading the fixture + PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), +) +class AuthContextProcessorTests(TestCase): + """ + Tests for the ``django.contrib.auth.context_processors.auth`` processor + """ + urls = 'django.contrib.auth.tests.urls' + fixtures = ['context-processors-users.xml'] + + @override_settings( + MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES, + TEMPLATE_CONTEXT_PROCESSORS=global_settings.TEMPLATE_CONTEXT_PROCESSORS, + ) + def test_session_not_accessed(self): + """ + Tests that the session is not accessed simply by including + the auth context processor + """ + response = self.client.get('/auth_processor_no_attr_access/') + self.assertContains(response, "Session not accessed") + + @override_settings( + MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES, + TEMPLATE_CONTEXT_PROCESSORS=global_settings.TEMPLATE_CONTEXT_PROCESSORS, + ) + def test_session_is_accessed(self): + """ + Tests that the session is accessed if the auth context processor + is used and relevant attributes accessed. + """ + response = self.client.get('/auth_processor_attr_access/') + self.assertContains(response, "Session accessed") + + def test_perms_attrs(self): + u = User.objects.create_user(username='normal', password='secret') + u.user_permissions.add( + Permission.objects.get( + content_type=ContentType.objects.get_for_model(Permission), + codename='add_permission')) + self.client.login(username='normal', password='secret') + response = self.client.get('/auth_processor_perms/') + self.assertContains(response, "Has auth permissions") + self.assertContains(response, "Has auth.add_permission permissions") + self.assertNotContains(response, "nonexisting") + + def test_perm_in_perms_attrs(self): + u = User.objects.create_user(username='normal', password='secret') + u.user_permissions.add( + Permission.objects.get( + content_type=ContentType.objects.get_for_model(Permission), + codename='add_permission')) + self.client.login(username='normal', password='secret') + response = self.client.get('/auth_processor_perm_in_perms/') + self.assertContains(response, "Has auth permissions") + self.assertContains(response, "Has auth.add_permission permissions") + self.assertNotContains(response, "nonexisting") + + def test_message_attrs(self): + self.client.login(username='super', password='secret') + response = self.client.get('/auth_processor_messages/') + self.assertContains(response, "Message 1") + + def test_user_attrs(self): + """ + Test that the lazy objects returned behave just like the wrapped objects. + """ + # These are 'functional' level tests for common use cases. Direct + # testing of the implementation (SimpleLazyObject) is in the 'utils' + # tests. + self.client.login(username='super', password='secret') + user = authenticate(username='super', password='secret') + response = self.client.get('/auth_processor_user/') + self.assertContains(response, "unicode: super") + self.assertContains(response, "id: 100") + self.assertContains(response, "username: super") + # bug #12037 is tested by the {% url %} in the template: + self.assertContains(response, "url: /userpage/super/") + + # See if this object can be used for queries where a Q() comparing + # a user can be used with another Q() (in an AND or OR fashion). + # This simulates what a template tag might do with the user from the + # context. Note that we don't need to execute a query, just build it. + # + # The failure case (bug #12049) on Python 2.4 with a LazyObject-wrapped + # User is a fatal TypeError: "function() takes at least 2 arguments + # (0 given)" deep inside deepcopy(). + # + # Python 2.5 and 2.6 succeeded, but logged internally caught exception + # spew: + # + # Exception RuntimeError: 'maximum recursion depth exceeded while + # calling a Python object' in + # ignored" + query = Q(user=response.context['user']) & Q(someflag=True) + + # Tests for user equality. This is hard because User defines + # equality in a non-duck-typing way + # See bug #12060 + self.assertEqual(response.context['user'], user) + self.assertEqual(user, response.context['user']) diff --git a/django/contrib/auth/tests/test_custom_user.py b/django/contrib/auth/tests/test_custom_user.py new file mode 100644 index 0000000000..0d324f0953 --- /dev/null +++ b/django/contrib/auth/tests/test_custom_user.py @@ -0,0 +1,168 @@ +from django.db import models +from django.contrib.auth.models import ( + BaseUserManager, + AbstractBaseUser, + AbstractUser, + UserManager, + PermissionsMixin +) + + +# The custom User uses email as the unique identifier, and requires +# that every user provide a date of birth. This lets us test +# changes in username datatype, and non-text required fields. + +class CustomUserManager(BaseUserManager): + def create_user(self, email, date_of_birth, password=None): + """ + Creates and saves a User with the given email and password. + """ + if not email: + raise ValueError('Users must have an email address') + + user = self.model( + email=CustomUserManager.normalize_email(email), + date_of_birth=date_of_birth, + ) + + user.set_password(password) + user.save(using=self._db) + return user + + def create_superuser(self, email, password, date_of_birth): + u = self.create_user(email, password=password, date_of_birth=date_of_birth) + u.is_admin = True + u.save(using=self._db) + return u + + +class CustomUser(AbstractBaseUser): + email = models.EmailField(verbose_name='email address', max_length=255, unique=True) + is_active = models.BooleanField(default=True) + is_admin = models.BooleanField(default=False) + date_of_birth = models.DateField() + + custom_objects = CustomUserManager() + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['date_of_birth'] + + class Meta: + app_label = 'auth' + + def get_full_name(self): + return self.email + + def get_short_name(self): + return self.email + + def __unicode__(self): + return self.email + + # Maybe required? + def get_group_permissions(self, obj=None): + return set() + + def get_all_permissions(self, obj=None): + return set() + + def has_perm(self, perm, obj=None): + return True + + def has_perms(self, perm_list, obj=None): + return True + + def has_module_perms(self, app_label): + return True + + # Admin required fields + @property + def is_staff(self): + return self.is_admin + + +# The extension user is a simple extension of the built-in user class, +# adding a required date_of_birth field. This allows us to check for +# any hard references to the name "User" in forms/handlers etc. + +class ExtensionUser(AbstractUser): + date_of_birth = models.DateField() + + custom_objects = UserManager() + + REQUIRED_FIELDS = AbstractUser.REQUIRED_FIELDS + ['date_of_birth'] + + class Meta: + app_label = 'auth' + + +# The CustomPermissionsUser users email as the identifier, but uses the normal +# Django permissions model. This allows us to check that the PermissionsMixin +# includes everything that is needed to interact with the ModelBackend. + +class CustomPermissionsUserManager(CustomUserManager): + def create_superuser(self, email, password, date_of_birth): + u = self.create_user(email, password=password, date_of_birth=date_of_birth) + u.is_superuser = True + u.save(using=self._db) + return u + + +class CustomPermissionsUser(AbstractBaseUser, PermissionsMixin): + email = models.EmailField(verbose_name='email address', max_length=255, unique=True) + date_of_birth = models.DateField() + + custom_objects = CustomPermissionsUserManager() + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['date_of_birth'] + + class Meta: + app_label = 'auth' + + def get_full_name(self): + return self.email + + def get_short_name(self): + return self.email + + def __unicode__(self): + return self.email + + +class IsActiveTestUser1(AbstractBaseUser): + """ + This test user class and derivatives test the default is_active behavior + """ + username = models.CharField(max_length=30, unique=True) + + custom_objects = BaseUserManager() + + USERNAME_FIELD = 'username' + + class Meta: + app_label = 'auth' + + # the is_active attr is provided by AbstractBaseUser + + +class CustomUserNonUniqueUsername(AbstractBaseUser): + "A user with a non-unique username" + username = models.CharField(max_length=30) + + USERNAME_FIELD = 'username' + + class Meta: + app_label = 'auth' + + +class CustomUserBadRequiredFields(AbstractBaseUser): + "A user with a non-unique username" + username = models.CharField(max_length=30, unique=True) + date_of_birth = models.DateField() + + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['username', 'date_of_birth'] + + class Meta: + app_label = 'auth' diff --git a/django/contrib/auth/tests/test_decorators.py b/django/contrib/auth/tests/test_decorators.py new file mode 100644 index 0000000000..a53dcd5bb7 --- /dev/null +++ b/django/contrib/auth/tests/test_decorators.py @@ -0,0 +1,48 @@ +from django.contrib.auth.decorators import login_required +from django.contrib.auth.tests.test_views import AuthViewsTestCase +from django.contrib.auth.tests.utils import skipIfCustomUser + + +@skipIfCustomUser +class LoginRequiredTestCase(AuthViewsTestCase): + """ + Tests the login_required decorators + """ + urls = 'django.contrib.auth.tests.urls' + + def testCallable(self): + """ + Check that login_required is assignable to callable objects. + """ + class CallableView(object): + def __call__(self, *args, **kwargs): + pass + login_required(CallableView()) + + def testView(self): + """ + Check that login_required is assignable to normal views. + """ + def normal_view(request): + pass + login_required(normal_view) + + def testLoginRequired(self, view_url='/login_required/', login_url='/login/'): + """ + Check that login_required works on a simple view wrapped in a + login_required decorator. + """ + response = self.client.get(view_url) + self.assertEqual(response.status_code, 302) + self.assertTrue(login_url in response.url) + self.login() + response = self.client.get(view_url) + self.assertEqual(response.status_code, 200) + + def testLoginRequiredNextUrl(self): + """ + Check that login_required works on a simple view wrapped in a + login_required decorator with a login_url set. + """ + self.testLoginRequired(view_url='/login_required_login_url/', + login_url='/somewhere/') diff --git a/django/contrib/auth/tests/test_forms.py b/django/contrib/auth/tests/test_forms.py new file mode 100644 index 0000000000..2c8f6e4faf --- /dev/null +++ b/django/contrib/auth/tests/test_forms.py @@ -0,0 +1,411 @@ +from __future__ import unicode_literals + +import os +from django.contrib.auth.models import User +from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm, + PasswordChangeForm, SetPasswordForm, UserChangeForm, PasswordResetForm, + ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget) +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.core import mail +from django.forms.fields import Field, CharField +from django.test import TestCase +from django.test.utils import override_settings +from django.utils.encoding import force_text +from django.utils._os import upath +from django.utils import translation +from django.utils.translation import ugettext as _ + + +@skipIfCustomUser +@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class UserCreationFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_user_already_exists(self): + data = { + 'username': 'testclient', + 'password1': 'test123', + 'password2': 'test123', + } + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["username"].errors, + [force_text(form.error_messages['duplicate_username'])]) + + def test_invalid_data(self): + data = { + 'username': 'jsmith!', + 'password1': 'test123', + 'password2': 'test123', + } + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["username"].errors, + [force_text(form.fields['username'].error_messages['invalid'])]) + + def test_password_verification(self): + # The verification password is incorrect. + data = { + 'username': 'jsmith', + 'password1': 'test123', + 'password2': 'test', + } + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["password2"].errors, + [force_text(form.error_messages['password_mismatch'])]) + + def test_both_passwords(self): + # One (or both) passwords weren't given + data = {'username': 'jsmith'} + form = UserCreationForm(data) + required_error = [force_text(Field.default_error_messages['required'])] + self.assertFalse(form.is_valid()) + self.assertEqual(form['password1'].errors, required_error) + self.assertEqual(form['password2'].errors, required_error) + + data['password2'] = 'test123' + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form['password1'].errors, required_error) + self.assertEqual(form['password2'].errors, []) + + def test_success(self): + # The success case. + data = { + 'username': 'jsmith@example.com', + 'password1': 'test123', + 'password2': 'test123', + } + form = UserCreationForm(data) + self.assertTrue(form.is_valid()) + u = form.save() + self.assertEqual(repr(u), '') + + +@skipIfCustomUser +@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class AuthenticationFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_invalid_username(self): + # The user submits an invalid username. + + data = { + 'username': 'jsmith_does_not_exist', + 'password': 'test123', + } + form = AuthenticationForm(None, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form.non_field_errors(), + [force_text(form.error_messages['invalid_login'] % { + 'username': User._meta.get_field('username').verbose_name + })]) + + def test_inactive_user(self): + # The user is inactive. + data = { + 'username': 'inactive', + 'password': 'password', + } + form = AuthenticationForm(None, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form.non_field_errors(), + [force_text(form.error_messages['inactive'])]) + + def test_inactive_user_i18n(self): + with self.settings(USE_I18N=True): + with translation.override('pt-br', deactivate=True): + # The user is inactive. + data = { + 'username': 'inactive', + 'password': 'password', + } + form = AuthenticationForm(None, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form.non_field_errors(), + [force_text(form.error_messages['inactive'])]) + + def test_success(self): + # The success case + data = { + 'username': 'testclient', + 'password': 'password', + } + form = AuthenticationForm(None, data) + self.assertTrue(form.is_valid()) + self.assertEqual(form.non_field_errors(), []) + + def test_username_field_label(self): + + class CustomAuthenticationForm(AuthenticationForm): + username = CharField(label="Name", max_length=75) + + form = CustomAuthenticationForm() + self.assertEqual(form['username'].label, "Name") + + +@skipIfCustomUser +@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class SetPasswordFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_password_verification(self): + # The two new passwords do not match. + user = User.objects.get(username='testclient') + data = { + 'new_password1': 'abc123', + 'new_password2': 'abc', + } + form = SetPasswordForm(user, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["new_password2"].errors, + [force_text(form.error_messages['password_mismatch'])]) + + def test_success(self): + user = User.objects.get(username='testclient') + data = { + 'new_password1': 'abc123', + 'new_password2': 'abc123', + } + form = SetPasswordForm(user, data) + self.assertTrue(form.is_valid()) + + +@skipIfCustomUser +@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class PasswordChangeFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_incorrect_password(self): + user = User.objects.get(username='testclient') + data = { + 'old_password': 'test', + 'new_password1': 'abc123', + 'new_password2': 'abc123', + } + form = PasswordChangeForm(user, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["old_password"].errors, + [force_text(form.error_messages['password_incorrect'])]) + + def test_password_verification(self): + # The two new passwords do not match. + user = User.objects.get(username='testclient') + data = { + 'old_password': 'password', + 'new_password1': 'abc123', + 'new_password2': 'abc', + } + form = PasswordChangeForm(user, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["new_password2"].errors, + [force_text(form.error_messages['password_mismatch'])]) + + def test_success(self): + # The success case. + user = User.objects.get(username='testclient') + data = { + 'old_password': 'password', + 'new_password1': 'abc123', + 'new_password2': 'abc123', + } + form = PasswordChangeForm(user, data) + self.assertTrue(form.is_valid()) + + def test_field_order(self): + # Regression test - check the order of fields: + user = User.objects.get(username='testclient') + self.assertEqual(list(PasswordChangeForm(user, {}).fields), + ['old_password', 'new_password1', 'new_password2']) + + +@skipIfCustomUser +@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class UserChangeFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_username_validity(self): + user = User.objects.get(username='testclient') + data = {'username': 'not valid'} + form = UserChangeForm(data, instance=user) + self.assertFalse(form.is_valid()) + self.assertEqual(form['username'].errors, + [force_text(form.fields['username'].error_messages['invalid'])]) + + def test_bug_14242(self): + # A regression test, introduce by adding an optimization for the + # UserChangeForm. + + class MyUserForm(UserChangeForm): + def __init__(self, *args, **kwargs): + super(MyUserForm, self).__init__(*args, **kwargs) + self.fields['groups'].help_text = 'These groups give users different permissions' + + class Meta(UserChangeForm.Meta): + fields = ('groups',) + + # Just check we can create it + form = MyUserForm({}) + + def test_unsuable_password(self): + user = User.objects.get(username='empty_password') + user.set_unusable_password() + user.save() + form = UserChangeForm(instance=user) + self.assertIn(_("No password set."), form.as_table()) + + def test_bug_17944_empty_password(self): + user = User.objects.get(username='empty_password') + form = UserChangeForm(instance=user) + self.assertIn(_("No password set."), form.as_table()) + + def test_bug_17944_unmanageable_password(self): + user = User.objects.get(username='unmanageable_password') + form = UserChangeForm(instance=user) + self.assertIn(_("Invalid password format or unknown hashing algorithm."), + form.as_table()) + + def test_bug_17944_unknown_password_algorithm(self): + user = User.objects.get(username='unknown_password') + form = UserChangeForm(instance=user) + self.assertIn(_("Invalid password format or unknown hashing algorithm."), + form.as_table()) + + def test_bug_19133(self): + "The change form does not return the password value" + # Use the form to construct the POST data + user = User.objects.get(username='testclient') + form_for_data = UserChangeForm(instance=user) + post_data = form_for_data.initial + + # The password field should be readonly, so anything + # posted here should be ignored; the form will be + # valid, and give back the 'initial' value for the + # password field. + post_data['password'] = 'new password' + form = UserChangeForm(instance=user, data=post_data) + + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['password'], 'sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161') + + def test_bug_19349_bound_password_field(self): + user = User.objects.get(username='testclient') + form = UserChangeForm(data={}, instance=user) + # When rendering the bound password field, + # ReadOnlyPasswordHashWidget needs the initial + # value to render correctly + self.assertEqual(form.initial['password'], form['password'].value()) + + +@skipIfCustomUser +@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class PasswordResetFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def create_dummy_user(self): + """creates a user and returns a tuple + (user_object, username, email) + """ + username = 'jsmith' + email = 'jsmith@example.com' + user = User.objects.create_user(username, email, 'test123') + return (user, username, email) + + def test_invalid_email(self): + data = {'email': 'not valid'} + form = PasswordResetForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form['email'].errors, [_('Enter a valid email address.')]) + + def test_nonexistant_email(self): + # Test nonexistant email address. This should not fail because it would + # expose information about registered users. + data = {'email': 'foo@bar.com'} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + self.assertEqual(len(mail.outbox), 0) + + @override_settings( + TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',), + TEMPLATE_DIRS=( + os.path.join(os.path.dirname(upath(__file__)), 'templates'), + ), + ) + def test_cleaned_data(self): + # Regression test + (user, username, email) = self.create_dummy_user() + data = {'email': email} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + form.save(domain_override='example.com') + self.assertEqual(form.cleaned_data['email'], email) + self.assertEqual(len(mail.outbox), 1) + + @override_settings( + TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',), + TEMPLATE_DIRS=( + os.path.join(os.path.dirname(upath(__file__)), 'templates'), + ), + ) + def test_custom_email_subject(self): + data = {'email': 'testclient@example.com'} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + # Since we're not providing a request object, we must provide a + # domain_override to prevent the save operation from failing in the + # potential case where contrib.sites is not installed. Refs #16412. + form.save(domain_override='example.com') + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].subject, 'Custom password reset on example.com') + + def test_bug_5605(self): + # bug #5605, preserve the case of the user name (before the @ in the + # email address) when creating a user. + user = User.objects.create_user('forms_test2', 'tesT@EXAMple.com', 'test') + self.assertEqual(user.email, 'tesT@example.com') + user = User.objects.create_user('forms_test3', 'tesT', 'test') + self.assertEqual(user.email, 'tesT') + + def test_inactive_user(self): + #tests that inactive user cannot + #receive password reset email + (user, username, email) = self.create_dummy_user() + user.is_active = False + user.save() + form = PasswordResetForm({'email': email}) + self.assertTrue(form.is_valid()) + self.assertEqual(len(mail.outbox), 0) + + def test_unusable_password(self): + user = User.objects.create_user('testuser', 'test@example.com', 'test') + data = {"email": "test@example.com"} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + user.set_unusable_password() + user.save() + form = PasswordResetForm(data) + # The form itself is valid, but no email is sent + self.assertTrue(form.is_valid()) + form.save() + self.assertEqual(len(mail.outbox), 0) + + +class ReadOnlyPasswordHashTest(TestCase): + + def test_bug_19349_render_with_none_value(self): + # Rendering the widget with value set to None + # mustn't raise an exception. + widget = ReadOnlyPasswordHashWidget() + html = widget.render(name='password', value=None, attrs={}) + self.assertIn(_("No password set."), html) + + def test_readonly_field_has_changed(self): + field = ReadOnlyPasswordHashField() + self.assertFalse(field._has_changed('aaa', 'bbb')) diff --git a/django/contrib/auth/tests/test_handlers.py b/django/contrib/auth/tests/test_handlers.py new file mode 100644 index 0000000000..41063aaf4a --- /dev/null +++ b/django/contrib/auth/tests/test_handlers.py @@ -0,0 +1,71 @@ +from __future__ import unicode_literals + +from django.contrib.auth.handlers.modwsgi import check_password, groups_for_user +from django.contrib.auth.models import User, Group +from django.contrib.auth.tests import CustomUser +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.test import TransactionTestCase +from django.test.utils import override_settings + + +class ModWsgiHandlerTestCase(TransactionTestCase): + """ + Tests for the mod_wsgi authentication handler + """ + @skipIfCustomUser + def test_check_password(self): + """ + Verify that check_password returns the correct values as per + http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Authentication_Provider + """ + User.objects.create_user('test', 'test@example.com', 'test') + + # User not in database + self.assertTrue(check_password({}, 'unknown', '') is None) + + # Valid user with correct password + self.assertTrue(check_password({}, 'test', 'test')) + + # correct password, but user is inactive + User.objects.filter(username='test').update(is_active=False) + self.assertFalse(check_password({}, 'test', 'test')) + + # Valid user with incorrect password + self.assertFalse(check_password({}, 'test', 'incorrect')) + + @override_settings(AUTH_USER_MODEL='auth.CustomUser') + def test_check_password_custom_user(self): + """ + Verify that check_password returns the correct values as per + http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Authentication_Provider + + with custom user installed + """ + + CustomUser._default_manager.create_user('test@example.com', '1990-01-01', 'test') + + # User not in database + self.assertTrue(check_password({}, 'unknown', '') is None) + + # Valid user with correct password' + self.assertTrue(check_password({}, 'test@example.com', 'test')) + + # Valid user with incorrect password + self.assertFalse(check_password({}, 'test@example.com', 'incorrect')) + + @skipIfCustomUser + def test_groups_for_user(self): + """ + Check that groups_for_user returns correct values as per + http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Group_Authorisation + """ + user1 = User.objects.create_user('test', 'test@example.com', 'test') + User.objects.create_user('test1', 'test1@example.com', 'test1') + group = Group.objects.create(name='test_group') + user1.groups.add(group) + + # User not in database + self.assertEqual(groups_for_user({}, 'unknown'), []) + + self.assertEqual(groups_for_user({}, 'test'), [b'test_group']) + self.assertEqual(groups_for_user({}, 'test1'), []) diff --git a/django/contrib/auth/tests/test_hashers.py b/django/contrib/auth/tests/test_hashers.py new file mode 100644 index 0000000000..9253fcbc43 --- /dev/null +++ b/django/contrib/auth/tests/test_hashers.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.conf.global_settings import PASSWORD_HASHERS as default_hashers +from django.contrib.auth.hashers import (is_password_usable, + check_password, make_password, PBKDF2PasswordHasher, load_hashers, + PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD) +from django.utils import unittest +from django.utils.unittest import skipUnless + + +try: + import crypt +except ImportError: + crypt = None + +try: + import bcrypt +except ImportError: + bcrypt = None + + +class TestUtilsHashPass(unittest.TestCase): + + def setUp(self): + load_hashers(password_hashers=default_hashers) + + def test_simple(self): + encoded = make_password('lètmein') + self.assertTrue(encoded.startswith('pbkdf2_sha256$')) + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + + def test_pkbdf2(self): + encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256') + self.assertEqual(encoded, + 'pbkdf2_sha256$10000$seasalt$CWWFdHOWwPnki7HvkcqN9iA2T3KLW1cf2uZ5kvArtVY=') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256") + + def test_sha1(self): + encoded = make_password('lètmein', 'seasalt', 'sha1') + self.assertEqual(encoded, + 'sha1$seasalt$cff36ea83f5706ce9aa7454e63e431fc726b2dc8') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "sha1") + + def test_md5(self): + encoded = make_password('lètmein', 'seasalt', 'md5') + self.assertEqual(encoded, + 'md5$seasalt$3f86d0d3d465b7b458c231bf3555c0e3') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "md5") + + def test_unsalted_md5(self): + encoded = make_password('lètmein', '', 'unsalted_md5') + self.assertEqual(encoded, '88a434c88cca4e900f7874cd98123f43') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5") + # Alternate unsalted syntax + alt_encoded = "md5$$%s" % encoded + self.assertTrue(is_password_usable(alt_encoded)) + self.assertTrue(check_password('lètmein', alt_encoded)) + self.assertFalse(check_password('lètmeinz', alt_encoded)) + + def test_unsalted_sha1(self): + encoded = make_password('lètmein', '', 'unsalted_sha1') + self.assertEqual(encoded, 'sha1$$6d138ca3ae545631b3abd71a4f076ce759c5700b') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_sha1") + # Raw SHA1 isn't acceptable + alt_encoded = encoded[6:] + self.assertFalse(check_password('lètmein', alt_encoded)) + + @skipUnless(crypt, "no crypt module to generate password.") + def test_crypt(self): + encoded = make_password('lètmei', 'ab', 'crypt') + self.assertEqual(encoded, 'crypt$$ab1Hv2Lg7ltQo') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(check_password('lètmei', encoded)) + self.assertFalse(check_password('lètmeiz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "crypt") + + @skipUnless(bcrypt, "py-bcrypt not installed") + def test_bcrypt_sha256(self): + encoded = make_password('lètmein', hasher='bcrypt_sha256') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(encoded.startswith('bcrypt_sha256$')) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt_sha256") + + # Verify that password truncation no longer works + password = ('VSK0UYV6FFQVZ0KG88DYN9WADAADZO1CTSIVDJUNZSUML6IBX7LN7ZS3R5' + 'JGB3RGZ7VI7G7DJQ9NI8BQFSRPTG6UWTTVESA5ZPUN') + encoded = make_password(password, hasher='bcrypt_sha256') + self.assertTrue(check_password(password, encoded)) + self.assertFalse(check_password(password[:72], encoded)) + + @skipUnless(bcrypt, "py-bcrypt not installed") + def test_bcrypt(self): + encoded = make_password('lètmein', hasher='bcrypt') + self.assertTrue(is_password_usable(encoded)) + self.assertTrue(encoded.startswith('bcrypt$')) + self.assertTrue(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt") + + def test_unusable(self): + encoded = make_password(None) + self.assertFalse(is_password_usable(encoded)) + self.assertFalse(check_password(None, encoded)) + self.assertFalse(check_password(UNUSABLE_PASSWORD, encoded)) + self.assertFalse(check_password('', encoded)) + self.assertFalse(check_password('lètmein', encoded)) + self.assertFalse(check_password('lètmeinz', encoded)) + self.assertRaises(ValueError, identify_hasher, encoded) + + def test_bad_algorithm(self): + def doit(): + make_password('lètmein', hasher='lolcat') + self.assertRaises(ValueError, doit) + self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash") + + def test_bad_encoded(self): + self.assertFalse(is_password_usable('lètmein_badencoded')) + self.assertFalse(is_password_usable('')) + + def test_low_level_pkbdf2(self): + hasher = PBKDF2PasswordHasher() + encoded = hasher.encode('lètmein', 'seasalt') + self.assertEqual(encoded, + 'pbkdf2_sha256$10000$seasalt$CWWFdHOWwPnki7HvkcqN9iA2T3KLW1cf2uZ5kvArtVY=') + self.assertTrue(hasher.verify('lètmein', encoded)) + + def test_low_level_pbkdf2_sha1(self): + hasher = PBKDF2SHA1PasswordHasher() + encoded = hasher.encode('lètmein', 'seasalt') + self.assertEqual(encoded, + 'pbkdf2_sha1$10000$seasalt$oAfF6vgs95ncksAhGXOWf4Okq7o=') + self.assertTrue(hasher.verify('lètmein', encoded)) + + def test_upgrade(self): + self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) + for algo in ('sha1', 'md5'): + encoded = make_password('lètmein', hasher=algo) + state = {'upgraded': False} + def setter(password): + state['upgraded'] = True + self.assertTrue(check_password('lètmein', encoded, setter)) + self.assertTrue(state['upgraded']) + + def test_no_upgrade(self): + encoded = make_password('lètmein') + state = {'upgraded': False} + def setter(): + state['upgraded'] = True + self.assertFalse(check_password('WRONG', encoded, setter)) + self.assertFalse(state['upgraded']) + + def test_no_upgrade_on_incorrect_pass(self): + self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) + for algo in ('sha1', 'md5'): + encoded = make_password('lètmein', hasher=algo) + state = {'upgraded': False} + def setter(): + state['upgraded'] = True + self.assertFalse(check_password('WRONG', encoded, setter)) + self.assertFalse(state['upgraded']) diff --git a/django/contrib/auth/tests/test_management.py b/django/contrib/auth/tests/test_management.py new file mode 100644 index 0000000000..687a5c31cb --- /dev/null +++ b/django/contrib/auth/tests/test_management.py @@ -0,0 +1,228 @@ +from __future__ import unicode_literals +from datetime import date + +from django.contrib.auth import models, management +from django.contrib.auth.management import create_permissions +from django.contrib.auth.management.commands import changepassword +from django.contrib.auth.models import User +from django.contrib.auth.tests import CustomUser +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.core.management import call_command +from django.core.management.base import CommandError +from django.core.management.validation import get_validation_errors +from django.db.models.loading import get_app +from django.test import TestCase +from django.test.utils import override_settings +from django.utils import six +from django.utils.six import StringIO + + +@skipIfCustomUser +class GetDefaultUsernameTestCase(TestCase): + + def setUp(self): + self.old_get_system_username = management.get_system_username + + def tearDown(self): + management.get_system_username = self.old_get_system_username + + def test_actual_implementation(self): + self.assertIsInstance(management.get_system_username(), six.text_type) + + def test_simple(self): + management.get_system_username = lambda: 'joe' + self.assertEqual(management.get_default_username(), 'joe') + + def test_existing(self): + models.User.objects.create(username='joe') + management.get_system_username = lambda: 'joe' + self.assertEqual(management.get_default_username(), '') + self.assertEqual( + management.get_default_username(check_db=False), 'joe') + + def test_i18n(self): + # 'Julia' with accented 'u': + management.get_system_username = lambda: 'J\xfalia' + self.assertEqual(management.get_default_username(), 'julia') + + +@skipIfCustomUser +class ChangepasswordManagementCommandTestCase(TestCase): + + def setUp(self): + self.user = models.User.objects.create_user(username='joe', password='qwerty') + self.stdout = StringIO() + self.stderr = StringIO() + + def tearDown(self): + self.stdout.close() + self.stderr.close() + + def test_that_changepassword_command_changes_joes_password(self): + "Executing the changepassword management command should change joe's password" + self.assertTrue(self.user.check_password('qwerty')) + command = changepassword.Command() + command._get_pass = lambda *args: 'not qwerty' + + command.execute("joe", stdout=self.stdout) + command_output = self.stdout.getvalue().strip() + + self.assertEqual(command_output, "Changing password for user 'joe'\nPassword changed successfully for user 'joe'") + self.assertTrue(models.User.objects.get(username="joe").check_password("not qwerty")) + + def test_that_max_tries_exits_1(self): + """ + A CommandError should be thrown by handle() if the user enters in + mismatched passwords three times. + """ + command = changepassword.Command() + command._get_pass = lambda *args: args or 'foo' + + with self.assertRaises(CommandError): + command.execute("joe", stdout=self.stdout, stderr=self.stderr) + + +@skipIfCustomUser +class CreatesuperuserManagementCommandTestCase(TestCase): + + def test_createsuperuser(self): + "Check the operation of the createsuperuser management command" + # We can use the management command to create a superuser + new_io = StringIO() + call_command("createsuperuser", + interactive=False, + username="joe", + email="joe@somewhere.org", + stdout=new_io + ) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, 'Superuser created successfully.') + u = User.objects.get(username="joe") + self.assertEqual(u.email, 'joe@somewhere.org') + + # created password should be unusable + self.assertFalse(u.has_usable_password()) + + def test_verbosity_zero(self): + # We can supress output on the management command + new_io = StringIO() + call_command("createsuperuser", + interactive=False, + username="joe2", + email="joe2@somewhere.org", + verbosity=0, + stdout=new_io + ) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, '') + u = User.objects.get(username="joe2") + self.assertEqual(u.email, 'joe2@somewhere.org') + self.assertFalse(u.has_usable_password()) + + def test_email_in_username(self): + new_io = StringIO() + call_command("createsuperuser", + interactive=False, + username="joe+admin@somewhere.org", + email="joe@somewhere.org", + stdout=new_io + ) + u = User._default_manager.get(username="joe+admin@somewhere.org") + self.assertEqual(u.email, 'joe@somewhere.org') + self.assertFalse(u.has_usable_password()) + + @override_settings(AUTH_USER_MODEL='auth.CustomUser') + def test_swappable_user(self): + "A superuser can be created when a custom User model is in use" + # We can use the management command to create a superuser + # We skip validation because the temporary substitution of the + # swappable User model messes with validation. + new_io = StringIO() + call_command("createsuperuser", + interactive=False, + email="joe@somewhere.org", + date_of_birth="1976-04-01", + stdout=new_io, + skip_validation=True + ) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, 'Superuser created successfully.') + u = CustomUser._default_manager.get(email="joe@somewhere.org") + self.assertEqual(u.date_of_birth, date(1976, 4, 1)) + + # created password should be unusable + self.assertFalse(u.has_usable_password()) + + @override_settings(AUTH_USER_MODEL='auth.CustomUser') + def test_swappable_user_missing_required_field(self): + "A Custom superuser won't be created when a required field isn't provided" + # We can use the management command to create a superuser + # We skip validation because the temporary substitution of the + # swappable User model messes with validation. + new_io = StringIO() + with self.assertRaises(CommandError): + call_command("createsuperuser", + interactive=False, + username="joe@somewhere.org", + stdout=new_io, + stderr=new_io, + skip_validation=True + ) + + self.assertEqual(CustomUser._default_manager.count(), 0) + + +class CustomUserModelValidationTestCase(TestCase): + @override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields') + def test_username_not_in_required_fields(self): + "USERNAME_FIELD should not appear in REQUIRED_FIELDS." + new_io = StringIO() + get_validation_errors(new_io, get_app('auth')) + self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue()) + + @override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername') + def test_username_non_unique(self): + "A non-unique USERNAME_FIELD should raise a model validation error." + new_io = StringIO() + get_validation_errors(new_io, get_app('auth')) + self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue()) + + +class PermissionDuplicationTestCase(TestCase): + + def setUp(self): + self._original_permissions = models.Permission._meta.permissions[:] + + def tearDown(self): + models.Permission._meta.permissions = self._original_permissions + + def test_duplicated_permissions(self): + """ + Test that we show proper error message if we are trying to create + duplicate permissions. + """ + # check duplicated default permission + models.Permission._meta.permissions = [ + ('change_permission', 'Can edit permission (duplicate)')] + six.assertRaisesRegex(self, CommandError, + "The permission codename 'change_permission' clashes with a " + "builtin permission for model 'auth.Permission'.", + create_permissions, models, [], verbosity=0) + + # check duplicated custom permissions + models.Permission._meta.permissions = [ + ('my_custom_permission', 'Some permission'), + ('other_one', 'Some other permission'), + ('my_custom_permission', 'Some permission with duplicate permission code'), + ] + six.assertRaisesRegex(self, CommandError, + "The permission codename 'my_custom_permission' is duplicated for model " + "'auth.Permission'.", + create_permissions, models, [], verbosity=0) + + # should not raise anything + models.Permission._meta.permissions = [ + ('my_custom_permission', 'Some permission'), + ('other_one', 'Some other permission'), + ] + create_permissions(models, [], verbosity=0) diff --git a/django/contrib/auth/tests/test_models.py b/django/contrib/auth/tests/test_models.py new file mode 100644 index 0000000000..8ac0599e6b --- /dev/null +++ b/django/contrib/auth/tests/test_models.py @@ -0,0 +1,142 @@ +import warnings + +from django.conf import settings +from django.contrib.auth import get_user_model +from django.contrib.auth.models import (Group, User, SiteProfileNotAvailable, + UserManager) +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.test import TestCase +from django.test.utils import override_settings +from django.utils import six + + +@skipIfCustomUser +@override_settings(USE_TZ=False, AUTH_PROFILE_MODULE='') +class ProfileTestCase(TestCase): + + def test_site_profile_not_available(self): + user = User.objects.create(username='testclient') + + # calling get_profile without AUTH_PROFILE_MODULE set + del settings.AUTH_PROFILE_MODULE + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + with six.assertRaisesRegex(self, SiteProfileNotAvailable, + "You need to set AUTH_PROFILE_MODULE in your project"): + user.get_profile() + + # Bad syntax in AUTH_PROFILE_MODULE: + settings.AUTH_PROFILE_MODULE = 'foobar' + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + with six.assertRaisesRegex(self, SiteProfileNotAvailable, + "app_label and model_name should be separated by a dot"): + user.get_profile() + + # module that doesn't exist + settings.AUTH_PROFILE_MODULE = 'foo.bar' + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + with six.assertRaisesRegex(self, SiteProfileNotAvailable, + "Unable to load the profile model"): + user.get_profile() + + +@skipIfCustomUser +@override_settings(USE_TZ=False) +class NaturalKeysTestCase(TestCase): + fixtures = ['authtestdata.json'] + + def test_user_natural_key(self): + staff_user = User.objects.get(username='staff') + self.assertEqual(User.objects.get_by_natural_key('staff'), staff_user) + self.assertEqual(staff_user.natural_key(), ('staff',)) + + def test_group_natural_key(self): + users_group = Group.objects.create(name='users') + self.assertEqual(Group.objects.get_by_natural_key('users'), users_group) + + +@skipIfCustomUser +@override_settings(USE_TZ=False) +class LoadDataWithoutNaturalKeysTestCase(TestCase): + fixtures = ['regular.json'] + + def test_user_is_created_and_added_to_group(self): + user = User.objects.get(username='my_username') + group = Group.objects.get(name='my_group') + self.assertEqual(group, user.groups.get()) + + +@skipIfCustomUser +@override_settings(USE_TZ=False) +class LoadDataWithNaturalKeysTestCase(TestCase): + fixtures = ['natural.json'] + + def test_user_is_created_and_added_to_group(self): + user = User.objects.get(username='my_username') + group = Group.objects.get(name='my_group') + self.assertEqual(group, user.groups.get()) + + +@skipIfCustomUser +class UserManagerTestCase(TestCase): + + def test_create_user(self): + email_lowercase = 'normal@normal.com' + user = User.objects.create_user('user', email_lowercase) + self.assertEqual(user.email, email_lowercase) + self.assertEqual(user.username, 'user') + self.assertEqual(user.password, '!') + + def test_create_user_email_domain_normalize_rfc3696(self): + # According to http://tools.ietf.org/html/rfc3696#section-3 + # the "@" symbol can be part of the local part of an email address + returned = UserManager.normalize_email(r'Abc\@DEF@EXAMPLE.com') + self.assertEqual(returned, r'Abc\@DEF@example.com') + + def test_create_user_email_domain_normalize(self): + returned = UserManager.normalize_email('normal@DOMAIN.COM') + self.assertEqual(returned, 'normal@domain.com') + + def test_create_user_email_domain_normalize_with_whitespace(self): + returned = UserManager.normalize_email('email\ with_whitespace@D.COM') + self.assertEqual(returned, 'email\ with_whitespace@d.com') + + def test_empty_username(self): + self.assertRaisesMessage(ValueError, + 'The given username must be set', + User.objects.create_user, username='') + + +class IsActiveTestCase(TestCase): + """ + Tests the behavior of the guaranteed is_active attribute + """ + + @skipIfCustomUser + def test_builtin_user_isactive(self): + user = User.objects.create(username='foo', email='foo@bar.com') + # is_active is true by default + self.assertEqual(user.is_active, True) + user.is_active = False + user.save() + user_fetched = User.objects.get(pk=user.pk) + # the is_active flag is saved + self.assertFalse(user_fetched.is_active) + + @override_settings(AUTH_USER_MODEL='auth.IsActiveTestUser1') + def test_is_active_field_default(self): + """ + tests that the default value for is_active is provided + """ + UserModel = get_user_model() + user = UserModel(username='foo') + self.assertEqual(user.is_active, True) + # you can set the attribute - but it will not save + user.is_active = False + # there should be no problem saving - but the attribute is not saved + user.save() + user_fetched = UserModel._default_manager.get(pk=user.pk) + # the attribute is always true for newly retrieved instance + self.assertEqual(user_fetched.is_active, True) diff --git a/django/contrib/auth/tests/test_remote_user.py b/django/contrib/auth/tests/test_remote_user.py new file mode 100644 index 0000000000..dae61afed5 --- /dev/null +++ b/django/contrib/auth/tests/test_remote_user.py @@ -0,0 +1,198 @@ +from datetime import datetime + +from django.conf import settings +from django.contrib.auth import authenticate +from django.contrib.auth.backends import RemoteUserBackend +from django.contrib.auth.models import User, AnonymousUser +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.test import TestCase +from django.utils import timezone + + +@skipIfCustomUser +class RemoteUserTest(TestCase): + + urls = 'django.contrib.auth.tests.urls' + middleware = 'django.contrib.auth.middleware.RemoteUserMiddleware' + backend = 'django.contrib.auth.backends.RemoteUserBackend' + + # Usernames to be passed in REMOTE_USER for the test_known_user test case. + known_user = 'knownuser' + known_user2 = 'knownuser2' + + def setUp(self): + self.curr_middleware = settings.MIDDLEWARE_CLASSES + self.curr_auth = settings.AUTHENTICATION_BACKENDS + settings.MIDDLEWARE_CLASSES += (self.middleware,) + settings.AUTHENTICATION_BACKENDS += (self.backend,) + + def test_no_remote_user(self): + """ + Tests requests where no remote user is specified and insures that no + users get created. + """ + num_users = User.objects.count() + + response = self.client.get('/remote_user/') + self.assertTrue(response.context['user'].is_anonymous()) + self.assertEqual(User.objects.count(), num_users) + + response = self.client.get('/remote_user/', REMOTE_USER=None) + self.assertTrue(response.context['user'].is_anonymous()) + self.assertEqual(User.objects.count(), num_users) + + response = self.client.get('/remote_user/', REMOTE_USER='') + self.assertTrue(response.context['user'].is_anonymous()) + self.assertEqual(User.objects.count(), num_users) + + def test_unknown_user(self): + """ + Tests the case where the username passed in the header does not exist + as a User. + """ + num_users = User.objects.count() + response = self.client.get('/remote_user/', REMOTE_USER='newuser') + self.assertEqual(response.context['user'].username, 'newuser') + self.assertEqual(User.objects.count(), num_users + 1) + User.objects.get(username='newuser') + + # Another request with same user should not create any new users. + response = self.client.get('/remote_user/', REMOTE_USER='newuser') + self.assertEqual(User.objects.count(), num_users + 1) + + def test_known_user(self): + """ + Tests the case where the username passed in the header is a valid User. + """ + User.objects.create(username='knownuser') + User.objects.create(username='knownuser2') + num_users = User.objects.count() + response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) + self.assertEqual(response.context['user'].username, 'knownuser') + self.assertEqual(User.objects.count(), num_users) + # Test that a different user passed in the headers causes the new user + # to be logged in. + response = self.client.get('/remote_user/', REMOTE_USER=self.known_user2) + self.assertEqual(response.context['user'].username, 'knownuser2') + self.assertEqual(User.objects.count(), num_users) + + def test_last_login(self): + """ + Tests that a user's last_login is set the first time they make a + request but not updated in subsequent requests with the same session. + """ + user = User.objects.create(username='knownuser') + # Set last_login to something so we can determine if it changes. + default_login = datetime(2000, 1, 1) + if settings.USE_TZ: + default_login = default_login.replace(tzinfo=timezone.utc) + user.last_login = default_login + user.save() + + response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) + self.assertNotEqual(default_login, response.context['user'].last_login) + + user = User.objects.get(username='knownuser') + user.last_login = default_login + user.save() + response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) + self.assertEqual(default_login, response.context['user'].last_login) + + def test_header_disappears(self): + """ + Tests that a logged in user is logged out automatically when + the REMOTE_USER header disappears during the same browser session. + """ + User.objects.create(username='knownuser') + # Known user authenticates + response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) + self.assertEqual(response.context['user'].username, 'knownuser') + # During the session, the REMOTE_USER header disappears. Should trigger logout. + response = self.client.get('/remote_user/') + self.assertEqual(response.context['user'].is_anonymous(), True) + # verify the remoteuser middleware will not remove a user + # authenticated via another backend + User.objects.create_user(username='modeluser', password='foo') + self.client.login(username='modeluser', password='foo') + authenticate(username='modeluser', password='foo') + response = self.client.get('/remote_user/') + self.assertEqual(response.context['user'].username, 'modeluser') + + def tearDown(self): + """Restores settings to avoid breaking other tests.""" + settings.MIDDLEWARE_CLASSES = self.curr_middleware + settings.AUTHENTICATION_BACKENDS = self.curr_auth + + +class RemoteUserNoCreateBackend(RemoteUserBackend): + """Backend that doesn't create unknown users.""" + create_unknown_user = False + + +@skipIfCustomUser +class RemoteUserNoCreateTest(RemoteUserTest): + """ + Contains the same tests as RemoteUserTest, but using a custom auth backend + class that doesn't create unknown users. + """ + + backend =\ + 'django.contrib.auth.tests.test_remote_user.RemoteUserNoCreateBackend' + + def test_unknown_user(self): + num_users = User.objects.count() + response = self.client.get('/remote_user/', REMOTE_USER='newuser') + self.assertTrue(response.context['user'].is_anonymous()) + self.assertEqual(User.objects.count(), num_users) + + +class CustomRemoteUserBackend(RemoteUserBackend): + """ + Backend that overrides RemoteUserBackend methods. + """ + + def clean_username(self, username): + """ + Grabs username before the @ character. + """ + return username.split('@')[0] + + def configure_user(self, user): + """ + Sets user's email address. + """ + user.email = 'user@example.com' + user.save() + return user + + +@skipIfCustomUser +class RemoteUserCustomTest(RemoteUserTest): + """ + Tests a custom RemoteUserBackend subclass that overrides the clean_username + and configure_user methods. + """ + + backend =\ + 'django.contrib.auth.tests.test_remote_user.CustomRemoteUserBackend' + # REMOTE_USER strings with email addresses for the custom backend to + # clean. + known_user = 'knownuser@example.com' + known_user2 = 'knownuser2@example.com' + + def test_known_user(self): + """ + The strings passed in REMOTE_USER should be cleaned and the known users + should not have been configured with an email address. + """ + super(RemoteUserCustomTest, self).test_known_user() + self.assertEqual(User.objects.get(username='knownuser').email, '') + self.assertEqual(User.objects.get(username='knownuser2').email, '') + + def test_unknown_user(self): + """ + The unknown user created should be configured with an email address. + """ + super(RemoteUserCustomTest, self).test_unknown_user() + newuser = User.objects.get(username='newuser') + self.assertEqual(newuser.email, 'user@example.com') diff --git a/django/contrib/auth/tests/test_signals.py b/django/contrib/auth/tests/test_signals.py new file mode 100644 index 0000000000..024f44f547 --- /dev/null +++ b/django/contrib/auth/tests/test_signals.py @@ -0,0 +1,80 @@ +from django.contrib.auth import signals +from django.contrib.auth.models import User +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.test import TestCase +from django.test.client import RequestFactory +from django.test.utils import override_settings + + +@skipIfCustomUser +@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class SignalTestCase(TestCase): + urls = 'django.contrib.auth.tests.urls' + fixtures = ['authtestdata.json'] + + def listener_login(self, user, **kwargs): + self.logged_in.append(user) + + def listener_logout(self, user, **kwargs): + self.logged_out.append(user) + + def listener_login_failed(self, sender, credentials, **kwargs): + self.login_failed.append(credentials) + + def setUp(self): + """Set up the listeners and reset the logged in/logged out counters""" + self.logged_in = [] + self.logged_out = [] + self.login_failed = [] + signals.user_logged_in.connect(self.listener_login) + signals.user_logged_out.connect(self.listener_logout) + signals.user_login_failed.connect(self.listener_login_failed) + + def tearDown(self): + """Disconnect the listeners""" + signals.user_logged_in.disconnect(self.listener_login) + signals.user_logged_out.disconnect(self.listener_logout) + signals.user_login_failed.disconnect(self.listener_login_failed) + + def test_login(self): + # Only a successful login will trigger the success signal. + self.client.login(username='testclient', password='bad') + self.assertEqual(len(self.logged_in), 0) + self.assertEqual(len(self.login_failed), 1) + self.assertEqual(self.login_failed[0]['username'], 'testclient') + # verify the password is cleansed + self.assertTrue('***' in self.login_failed[0]['password']) + + # Like this: + self.client.login(username='testclient', password='password') + self.assertEqual(len(self.logged_in), 1) + self.assertEqual(self.logged_in[0].username, 'testclient') + + # Ensure there were no more failures. + self.assertEqual(len(self.login_failed), 1) + + def test_logout_anonymous(self): + # The log_out function will still trigger the signal for anonymous + # users. + self.client.get('/logout/next_page/') + self.assertEqual(len(self.logged_out), 1) + self.assertEqual(self.logged_out[0], None) + + def test_logout(self): + self.client.login(username='testclient', password='password') + self.client.get('/logout/next_page/') + self.assertEqual(len(self.logged_out), 1) + self.assertEqual(self.logged_out[0].username, 'testclient') + + def test_update_last_login(self): + """Ensure that only `last_login` is updated in `update_last_login`""" + user = User.objects.get(pk=3) + old_last_login = user.last_login + + user.username = "This username shouldn't get saved" + request = RequestFactory().get('/login') + signals.user_logged_in.send(sender=user.__class__, request=request, + user=user) + user = User.objects.get(pk=3) + self.assertEqual(user.username, 'staff') + self.assertNotEqual(user.last_login, old_last_login) diff --git a/django/contrib/auth/tests/test_tokens.py b/django/contrib/auth/tests/test_tokens.py new file mode 100644 index 0000000000..e8aeb46326 --- /dev/null +++ b/django/contrib/auth/tests/test_tokens.py @@ -0,0 +1,69 @@ +import sys +from datetime import date, timedelta + +from django.conf import settings +from django.contrib.auth.models import User +from django.contrib.auth.tokens import PasswordResetTokenGenerator +from django.contrib.auth.tests.utils import skipIfCustomUser +from django.test import TestCase +from django.utils import unittest + + +@skipIfCustomUser +class TokenGeneratorTest(TestCase): + + def test_make_token(self): + """ + Ensure that we can make a token and that it is valid + """ + user = User.objects.create_user('tokentestuser', 'test2@example.com', 'testpw') + p0 = PasswordResetTokenGenerator() + tk1 = p0.make_token(user) + self.assertTrue(p0.check_token(user, tk1)) + + def test_10265(self): + """ + Ensure that the token generated for a user created in the same request + will work correctly. + """ + # See ticket #10265 + user = User.objects.create_user('comebackkid', 'test3@example.com', 'testpw') + p0 = PasswordResetTokenGenerator() + tk1 = p0.make_token(user) + reload = User.objects.get(username='comebackkid') + tk2 = p0.make_token(reload) + self.assertEqual(tk1, tk2) + + def test_timeout(self): + """ + Ensure we can use the token after n days, but no greater. + """ + # Uses a mocked version of PasswordResetTokenGenerator so we can change + # the value of 'today' + class Mocked(PasswordResetTokenGenerator): + def __init__(self, today): + self._today_val = today + def _today(self): + return self._today_val + + user = User.objects.create_user('tokentestuser', 'test2@example.com', 'testpw') + p0 = PasswordResetTokenGenerator() + tk1 = p0.make_token(user) + p1 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS)) + self.assertTrue(p1.check_token(user, tk1)) + + p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1)) + self.assertFalse(p2.check_token(user, tk1)) + + @unittest.skipIf(sys.version_info[:2] >= (3, 0), "Unnecessary test with Python 3") + def test_date_length(self): + """ + Make sure we don't allow overly long dates, causing a potential DoS. + """ + user = User.objects.create_user('ima1337h4x0r', 'test4@example.com', 'p4ssw0rd') + p0 = PasswordResetTokenGenerator() + + # This will put a 14-digit base36 timestamp into the token, which is too large. + self.assertRaises(ValueError, + p0._make_token_with_timestamp, + user, 175455491841851871349) diff --git a/django/contrib/auth/tests/test_views.py b/django/contrib/auth/tests/test_views.py new file mode 100644 index 0000000000..7cbf72327e --- /dev/null +++ b/django/contrib/auth/tests/test_views.py @@ -0,0 +1,545 @@ +import itertools +import os +import re + +from django.conf import global_settings, settings +from django.contrib.sites.models import Site, RequestSite +from django.contrib.auth.models import User +from django.core import mail +from django.core.exceptions import SuspiciousOperation +from django.core.urlresolvers import reverse, NoReverseMatch +from django.http import QueryDict +from django.utils.encoding import force_text +from django.utils.html import escape +from django.utils.http import urlquote +from django.utils._os import upath +from django.test import TestCase +from django.test.utils import override_settings + +from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME +from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm, + SetPasswordForm, PasswordResetForm) +from django.contrib.auth.tests.utils import skipIfCustomUser + + +@override_settings( + LANGUAGES=( + ('en', 'English'), + ), + LANGUAGE_CODE='en', + TEMPLATE_LOADERS=global_settings.TEMPLATE_LOADERS, + TEMPLATE_DIRS=( + os.path.join(os.path.dirname(upath(__file__)), 'templates'), + ), + USE_TZ=False, + PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), +) +class AuthViewsTestCase(TestCase): + """ + Helper base class for all the follow test cases. + """ + fixtures = ['authtestdata.json'] + urls = 'django.contrib.auth.tests.urls' + + def login(self, password='password'): + response = self.client.post('/login/', { + 'username': 'testclient', + 'password': password, + }) + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith(settings.LOGIN_REDIRECT_URL)) + self.assertTrue(SESSION_KEY in self.client.session) + + def assertFormError(self, response, error): + """Assert that error is found in response.context['form'] errors""" + form_errors = list(itertools.chain(*response.context['form'].errors.values())) + self.assertIn(force_text(error), form_errors) + + +@skipIfCustomUser +class AuthViewNamedURLTests(AuthViewsTestCase): + urls = 'django.contrib.auth.urls' + + def test_named_urls(self): + "Named URLs should be reversible" + expected_named_urls = [ + ('login', [], {}), + ('logout', [], {}), + ('password_change', [], {}), + ('password_change_done', [], {}), + ('password_reset', [], {}), + ('password_reset_done', [], {}), + ('password_reset_confirm', [], { + 'uidb36': 'aaaaaaa', + 'token': '1111-aaaaa', + }), + ('password_reset_complete', [], {}), + ] + for name, args, kwargs in expected_named_urls: + try: + reverse(name, args=args, kwargs=kwargs) + except NoReverseMatch: + self.fail("Reversal of url named '%s' failed with NoReverseMatch" % name) + + +@skipIfCustomUser +class PasswordResetTest(AuthViewsTestCase): + + def test_email_not_found(self): + """If the provided email is not registered, don't raise any error but + also don't send any email.""" + response = self.client.get('/password_reset/') + self.assertEqual(response.status_code, 200) + response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'}) + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 0) + + def test_email_found(self): + "Email is sent if a valid email address is provided for password reset" + response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) + self.assertTrue("http://" in mail.outbox[0].body) + self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email) + + def test_email_found_custom_from(self): + "Email is sent if a valid email address is provided for password reset when a custom from_email is provided." + response = self.client.post('/password_reset_from_email/', {'email': 'staffmember@example.com'}) + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual("staffmember@example.com", mail.outbox[0].from_email) + + @override_settings(ALLOWED_HOSTS=['adminsite.com']) + def test_admin_reset(self): + "If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override." + response = self.client.post('/admin_password_reset/', + {'email': 'staffmember@example.com'}, + HTTP_HOST='adminsite.com' + ) + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) + self.assertTrue("http://adminsite.com" in mail.outbox[0].body) + self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email) + + # Skip any 500 handler action (like sending more mail...) + @override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True) + def test_poisoned_http_host(self): + "Poisoned HTTP_HOST headers can't be used for reset emails" + # This attack is based on the way browsers handle URLs. The colon + # should be used to separate the port, but if the URL contains an @, + # the colon is interpreted as part of a username for login purposes, + # making 'evil.com' the request domain. Since HTTP_HOST is used to + # produce a meaningful reset URL, we need to be certain that the + # HTTP_HOST header isn't poisoned. This is done as a check when get_host() + # is invoked, but we check here as a practical consequence. + with self.assertRaises(SuspiciousOperation): + self.client.post('/password_reset/', + {'email': 'staffmember@example.com'}, + HTTP_HOST='www.example:dr.frankenstein@evil.tld' + ) + self.assertEqual(len(mail.outbox), 0) + + # Skip any 500 handler action (like sending more mail...) + @override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True) + def test_poisoned_http_host_admin_site(self): + "Poisoned HTTP_HOST headers can't be used for reset emails on admin views" + with self.assertRaises(SuspiciousOperation): + self.client.post('/admin_password_reset/', + {'email': 'staffmember@example.com'}, + HTTP_HOST='www.example:dr.frankenstein@evil.tld' + ) + self.assertEqual(len(mail.outbox), 0) + + def _test_confirm_start(self): + # Start by creating the email + response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) + return self._read_signup_email(mail.outbox[0]) + + def _read_signup_email(self, email): + urlmatch = re.search(r"https?://[^/]*(/.*reset/\S*)", email.body) + self.assertTrue(urlmatch is not None, "No URL found in sent email") + return urlmatch.group(), urlmatch.groups()[0] + + def test_confirm_valid(self): + url, path = self._test_confirm_start() + response = self.client.get(path) + # redirect to a 'complete' page: + self.assertContains(response, "Please enter your new password") + + def test_confirm_invalid(self): + url, path = self._test_confirm_start() + # Let's munge the token in the path, but keep the same length, + # in case the URLconf will reject a different length. + path = path[:-5] + ("0" * 4) + path[-1] + + response = self.client.get(path) + self.assertContains(response, "The password reset link was invalid") + + def test_confirm_invalid_user(self): + # Ensure that we get a 200 response for a non-existant user, not a 404 + response = self.client.get('/reset/123456-1-1/') + self.assertContains(response, "The password reset link was invalid") + + def test_confirm_overflow_user(self): + # Ensure that we get a 200 response for a base36 user id that overflows int + response = self.client.get('/reset/zzzzzzzzzzzzz-1-1/') + self.assertContains(response, "The password reset link was invalid") + + def test_confirm_invalid_post(self): + # Same as test_confirm_invalid, but trying + # to do a POST instead. + url, path = self._test_confirm_start() + path = path[:-5] + ("0" * 4) + path[-1] + + self.client.post(path, { + 'new_password1': 'anewpassword', + 'new_password2': ' anewpassword', + }) + # Check the password has not been changed + u = User.objects.get(email='staffmember@example.com') + self.assertTrue(not u.check_password("anewpassword")) + + def test_confirm_complete(self): + url, path = self._test_confirm_start() + response = self.client.post(path, {'new_password1': 'anewpassword', + 'new_password2': 'anewpassword'}) + # It redirects us to a 'complete' page: + self.assertEqual(response.status_code, 302) + # Check the password has been changed + u = User.objects.get(email='staffmember@example.com') + self.assertTrue(u.check_password("anewpassword")) + + # Check we can't use the link again + response = self.client.get(path) + self.assertContains(response, "The password reset link was invalid") + + def test_confirm_different_passwords(self): + url, path = self._test_confirm_start() + response = self.client.post(path, {'new_password1': 'anewpassword', + 'new_password2': 'x'}) + self.assertFormError(response, SetPasswordForm.error_messages['password_mismatch']) + + +@override_settings(AUTH_USER_MODEL='auth.CustomUser') +class CustomUserPasswordResetTest(AuthViewsTestCase): + fixtures = ['custom_user.json'] + + def _test_confirm_start(self): + # Start by creating the email + response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) + return self._read_signup_email(mail.outbox[0]) + + def _read_signup_email(self, email): + urlmatch = re.search(r"https?://[^/]*(/.*reset/\S*)", email.body) + self.assertTrue(urlmatch is not None, "No URL found in sent email") + return urlmatch.group(), urlmatch.groups()[0] + + def test_confirm_valid_custom_user(self): + url, path = self._test_confirm_start() + response = self.client.get(path) + # redirect to a 'complete' page: + self.assertContains(response, "Please enter your new password") + + +@skipIfCustomUser +class ChangePasswordTest(AuthViewsTestCase): + + def fail_login(self, password='password'): + response = self.client.post('/login/', { + 'username': 'testclient', + 'password': password, + }) + self.assertFormError(response, AuthenticationForm.error_messages['invalid_login'] % { + 'username': User._meta.get_field('username').verbose_name + }) + + def logout(self): + response = self.client.get('/logout/') + + def test_password_change_fails_with_invalid_old_password(self): + self.login() + response = self.client.post('/password_change/', { + 'old_password': 'donuts', + 'new_password1': 'password1', + 'new_password2': 'password1', + }) + self.assertFormError(response, PasswordChangeForm.error_messages['password_incorrect']) + + def test_password_change_fails_with_mismatched_passwords(self): + self.login() + response = self.client.post('/password_change/', { + 'old_password': 'password', + 'new_password1': 'password1', + 'new_password2': 'donuts', + }) + self.assertFormError(response, SetPasswordForm.error_messages['password_mismatch']) + + def test_password_change_succeeds(self): + self.login() + response = self.client.post('/password_change/', { + 'old_password': 'password', + 'new_password1': 'password1', + 'new_password2': 'password1', + }) + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/password_change/done/')) + self.fail_login() + self.login(password='password1') + + def test_password_change_done_succeeds(self): + self.login() + response = self.client.post('/password_change/', { + 'old_password': 'password', + 'new_password1': 'password1', + 'new_password2': 'password1', + }) + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/password_change/done/')) + + def test_password_change_done_fails(self): + with self.settings(LOGIN_URL='/login/'): + response = self.client.get('/password_change/done/') + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/login/?next=/password_change/done/')) + + +@skipIfCustomUser +class LoginTest(AuthViewsTestCase): + + def test_current_site_in_context_after_login(self): + response = self.client.get(reverse('django.contrib.auth.views.login')) + self.assertEqual(response.status_code, 200) + if Site._meta.installed: + site = Site.objects.get_current() + self.assertEqual(response.context['site'], site) + self.assertEqual(response.context['site_name'], site.name) + else: + self.assertIsInstance(response.context['site'], RequestSite) + self.assertTrue(isinstance(response.context['form'], AuthenticationForm), + 'Login form is not an AuthenticationForm') + + def test_security_check(self, password='password'): + login_url = reverse('django.contrib.auth.views.login') + + # Those URLs should not pass the security check + for bad_url in ('http://example.com', + 'https://example.com', + 'ftp://exampel.com', + '//example.com'): + + nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { + 'url': login_url, + 'next': REDIRECT_FIELD_NAME, + 'bad_url': urlquote(bad_url), + } + response = self.client.post(nasty_url, { + 'username': 'testclient', + 'password': password, + }) + self.assertEqual(response.status_code, 302) + self.assertFalse(bad_url in response.url, + "%s should be blocked" % bad_url) + + # These URLs *should* still pass the security check + for good_url in ('/view/?param=http://example.com', + '/view/?param=https://example.com', + '/view?param=ftp://exampel.com', + 'view/?param=//example.com', + 'https:///', + '//testserver/', + '/url%20with%20spaces/'): # see ticket #12534 + safe_url = '%(url)s?%(next)s=%(good_url)s' % { + 'url': login_url, + 'next': REDIRECT_FIELD_NAME, + 'good_url': urlquote(good_url), + } + response = self.client.post(safe_url, { + 'username': 'testclient', + 'password': password, + }) + self.assertEqual(response.status_code, 302) + self.assertTrue(good_url in response.url, + "%s should be allowed" % good_url) + + def test_login_form_contains_request(self): + # 15198 + response = self.client.post('/custom_requestauth_login/', { + 'username': 'testclient', + 'password': 'password', + }, follow=True) + # the custom authentication form used by this login asserts + # that a request is passed to the form successfully. + +@skipIfCustomUser +class LoginURLSettings(AuthViewsTestCase): + + def setUp(self): + super(LoginURLSettings, self).setUp() + self.old_LOGIN_URL = settings.LOGIN_URL + + def tearDown(self): + super(LoginURLSettings, self).tearDown() + settings.LOGIN_URL = self.old_LOGIN_URL + + def get_login_required_url(self, login_url): + settings.LOGIN_URL = login_url + response = self.client.get('/login_required/') + self.assertEqual(response.status_code, 302) + return response.url + + def test_standard_login_url(self): + login_url = '/login/' + login_required_url = self.get_login_required_url(login_url) + querystring = QueryDict('', mutable=True) + querystring['next'] = '/login_required/' + self.assertEqual(login_required_url, 'http://testserver%s?%s' % + (login_url, querystring.urlencode('/'))) + + def test_remote_login_url(self): + login_url = 'http://remote.example.com/login' + login_required_url = self.get_login_required_url(login_url) + querystring = QueryDict('', mutable=True) + querystring['next'] = 'http://testserver/login_required/' + self.assertEqual(login_required_url, + '%s?%s' % (login_url, querystring.urlencode('/'))) + + def test_https_login_url(self): + login_url = 'https:///login/' + login_required_url = self.get_login_required_url(login_url) + querystring = QueryDict('', mutable=True) + querystring['next'] = 'http://testserver/login_required/' + self.assertEqual(login_required_url, + '%s?%s' % (login_url, querystring.urlencode('/'))) + + def test_login_url_with_querystring(self): + login_url = '/login/?pretty=1' + login_required_url = self.get_login_required_url(login_url) + querystring = QueryDict('pretty=1', mutable=True) + querystring['next'] = '/login_required/' + self.assertEqual(login_required_url, 'http://testserver/login/?%s' % + querystring.urlencode('/')) + + def test_remote_login_url_with_next_querystring(self): + login_url = 'http://remote.example.com/login/' + login_required_url = self.get_login_required_url('%s?next=/default/' % + login_url) + querystring = QueryDict('', mutable=True) + querystring['next'] = 'http://testserver/login_required/' + self.assertEqual(login_required_url, '%s?%s' % (login_url, + querystring.urlencode('/'))) + + +@skipIfCustomUser +class LogoutTest(AuthViewsTestCase): + + def confirm_logged_out(self): + self.assertTrue(SESSION_KEY not in self.client.session) + + def test_logout_default(self): + "Logout without next_page option renders the default template" + self.login() + response = self.client.get('/logout/') + self.assertContains(response, 'Logged out') + self.confirm_logged_out() + + def test_14377(self): + # Bug 14377 + self.login() + response = self.client.get('/logout/') + self.assertTrue('site' in response.context) + + def test_logout_with_overridden_redirect_url(self): + # Bug 11223 + self.login() + response = self.client.get('/logout/next_page/') + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/somewhere/')) + + response = self.client.get('/logout/next_page/?next=/login/') + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/login/')) + + self.confirm_logged_out() + + def test_logout_with_next_page_specified(self): + "Logout with next_page option given redirects to specified resource" + self.login() + response = self.client.get('/logout/next_page/') + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/somewhere/')) + self.confirm_logged_out() + + def test_logout_with_redirect_argument(self): + "Logout with query string redirects to specified resource" + self.login() + response = self.client.get('/logout/?next=/login/') + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/login/')) + self.confirm_logged_out() + + def test_logout_with_custom_redirect_argument(self): + "Logout with custom query string redirects to specified resource" + self.login() + response = self.client.get('/logout/custom_query/?follow=/somewhere/') + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith('/somewhere/')) + self.confirm_logged_out() + + def test_security_check(self, password='password'): + logout_url = reverse('django.contrib.auth.views.logout') + + # Those URLs should not pass the security check + for bad_url in ('http://example.com', + 'https://example.com', + 'ftp://exampel.com', + '//example.com'): + nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { + 'url': logout_url, + 'next': REDIRECT_FIELD_NAME, + 'bad_url': urlquote(bad_url), + } + self.login() + response = self.client.get(nasty_url) + self.assertEqual(response.status_code, 302) + self.assertFalse(bad_url in response.url, + "%s should be blocked" % bad_url) + self.confirm_logged_out() + + # These URLs *should* still pass the security check + for good_url in ('/view/?param=http://example.com', + '/view/?param=https://example.com', + '/view?param=ftp://exampel.com', + 'view/?param=//example.com', + 'https:///', + '//testserver/', + '/url%20with%20spaces/'): # see ticket #12534 + safe_url = '%(url)s?%(next)s=%(good_url)s' % { + 'url': logout_url, + 'next': REDIRECT_FIELD_NAME, + 'good_url': urlquote(good_url), + } + self.login() + response = self.client.get(safe_url) + self.assertEqual(response.status_code, 302) + self.assertTrue(good_url in response.url, + "%s should be allowed" % good_url) + self.confirm_logged_out() + +@skipIfCustomUser +class ChangelistTests(AuthViewsTestCase): + urls = 'django.contrib.auth.tests.urls_admin' + + # #20078 - users shouldn't be allowed to guess password hashes via + # repeated password__startswith queries. + def test_changelist_disallows_password_lookups(self): + # Make me a superuser before loging in. + User.objects.filter(username='testclient').update(is_staff=True, is_superuser=True) + self.login() + + # A lookup that tries to filter on password isn't OK + with self.assertRaises(SuspiciousOperation): + response = self.client.get('/admin/auth/user/?password__startswith=sha1$') diff --git a/django/contrib/auth/tests/tokens.py b/django/contrib/auth/tests/tokens.py deleted file mode 100644 index e8aeb46326..0000000000 --- a/django/contrib/auth/tests/tokens.py +++ /dev/null @@ -1,69 +0,0 @@ -import sys -from datetime import date, timedelta - -from django.conf import settings -from django.contrib.auth.models import User -from django.contrib.auth.tokens import PasswordResetTokenGenerator -from django.contrib.auth.tests.utils import skipIfCustomUser -from django.test import TestCase -from django.utils import unittest - - -@skipIfCustomUser -class TokenGeneratorTest(TestCase): - - def test_make_token(self): - """ - Ensure that we can make a token and that it is valid - """ - user = User.objects.create_user('tokentestuser', 'test2@example.com', 'testpw') - p0 = PasswordResetTokenGenerator() - tk1 = p0.make_token(user) - self.assertTrue(p0.check_token(user, tk1)) - - def test_10265(self): - """ - Ensure that the token generated for a user created in the same request - will work correctly. - """ - # See ticket #10265 - user = User.objects.create_user('comebackkid', 'test3@example.com', 'testpw') - p0 = PasswordResetTokenGenerator() - tk1 = p0.make_token(user) - reload = User.objects.get(username='comebackkid') - tk2 = p0.make_token(reload) - self.assertEqual(tk1, tk2) - - def test_timeout(self): - """ - Ensure we can use the token after n days, but no greater. - """ - # Uses a mocked version of PasswordResetTokenGenerator so we can change - # the value of 'today' - class Mocked(PasswordResetTokenGenerator): - def __init__(self, today): - self._today_val = today - def _today(self): - return self._today_val - - user = User.objects.create_user('tokentestuser', 'test2@example.com', 'testpw') - p0 = PasswordResetTokenGenerator() - tk1 = p0.make_token(user) - p1 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS)) - self.assertTrue(p1.check_token(user, tk1)) - - p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1)) - self.assertFalse(p2.check_token(user, tk1)) - - @unittest.skipIf(sys.version_info[:2] >= (3, 0), "Unnecessary test with Python 3") - def test_date_length(self): - """ - Make sure we don't allow overly long dates, causing a potential DoS. - """ - user = User.objects.create_user('ima1337h4x0r', 'test4@example.com', 'p4ssw0rd') - p0 = PasswordResetTokenGenerator() - - # This will put a 14-digit base36 timestamp into the token, which is too large. - self.assertRaises(ValueError, - p0._make_token_with_timestamp, - user, 175455491841851871349) diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py deleted file mode 100644 index 7cbf72327e..0000000000 --- a/django/contrib/auth/tests/views.py +++ /dev/null @@ -1,545 +0,0 @@ -import itertools -import os -import re - -from django.conf import global_settings, settings -from django.contrib.sites.models import Site, RequestSite -from django.contrib.auth.models import User -from django.core import mail -from django.core.exceptions import SuspiciousOperation -from django.core.urlresolvers import reverse, NoReverseMatch -from django.http import QueryDict -from django.utils.encoding import force_text -from django.utils.html import escape -from django.utils.http import urlquote -from django.utils._os import upath -from django.test import TestCase -from django.test.utils import override_settings - -from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME -from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm, - SetPasswordForm, PasswordResetForm) -from django.contrib.auth.tests.utils import skipIfCustomUser - - -@override_settings( - LANGUAGES=( - ('en', 'English'), - ), - LANGUAGE_CODE='en', - TEMPLATE_LOADERS=global_settings.TEMPLATE_LOADERS, - TEMPLATE_DIRS=( - os.path.join(os.path.dirname(upath(__file__)), 'templates'), - ), - USE_TZ=False, - PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), -) -class AuthViewsTestCase(TestCase): - """ - Helper base class for all the follow test cases. - """ - fixtures = ['authtestdata.json'] - urls = 'django.contrib.auth.tests.urls' - - def login(self, password='password'): - response = self.client.post('/login/', { - 'username': 'testclient', - 'password': password, - }) - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith(settings.LOGIN_REDIRECT_URL)) - self.assertTrue(SESSION_KEY in self.client.session) - - def assertFormError(self, response, error): - """Assert that error is found in response.context['form'] errors""" - form_errors = list(itertools.chain(*response.context['form'].errors.values())) - self.assertIn(force_text(error), form_errors) - - -@skipIfCustomUser -class AuthViewNamedURLTests(AuthViewsTestCase): - urls = 'django.contrib.auth.urls' - - def test_named_urls(self): - "Named URLs should be reversible" - expected_named_urls = [ - ('login', [], {}), - ('logout', [], {}), - ('password_change', [], {}), - ('password_change_done', [], {}), - ('password_reset', [], {}), - ('password_reset_done', [], {}), - ('password_reset_confirm', [], { - 'uidb36': 'aaaaaaa', - 'token': '1111-aaaaa', - }), - ('password_reset_complete', [], {}), - ] - for name, args, kwargs in expected_named_urls: - try: - reverse(name, args=args, kwargs=kwargs) - except NoReverseMatch: - self.fail("Reversal of url named '%s' failed with NoReverseMatch" % name) - - -@skipIfCustomUser -class PasswordResetTest(AuthViewsTestCase): - - def test_email_not_found(self): - """If the provided email is not registered, don't raise any error but - also don't send any email.""" - response = self.client.get('/password_reset/') - self.assertEqual(response.status_code, 200) - response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'}) - self.assertEqual(response.status_code, 302) - self.assertEqual(len(mail.outbox), 0) - - def test_email_found(self): - "Email is sent if a valid email address is provided for password reset" - response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) - self.assertEqual(response.status_code, 302) - self.assertEqual(len(mail.outbox), 1) - self.assertTrue("http://" in mail.outbox[0].body) - self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email) - - def test_email_found_custom_from(self): - "Email is sent if a valid email address is provided for password reset when a custom from_email is provided." - response = self.client.post('/password_reset_from_email/', {'email': 'staffmember@example.com'}) - self.assertEqual(response.status_code, 302) - self.assertEqual(len(mail.outbox), 1) - self.assertEqual("staffmember@example.com", mail.outbox[0].from_email) - - @override_settings(ALLOWED_HOSTS=['adminsite.com']) - def test_admin_reset(self): - "If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override." - response = self.client.post('/admin_password_reset/', - {'email': 'staffmember@example.com'}, - HTTP_HOST='adminsite.com' - ) - self.assertEqual(response.status_code, 302) - self.assertEqual(len(mail.outbox), 1) - self.assertTrue("http://adminsite.com" in mail.outbox[0].body) - self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email) - - # Skip any 500 handler action (like sending more mail...) - @override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True) - def test_poisoned_http_host(self): - "Poisoned HTTP_HOST headers can't be used for reset emails" - # This attack is based on the way browsers handle URLs. The colon - # should be used to separate the port, but if the URL contains an @, - # the colon is interpreted as part of a username for login purposes, - # making 'evil.com' the request domain. Since HTTP_HOST is used to - # produce a meaningful reset URL, we need to be certain that the - # HTTP_HOST header isn't poisoned. This is done as a check when get_host() - # is invoked, but we check here as a practical consequence. - with self.assertRaises(SuspiciousOperation): - self.client.post('/password_reset/', - {'email': 'staffmember@example.com'}, - HTTP_HOST='www.example:dr.frankenstein@evil.tld' - ) - self.assertEqual(len(mail.outbox), 0) - - # Skip any 500 handler action (like sending more mail...) - @override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True) - def test_poisoned_http_host_admin_site(self): - "Poisoned HTTP_HOST headers can't be used for reset emails on admin views" - with self.assertRaises(SuspiciousOperation): - self.client.post('/admin_password_reset/', - {'email': 'staffmember@example.com'}, - HTTP_HOST='www.example:dr.frankenstein@evil.tld' - ) - self.assertEqual(len(mail.outbox), 0) - - def _test_confirm_start(self): - # Start by creating the email - response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) - self.assertEqual(response.status_code, 302) - self.assertEqual(len(mail.outbox), 1) - return self._read_signup_email(mail.outbox[0]) - - def _read_signup_email(self, email): - urlmatch = re.search(r"https?://[^/]*(/.*reset/\S*)", email.body) - self.assertTrue(urlmatch is not None, "No URL found in sent email") - return urlmatch.group(), urlmatch.groups()[0] - - def test_confirm_valid(self): - url, path = self._test_confirm_start() - response = self.client.get(path) - # redirect to a 'complete' page: - self.assertContains(response, "Please enter your new password") - - def test_confirm_invalid(self): - url, path = self._test_confirm_start() - # Let's munge the token in the path, but keep the same length, - # in case the URLconf will reject a different length. - path = path[:-5] + ("0" * 4) + path[-1] - - response = self.client.get(path) - self.assertContains(response, "The password reset link was invalid") - - def test_confirm_invalid_user(self): - # Ensure that we get a 200 response for a non-existant user, not a 404 - response = self.client.get('/reset/123456-1-1/') - self.assertContains(response, "The password reset link was invalid") - - def test_confirm_overflow_user(self): - # Ensure that we get a 200 response for a base36 user id that overflows int - response = self.client.get('/reset/zzzzzzzzzzzzz-1-1/') - self.assertContains(response, "The password reset link was invalid") - - def test_confirm_invalid_post(self): - # Same as test_confirm_invalid, but trying - # to do a POST instead. - url, path = self._test_confirm_start() - path = path[:-5] + ("0" * 4) + path[-1] - - self.client.post(path, { - 'new_password1': 'anewpassword', - 'new_password2': ' anewpassword', - }) - # Check the password has not been changed - u = User.objects.get(email='staffmember@example.com') - self.assertTrue(not u.check_password("anewpassword")) - - def test_confirm_complete(self): - url, path = self._test_confirm_start() - response = self.client.post(path, {'new_password1': 'anewpassword', - 'new_password2': 'anewpassword'}) - # It redirects us to a 'complete' page: - self.assertEqual(response.status_code, 302) - # Check the password has been changed - u = User.objects.get(email='staffmember@example.com') - self.assertTrue(u.check_password("anewpassword")) - - # Check we can't use the link again - response = self.client.get(path) - self.assertContains(response, "The password reset link was invalid") - - def test_confirm_different_passwords(self): - url, path = self._test_confirm_start() - response = self.client.post(path, {'new_password1': 'anewpassword', - 'new_password2': 'x'}) - self.assertFormError(response, SetPasswordForm.error_messages['password_mismatch']) - - -@override_settings(AUTH_USER_MODEL='auth.CustomUser') -class CustomUserPasswordResetTest(AuthViewsTestCase): - fixtures = ['custom_user.json'] - - def _test_confirm_start(self): - # Start by creating the email - response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) - self.assertEqual(response.status_code, 302) - self.assertEqual(len(mail.outbox), 1) - return self._read_signup_email(mail.outbox[0]) - - def _read_signup_email(self, email): - urlmatch = re.search(r"https?://[^/]*(/.*reset/\S*)", email.body) - self.assertTrue(urlmatch is not None, "No URL found in sent email") - return urlmatch.group(), urlmatch.groups()[0] - - def test_confirm_valid_custom_user(self): - url, path = self._test_confirm_start() - response = self.client.get(path) - # redirect to a 'complete' page: - self.assertContains(response, "Please enter your new password") - - -@skipIfCustomUser -class ChangePasswordTest(AuthViewsTestCase): - - def fail_login(self, password='password'): - response = self.client.post('/login/', { - 'username': 'testclient', - 'password': password, - }) - self.assertFormError(response, AuthenticationForm.error_messages['invalid_login'] % { - 'username': User._meta.get_field('username').verbose_name - }) - - def logout(self): - response = self.client.get('/logout/') - - def test_password_change_fails_with_invalid_old_password(self): - self.login() - response = self.client.post('/password_change/', { - 'old_password': 'donuts', - 'new_password1': 'password1', - 'new_password2': 'password1', - }) - self.assertFormError(response, PasswordChangeForm.error_messages['password_incorrect']) - - def test_password_change_fails_with_mismatched_passwords(self): - self.login() - response = self.client.post('/password_change/', { - 'old_password': 'password', - 'new_password1': 'password1', - 'new_password2': 'donuts', - }) - self.assertFormError(response, SetPasswordForm.error_messages['password_mismatch']) - - def test_password_change_succeeds(self): - self.login() - response = self.client.post('/password_change/', { - 'old_password': 'password', - 'new_password1': 'password1', - 'new_password2': 'password1', - }) - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/password_change/done/')) - self.fail_login() - self.login(password='password1') - - def test_password_change_done_succeeds(self): - self.login() - response = self.client.post('/password_change/', { - 'old_password': 'password', - 'new_password1': 'password1', - 'new_password2': 'password1', - }) - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/password_change/done/')) - - def test_password_change_done_fails(self): - with self.settings(LOGIN_URL='/login/'): - response = self.client.get('/password_change/done/') - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/login/?next=/password_change/done/')) - - -@skipIfCustomUser -class LoginTest(AuthViewsTestCase): - - def test_current_site_in_context_after_login(self): - response = self.client.get(reverse('django.contrib.auth.views.login')) - self.assertEqual(response.status_code, 200) - if Site._meta.installed: - site = Site.objects.get_current() - self.assertEqual(response.context['site'], site) - self.assertEqual(response.context['site_name'], site.name) - else: - self.assertIsInstance(response.context['site'], RequestSite) - self.assertTrue(isinstance(response.context['form'], AuthenticationForm), - 'Login form is not an AuthenticationForm') - - def test_security_check(self, password='password'): - login_url = reverse('django.contrib.auth.views.login') - - # Those URLs should not pass the security check - for bad_url in ('http://example.com', - 'https://example.com', - 'ftp://exampel.com', - '//example.com'): - - nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { - 'url': login_url, - 'next': REDIRECT_FIELD_NAME, - 'bad_url': urlquote(bad_url), - } - response = self.client.post(nasty_url, { - 'username': 'testclient', - 'password': password, - }) - self.assertEqual(response.status_code, 302) - self.assertFalse(bad_url in response.url, - "%s should be blocked" % bad_url) - - # These URLs *should* still pass the security check - for good_url in ('/view/?param=http://example.com', - '/view/?param=https://example.com', - '/view?param=ftp://exampel.com', - 'view/?param=//example.com', - 'https:///', - '//testserver/', - '/url%20with%20spaces/'): # see ticket #12534 - safe_url = '%(url)s?%(next)s=%(good_url)s' % { - 'url': login_url, - 'next': REDIRECT_FIELD_NAME, - 'good_url': urlquote(good_url), - } - response = self.client.post(safe_url, { - 'username': 'testclient', - 'password': password, - }) - self.assertEqual(response.status_code, 302) - self.assertTrue(good_url in response.url, - "%s should be allowed" % good_url) - - def test_login_form_contains_request(self): - # 15198 - response = self.client.post('/custom_requestauth_login/', { - 'username': 'testclient', - 'password': 'password', - }, follow=True) - # the custom authentication form used by this login asserts - # that a request is passed to the form successfully. - -@skipIfCustomUser -class LoginURLSettings(AuthViewsTestCase): - - def setUp(self): - super(LoginURLSettings, self).setUp() - self.old_LOGIN_URL = settings.LOGIN_URL - - def tearDown(self): - super(LoginURLSettings, self).tearDown() - settings.LOGIN_URL = self.old_LOGIN_URL - - def get_login_required_url(self, login_url): - settings.LOGIN_URL = login_url - response = self.client.get('/login_required/') - self.assertEqual(response.status_code, 302) - return response.url - - def test_standard_login_url(self): - login_url = '/login/' - login_required_url = self.get_login_required_url(login_url) - querystring = QueryDict('', mutable=True) - querystring['next'] = '/login_required/' - self.assertEqual(login_required_url, 'http://testserver%s?%s' % - (login_url, querystring.urlencode('/'))) - - def test_remote_login_url(self): - login_url = 'http://remote.example.com/login' - login_required_url = self.get_login_required_url(login_url) - querystring = QueryDict('', mutable=True) - querystring['next'] = 'http://testserver/login_required/' - self.assertEqual(login_required_url, - '%s?%s' % (login_url, querystring.urlencode('/'))) - - def test_https_login_url(self): - login_url = 'https:///login/' - login_required_url = self.get_login_required_url(login_url) - querystring = QueryDict('', mutable=True) - querystring['next'] = 'http://testserver/login_required/' - self.assertEqual(login_required_url, - '%s?%s' % (login_url, querystring.urlencode('/'))) - - def test_login_url_with_querystring(self): - login_url = '/login/?pretty=1' - login_required_url = self.get_login_required_url(login_url) - querystring = QueryDict('pretty=1', mutable=True) - querystring['next'] = '/login_required/' - self.assertEqual(login_required_url, 'http://testserver/login/?%s' % - querystring.urlencode('/')) - - def test_remote_login_url_with_next_querystring(self): - login_url = 'http://remote.example.com/login/' - login_required_url = self.get_login_required_url('%s?next=/default/' % - login_url) - querystring = QueryDict('', mutable=True) - querystring['next'] = 'http://testserver/login_required/' - self.assertEqual(login_required_url, '%s?%s' % (login_url, - querystring.urlencode('/'))) - - -@skipIfCustomUser -class LogoutTest(AuthViewsTestCase): - - def confirm_logged_out(self): - self.assertTrue(SESSION_KEY not in self.client.session) - - def test_logout_default(self): - "Logout without next_page option renders the default template" - self.login() - response = self.client.get('/logout/') - self.assertContains(response, 'Logged out') - self.confirm_logged_out() - - def test_14377(self): - # Bug 14377 - self.login() - response = self.client.get('/logout/') - self.assertTrue('site' in response.context) - - def test_logout_with_overridden_redirect_url(self): - # Bug 11223 - self.login() - response = self.client.get('/logout/next_page/') - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/somewhere/')) - - response = self.client.get('/logout/next_page/?next=/login/') - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/login/')) - - self.confirm_logged_out() - - def test_logout_with_next_page_specified(self): - "Logout with next_page option given redirects to specified resource" - self.login() - response = self.client.get('/logout/next_page/') - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/somewhere/')) - self.confirm_logged_out() - - def test_logout_with_redirect_argument(self): - "Logout with query string redirects to specified resource" - self.login() - response = self.client.get('/logout/?next=/login/') - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/login/')) - self.confirm_logged_out() - - def test_logout_with_custom_redirect_argument(self): - "Logout with custom query string redirects to specified resource" - self.login() - response = self.client.get('/logout/custom_query/?follow=/somewhere/') - self.assertEqual(response.status_code, 302) - self.assertTrue(response.url.endswith('/somewhere/')) - self.confirm_logged_out() - - def test_security_check(self, password='password'): - logout_url = reverse('django.contrib.auth.views.logout') - - # Those URLs should not pass the security check - for bad_url in ('http://example.com', - 'https://example.com', - 'ftp://exampel.com', - '//example.com'): - nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { - 'url': logout_url, - 'next': REDIRECT_FIELD_NAME, - 'bad_url': urlquote(bad_url), - } - self.login() - response = self.client.get(nasty_url) - self.assertEqual(response.status_code, 302) - self.assertFalse(bad_url in response.url, - "%s should be blocked" % bad_url) - self.confirm_logged_out() - - # These URLs *should* still pass the security check - for good_url in ('/view/?param=http://example.com', - '/view/?param=https://example.com', - '/view?param=ftp://exampel.com', - 'view/?param=//example.com', - 'https:///', - '//testserver/', - '/url%20with%20spaces/'): # see ticket #12534 - safe_url = '%(url)s?%(next)s=%(good_url)s' % { - 'url': logout_url, - 'next': REDIRECT_FIELD_NAME, - 'good_url': urlquote(good_url), - } - self.login() - response = self.client.get(safe_url) - self.assertEqual(response.status_code, 302) - self.assertTrue(good_url in response.url, - "%s should be allowed" % good_url) - self.confirm_logged_out() - -@skipIfCustomUser -class ChangelistTests(AuthViewsTestCase): - urls = 'django.contrib.auth.tests.urls_admin' - - # #20078 - users shouldn't be allowed to guess password hashes via - # repeated password__startswith queries. - def test_changelist_disallows_password_lookups(self): - # Make me a superuser before loging in. - User.objects.filter(username='testclient').update(is_staff=True, is_superuser=True) - self.login() - - # A lookup that tries to filter on password isn't OK - with self.assertRaises(SuspiciousOperation): - response = self.client.get('/admin/auth/user/?password__startswith=sha1$') -- cgit v1.3