mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
[1.8.x] Moved contrib.auth tests out of contrib.
Backport of 2d7aca3da0 from master
This commit is contained in:
@@ -1,110 +0,0 @@
|
||||
[
|
||||
{
|
||||
"pk": "1",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "testclient",
|
||||
"first_name": "Test",
|
||||
"last_name": "Client",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": false,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
|
||||
"email": "testclient@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "2",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "inactive",
|
||||
"first_name": "Inactive",
|
||||
"last_name": "User",
|
||||
"is_active": false,
|
||||
"is_superuser": false,
|
||||
"is_staff": false,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
|
||||
"email": "testclient2@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "3",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "staff",
|
||||
"first_name": "Staff",
|
||||
"last_name": "Member",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": true,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
|
||||
"email": "staffmember@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "4",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "empty_password",
|
||||
"first_name": "Empty",
|
||||
"last_name": "Password",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": false,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "",
|
||||
"email": "empty_password@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "5",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "unmanageable_password",
|
||||
"first_name": "Unmanageable",
|
||||
"last_name": "Password",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": false,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "$",
|
||||
"email": "unmanageable_password@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "6",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "unknown_password",
|
||||
"first_name": "Unknown",
|
||||
"last_name": "Password",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": false,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "foo$bar",
|
||||
"email": "unknown_password@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<django-objects version="1.0">
|
||||
<object pk="100" model="auth.user">
|
||||
<field type="CharField" name="username">super</field>
|
||||
<field type="CharField" name="first_name">Super</field>
|
||||
<field type="CharField" name="last_name">User</field>
|
||||
<field type="CharField" name="email">super@example.com</field>
|
||||
<field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
|
||||
<field type="BooleanField" name="is_staff">True</field>
|
||||
<field type="BooleanField" name="is_active">True</field>
|
||||
<field type="BooleanField" name="is_superuser">True</field>
|
||||
<field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
|
||||
<field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
|
||||
<field to="auth.group" name="groups" rel="ManyToManyRel"></field>
|
||||
<field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
|
||||
</object>
|
||||
</django-objects>
|
||||
@@ -1,14 +0,0 @@
|
||||
[
|
||||
{
|
||||
"pk": "1",
|
||||
"model": "auth.customuser",
|
||||
"fields": {
|
||||
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"email": "staffmember@example.com",
|
||||
"is_active": true,
|
||||
"is_admin": false,
|
||||
"date_of_birth": "1976-11-08"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,32 +0,0 @@
|
||||
[
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "auth.group",
|
||||
"fields": {
|
||||
"name": "my_group",
|
||||
"permissions": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "my_username",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"is_active": true,
|
||||
"is_superuser": true,
|
||||
"is_staff": true,
|
||||
"last_login": "2012-01-13 00:14:00",
|
||||
"groups": [
|
||||
[
|
||||
"my_group"
|
||||
]
|
||||
],
|
||||
"user_permissions": [],
|
||||
"password": "pbkdf2_sha256$10000$LUyhxJjuLwXF$f6Zbpnx1L5dPze8m0itBaHMDyZ/n6JyhuavQy2RrBIM=",
|
||||
"email": "email@example.com",
|
||||
"date_joined": "2012-01-13 00:14:00"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,30 +0,0 @@
|
||||
[
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "auth.group",
|
||||
"fields": {
|
||||
"name": "my_group",
|
||||
"permissions": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "my_username",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"is_active": true,
|
||||
"is_superuser": true,
|
||||
"is_staff": true,
|
||||
"last_login": "2012-01-13 00:14:00",
|
||||
"groups": [
|
||||
1
|
||||
],
|
||||
"user_permissions": [],
|
||||
"password": "pbkdf2_sha256$10000$LUyhxJjuLwXF$f6Zbpnx1L5dPze8m0itBaHMDyZ/n6JyhuavQy2RrBIM=",
|
||||
"email": "email@example.com",
|
||||
"date_joined": "2012-01-13 00:14:00"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
# The password for the fixture data users is 'password'
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# For testing that auth backends can be referenced using a convenience import
|
||||
from .test_auth_backends import ImportedModelBackend
|
||||
|
||||
__all__ = ['ImportedModelBackend']
|
||||
@@ -1,20 +0,0 @@
|
||||
import os
|
||||
|
||||
from django.utils._os import upath
|
||||
|
||||
AUTH_MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
)
|
||||
|
||||
AUTH_TEMPLATES = [{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(os.path.dirname(upath(__file__)), 'templates')],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': (
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
),
|
||||
},
|
||||
}]
|
||||
@@ -1 +0,0 @@
|
||||
{{ user }}
|
||||
@@ -1 +0,0 @@
|
||||
{% for m in messages %}{{ m }}{% endfor %}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{% if 'auth' in perms %}Has auth permissions{% endif %}
|
||||
{% if 'auth.add_permission' in perms %}Has auth.add_permission permissions{% endif %}
|
||||
{% if 'nonexisting' in perms %}nonexisting perm found{% endif %}
|
||||
{% if 'auth.nonexisting' in perms %}auth.nonexisting perm found{% endif %}
|
||||
@@ -1,4 +0,0 @@
|
||||
{% if perms.auth %}Has auth permissions{% endif %}
|
||||
{% if perms.auth.add_permission %}Has auth.add_permission permissions{% endif %}
|
||||
{% if perms.nonexisting %}nonexisting perm found{% endif %}
|
||||
{% if perms.auth.nonexisting in perms %}auth.nonexisting perm found{% endif %}
|
||||
@@ -1 +0,0 @@
|
||||
{% if session_accessed %}Session accessed{% else %}Session not accessed{% endif %}
|
||||
@@ -1,4 +0,0 @@
|
||||
unicode: {{ user }}
|
||||
id: {{ user.pk }}
|
||||
username: {{ user.username }}
|
||||
url: {% url 'userpage' user %}
|
||||
@@ -1 +0,0 @@
|
||||
<html><a href="{{ protocol }}://{{ domain }}/reset/{{ uid }}/{{ token }}/">Link</a></html>
|
||||
@@ -1 +0,0 @@
|
||||
Logged out
|
||||
@@ -1 +0,0 @@
|
||||
{{ form.as_ul }}
|
||||
@@ -1 +0,0 @@
|
||||
{{ form }}
|
||||
@@ -1 +0,0 @@
|
||||
Password reset successfully
|
||||
@@ -1,7 +0,0 @@
|
||||
Hello, {{ form.user }}.
|
||||
|
||||
{% if validlink %}
|
||||
Please enter your new password: {{ form }}
|
||||
{% else %}
|
||||
The password reset link was invalid
|
||||
{% endif %}
|
||||
@@ -1 +0,0 @@
|
||||
Email sent
|
||||
@@ -1 +0,0 @@
|
||||
{{ protocol }}://{{ domain }}/reset/{{ uid }}/{{ token }}/
|
||||
@@ -1 +0,0 @@
|
||||
{{ form }}
|
||||
@@ -1 +0,0 @@
|
||||
{% autoescape off %}Custom password reset on {{ site_name }}{% endautoescape %}
|
||||
@@ -1,636 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import date
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import BACKEND_SESSION_KEY, authenticate, get_user
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.contrib.auth.hashers import MD5PasswordHasher
|
||||
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
|
||||
from django.contrib.auth.tests.custom_user import (
|
||||
CustomPermissionsUser, CustomUser, ExtensionUser,
|
||||
)
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.http import HttpRequest
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
class CountingMD5PasswordHasher(MD5PasswordHasher):
|
||||
"""Hasher that counts how many times it computes a hash."""
|
||||
|
||||
calls = 0
|
||||
|
||||
def encode(self, *args, **kwargs):
|
||||
type(self).calls += 1
|
||||
return super(CountingMD5PasswordHasher, self).encode(*args, **kwargs)
|
||||
|
||||
|
||||
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 = 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)
|
||||
|
||||
# reloading user to purge the _perm_cache
|
||||
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
||||
self.assertEqual(user.get_all_permissions() == {'auth.test'}, 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)
|
||||
perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3')
|
||||
user.user_permissions.add(perm)
|
||||
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
||||
self.assertEqual(user.get_all_permissions(), {'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)
|
||||
user.groups.add(group)
|
||||
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
||||
exp = {'auth.test2', 'auth.test', 'auth.test3', 'auth.test_group'}
|
||||
self.assertEqual(user.get_all_permissions(), exp)
|
||||
self.assertEqual(user.get_group_permissions(), {'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)
|
||||
|
||||
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(), {'auth.test'})
|
||||
|
||||
def test_anonymous_has_no_permissions(self):
|
||||
"""
|
||||
#17903 -- Anonymous users shouldn't have permissions in
|
||||
ModelBackend.get_(all|user|group)_permissions().
|
||||
"""
|
||||
backend = ModelBackend()
|
||||
|
||||
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
||||
content_type = ContentType.objects.get_for_model(Group)
|
||||
user_perm = Permission.objects.create(name='test', content_type=content_type, codename='test_user')
|
||||
group_perm = Permission.objects.create(name='test2', content_type=content_type, codename='test_group')
|
||||
user.user_permissions.add(user_perm)
|
||||
|
||||
group = Group.objects.create(name='test_group')
|
||||
user.groups.add(group)
|
||||
group.permissions.add(group_perm)
|
||||
|
||||
self.assertEqual(backend.get_all_permissions(user), {'auth.test_user', 'auth.test_group'})
|
||||
self.assertEqual(backend.get_user_permissions(user), {'auth.test_user', 'auth.test_group'})
|
||||
self.assertEqual(backend.get_group_permissions(user), {'auth.test_group'})
|
||||
|
||||
user.is_anonymous = lambda: True
|
||||
|
||||
self.assertEqual(backend.get_all_permissions(user), set())
|
||||
self.assertEqual(backend.get_user_permissions(user), set())
|
||||
self.assertEqual(backend.get_group_permissions(user), set())
|
||||
|
||||
def test_inactive_has_no_permissions(self):
|
||||
"""
|
||||
#17903 -- Inactive users shouldn't have permissions in
|
||||
ModelBackend.get_(all|user|group)_permissions().
|
||||
"""
|
||||
backend = ModelBackend()
|
||||
|
||||
user = self.UserModel._default_manager.get(pk=self.user.pk)
|
||||
content_type = ContentType.objects.get_for_model(Group)
|
||||
user_perm = Permission.objects.create(name='test', content_type=content_type, codename='test_user')
|
||||
group_perm = Permission.objects.create(name='test2', content_type=content_type, codename='test_group')
|
||||
user.user_permissions.add(user_perm)
|
||||
|
||||
group = Group.objects.create(name='test_group')
|
||||
user.groups.add(group)
|
||||
group.permissions.add(group_perm)
|
||||
|
||||
self.assertEqual(backend.get_all_permissions(user), {'auth.test_user', 'auth.test_group'})
|
||||
self.assertEqual(backend.get_user_permissions(user), {'auth.test_user', 'auth.test_group'})
|
||||
self.assertEqual(backend.get_group_permissions(user), {'auth.test_group'})
|
||||
|
||||
user.is_active = False
|
||||
user.save()
|
||||
|
||||
self.assertEqual(backend.get_all_permissions(user), set())
|
||||
self.assertEqual(backend.get_user_permissions(user), set())
|
||||
self.assertEqual(backend.get_group_permissions(user), set())
|
||||
|
||||
def test_get_all_superuser_permissions(self):
|
||||
"""A superuser has all permissions. Refs #14795."""
|
||||
user = self.UserModel._default_manager.get(pk=self.superuser.pk)
|
||||
self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all()))
|
||||
|
||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.tests.test_auth_backends.CountingMD5PasswordHasher',))
|
||||
def test_authentication_timing(self):
|
||||
"""Hasher is run once regardless of whether the user exists. Refs #20760."""
|
||||
# Re-set the password, because this tests overrides PASSWORD_HASHERS
|
||||
self.user.set_password('test')
|
||||
self.user.save()
|
||||
|
||||
CountingMD5PasswordHasher.calls = 0
|
||||
username = getattr(self.user, self.UserModel.USERNAME_FIELD)
|
||||
authenticate(username=username, password='test')
|
||||
self.assertEqual(CountingMD5PasswordHasher.calls, 1)
|
||||
|
||||
CountingMD5PasswordHasher.calls = 0
|
||||
authenticate(username='no_such_user', password='test')
|
||||
self.assertEqual(CountingMD5PasswordHasher.calls, 1)
|
||||
|
||||
|
||||
@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()), {'simple'})
|
||||
self.assertEqual(self.user2.get_all_permissions(TestObj()), {'simple', 'advanced'})
|
||||
self.assertEqual(self.user2.get_all_permissions(), set())
|
||||
|
||||
def test_get_group_permissions(self):
|
||||
group = Group.objects.create(name='test_group')
|
||||
self.user3.groups.add(group)
|
||||
self.assertEqual(self.user3.get_group_permissions(TestObj()), {'group_perm'})
|
||||
|
||||
|
||||
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()), {'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 an 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 in `authenticate`, `has_perm` and `has_module_perms`.
|
||||
"""
|
||||
supports_object_permissions = True
|
||||
supports_anonymous_user = True
|
||||
supports_inactive_user = True
|
||||
|
||||
def authenticate(self, username=None, password=None):
|
||||
raise PermissionDenied
|
||||
|
||||
def has_perm(self, user_obj, perm, obj=None):
|
||||
raise PermissionDenied
|
||||
|
||||
def has_module_perms(self, user_obj, app_label):
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
@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)
|
||||
|
||||
@override_settings(AUTHENTICATION_BACKENDS=(backend, ) +
|
||||
tuple(settings.AUTHENTICATION_BACKENDS))
|
||||
def test_has_perm_denied(self):
|
||||
content_type = ContentType.objects.get_for_model(Group)
|
||||
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
||||
self.user1.user_permissions.add(perm)
|
||||
|
||||
self.assertIs(self.user1.has_perm('auth.test'), False)
|
||||
self.assertIs(self.user1.has_module_perms('auth'), False)
|
||||
|
||||
@override_settings(AUTHENTICATION_BACKENDS=tuple(
|
||||
settings.AUTHENTICATION_BACKENDS) + (backend, ))
|
||||
def test_has_perm(self):
|
||||
content_type = ContentType.objects.get_for_model(Group)
|
||||
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
||||
self.user1.user_permissions.add(perm)
|
||||
|
||||
self.assertIs(self.user1.has_perm('auth.test'), True)
|
||||
self.assertIs(self.user1.has_module_perms('auth'), True)
|
||||
|
||||
|
||||
class NewModelBackend(ModelBackend):
|
||||
pass
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class ChangedBackendSettingsTest(TestCase):
|
||||
"""
|
||||
Tests for changes in the settings.AUTHENTICATION_BACKENDS
|
||||
"""
|
||||
backend = 'django.contrib.auth.tests.test_auth_backends.NewModelBackend'
|
||||
|
||||
TEST_USERNAME = 'test_user'
|
||||
TEST_PASSWORD = 'test_password'
|
||||
TEST_EMAIL = 'test@example.com'
|
||||
|
||||
def setUp(self):
|
||||
User.objects.create_user(self.TEST_USERNAME,
|
||||
self.TEST_EMAIL,
|
||||
self.TEST_PASSWORD)
|
||||
|
||||
@override_settings(AUTHENTICATION_BACKENDS=(backend, ))
|
||||
def test_changed_backend_settings(self):
|
||||
"""
|
||||
Tests that removing a backend configured in AUTHENTICATION_BACKENDS
|
||||
make already logged-in users disconnect.
|
||||
"""
|
||||
|
||||
# Get a session for the test user
|
||||
self.assertTrue(self.client.login(
|
||||
username=self.TEST_USERNAME,
|
||||
password=self.TEST_PASSWORD)
|
||||
)
|
||||
|
||||
# Prepare a request object
|
||||
request = HttpRequest()
|
||||
request.session = self.client.session
|
||||
|
||||
# Remove NewModelBackend
|
||||
with self.settings(AUTHENTICATION_BACKENDS=(
|
||||
'django.contrib.auth.backends.ModelBackend',)):
|
||||
# Get the user from the request
|
||||
user = get_user(request)
|
||||
|
||||
# Assert that the user retrieval is successful and the user is
|
||||
# anonymous as the backend is not longer available.
|
||||
self.assertIsNotNone(user)
|
||||
self.assertTrue(user.is_anonymous())
|
||||
|
||||
|
||||
class TypeErrorBackend(object):
|
||||
"""
|
||||
Always raises TypeError.
|
||||
"""
|
||||
supports_object_permissions = True
|
||||
supports_anonymous_user = True
|
||||
supports_inactive_user = True
|
||||
|
||||
def authenticate(self, username=None, password=None):
|
||||
raise TypeError
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class TypeErrorBackendTest(TestCase):
|
||||
"""
|
||||
Tests that a TypeError within a backend is propagated properly.
|
||||
|
||||
Regression test for ticket #18171
|
||||
"""
|
||||
backend = 'django.contrib.auth.tests.test_auth_backends.TypeErrorBackend'
|
||||
|
||||
def setUp(self):
|
||||
self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
|
||||
|
||||
@override_settings(AUTHENTICATION_BACKENDS=(backend, ))
|
||||
def test_type_error_raised(self):
|
||||
self.assertRaises(TypeError, authenticate, username='test', password='test')
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class ImproperlyConfiguredUserModelTest(TestCase):
|
||||
"""
|
||||
Tests that an exception from within get_user_model is propagated and doesn't
|
||||
raise an UnboundLocalError.
|
||||
|
||||
Regression test for ticket #21439
|
||||
"""
|
||||
def setUp(self):
|
||||
self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
|
||||
self.client.login(
|
||||
username='test',
|
||||
password='test'
|
||||
)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='thismodel.doesntexist')
|
||||
def test_does_not_shadow_exception(self):
|
||||
# Prepare a request object
|
||||
request = HttpRequest()
|
||||
request.session = self.client.session
|
||||
|
||||
self.assertRaises(ImproperlyConfigured, get_user, request)
|
||||
|
||||
|
||||
class ImportedModelBackend(ModelBackend):
|
||||
pass
|
||||
|
||||
|
||||
class ImportedBackendTests(TestCase):
|
||||
"""
|
||||
#23925 - The backend path added to the session should be the same
|
||||
as the one defined in AUTHENTICATION_BACKENDS setting.
|
||||
"""
|
||||
|
||||
backend = 'django.contrib.auth.tests.backend_alias.ImportedModelBackend'
|
||||
|
||||
@override_settings(AUTHENTICATION_BACKENDS=(backend, ))
|
||||
def test_backend_path(self):
|
||||
username = 'username'
|
||||
password = 'password'
|
||||
User.objects.create_user(username, 'email', password)
|
||||
self.assertTrue(self.client.login(username=username, password=password))
|
||||
request = HttpRequest()
|
||||
request.session = self.client.session
|
||||
self.assertEqual(request.session[BACKEND_SESSION_KEY], self.backend)
|
||||
@@ -1,119 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
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.dispatch import receiver
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.signals import setting_changed
|
||||
from django.utils import translation
|
||||
|
||||
|
||||
@receiver(setting_changed)
|
||||
def user_model_swapped(**kwargs):
|
||||
if kwargs['setting'] == 'AUTH_USER_MODEL':
|
||||
from django.db.models.manager import ensure_default_manager
|
||||
# Reset User manager
|
||||
setattr(User, 'objects', User._default_manager)
|
||||
ensure_default_manager(User)
|
||||
apps.clear_cache()
|
||||
|
||||
|
||||
@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 username getter
|
||||
self.assertEqual(u.get_username(), 'testuser')
|
||||
|
||||
# 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.assertEqual(a.username, '')
|
||||
self.assertEqual(a.get_username(), '')
|
||||
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_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')
|
||||
@@ -1,158 +0,0 @@
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.context_processors import PermLookupDict, PermWrapper
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Q
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from .settings import AUTH_MIDDLEWARE_CLASSES, AUTH_TEMPLATES
|
||||
from .utils import skipIfCustomUser
|
||||
|
||||
|
||||
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.assertIn('mockapp', perms)
|
||||
self.assertNotIn('nonexisting', perms)
|
||||
self.assertIn('mockapp.someperm', perms)
|
||||
self.assertNotIn('mockapp.nonexisting', 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(
|
||||
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||
ROOT_URLCONF='django.contrib.auth.tests.urls',
|
||||
TEMPLATES=AUTH_TEMPLATES,
|
||||
USE_TZ=False, # required for loading the fixture
|
||||
)
|
||||
class AuthContextProcessorTests(TestCase):
|
||||
"""
|
||||
Tests for the ``django.contrib.auth.context_processors.auth`` processor
|
||||
"""
|
||||
fixtures = ['context-processors-users.xml']
|
||||
|
||||
@override_settings(MIDDLEWARE_CLASSES=AUTH_MIDDLEWARE_CLASSES)
|
||||
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=AUTH_MIDDLEWARE_CLASSES)
|
||||
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 <type 'exceptions.AttributeError'>
|
||||
# ignored"
|
||||
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'])
|
||||
@@ -1,109 +0,0 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import models
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.tests.test_views import AuthViewsTestCase
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponse
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
@override_settings(ROOT_URLCONF='django.contrib.auth.tests.urls')
|
||||
class LoginRequiredTestCase(AuthViewsTestCase):
|
||||
"""
|
||||
Tests the login_required decorators
|
||||
"""
|
||||
|
||||
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=None):
|
||||
"""
|
||||
Check that login_required works on a simple view wrapped in a
|
||||
login_required decorator.
|
||||
"""
|
||||
if login_url is None:
|
||||
login_url = settings.LOGIN_URL
|
||||
response = self.client.get(view_url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn(login_url, 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/')
|
||||
|
||||
|
||||
class PermissionsRequiredDecoratorTest(TestCase):
|
||||
"""
|
||||
Tests for the permission_required decorator
|
||||
"""
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create(username='joe', password='qwerty')
|
||||
self.factory = RequestFactory()
|
||||
# Add permissions auth.add_customuser and auth.change_customuser
|
||||
perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser'))
|
||||
self.user.user_permissions.add(*perms)
|
||||
|
||||
def test_many_permissions_pass(self):
|
||||
|
||||
@permission_required(['auth.add_customuser', 'auth.change_customuser'])
|
||||
def a_view(request):
|
||||
return HttpResponse()
|
||||
request = self.factory.get('/rand')
|
||||
request.user = self.user
|
||||
resp = a_view(request)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_single_permission_pass(self):
|
||||
|
||||
@permission_required('auth.add_customuser')
|
||||
def a_view(request):
|
||||
return HttpResponse()
|
||||
request = self.factory.get('/rand')
|
||||
request.user = self.user
|
||||
resp = a_view(request)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_permissioned_denied_redirect(self):
|
||||
|
||||
@permission_required(['auth.add_customuser', 'auth.change_customuser', 'non-existent-permission'])
|
||||
def a_view(request):
|
||||
return HttpResponse()
|
||||
request = self.factory.get('/rand')
|
||||
request.user = self.user
|
||||
resp = a_view(request)
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
|
||||
def test_permissioned_denied_exception_raised(self):
|
||||
|
||||
@permission_required([
|
||||
'auth.add_customuser', 'auth.change_customuser', 'non-existent-permission'
|
||||
], raise_exception=True)
|
||||
def a_view(request):
|
||||
return HttpResponse()
|
||||
request = self.factory.get('/rand')
|
||||
request.user = self.user
|
||||
self.assertRaises(PermissionDenied, a_view, request)
|
||||
@@ -1,536 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import (
|
||||
AuthenticationForm, PasswordChangeForm, PasswordResetForm,
|
||||
ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget, SetPasswordForm,
|
||||
UserChangeForm, UserCreationForm,
|
||||
)
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import mail
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.forms.fields import CharField, Field
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils import translation
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from .settings import AUTH_TEMPLATES
|
||||
from .utils import skipIfCustomUser
|
||||
|
||||
|
||||
@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(User._meta.get_field('username').error_messages['unique'])])
|
||||
|
||||
def test_invalid_data(self):
|
||||
data = {
|
||||
'username': 'jsmith!',
|
||||
'password1': 'test123',
|
||||
'password2': 'test123',
|
||||
}
|
||||
form = UserCreationForm(data)
|
||||
self.assertFalse(form.is_valid())
|
||||
validator = next(v for v in User._meta.get_field('username').validators if v.code == 'invalid')
|
||||
self.assertEqual(form["username"].errors, [force_text(validator.message)])
|
||||
|
||||
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), '<User: jsmith@example.com>')
|
||||
|
||||
|
||||
@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), 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_custom_login_allowed_policy(self):
|
||||
# The user is inactive, but our custom form policy allows them to log in.
|
||||
data = {
|
||||
'username': 'inactive',
|
||||
'password': 'password',
|
||||
}
|
||||
|
||||
class AuthenticationFormWithInactiveUsersOkay(AuthenticationForm):
|
||||
def confirm_login_allowed(self, user):
|
||||
pass
|
||||
|
||||
form = AuthenticationFormWithInactiveUsersOkay(None, data)
|
||||
self.assertTrue(form.is_valid())
|
||||
|
||||
# If we want to disallow some logins according to custom logic,
|
||||
# we should raise a django.forms.ValidationError in the form.
|
||||
class PickyAuthenticationForm(AuthenticationForm):
|
||||
def confirm_login_allowed(self, user):
|
||||
if user.username == "inactive":
|
||||
raise forms.ValidationError("This user is disallowed.")
|
||||
raise forms.ValidationError("Sorry, nobody's allowed in.")
|
||||
|
||||
form = PickyAuthenticationForm(None, data)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(form.non_field_errors(), ['This user is disallowed.'])
|
||||
|
||||
data = {
|
||||
'username': 'testclient',
|
||||
'password': 'password',
|
||||
}
|
||||
form = PickyAuthenticationForm(None, data)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(form.non_field_errors(), ["Sorry, nobody's allowed in."])
|
||||
|
||||
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")
|
||||
|
||||
def test_username_field_label_not_set(self):
|
||||
|
||||
class CustomAuthenticationForm(AuthenticationForm):
|
||||
username = CharField()
|
||||
|
||||
form = CustomAuthenticationForm()
|
||||
username_field = User._meta.get_field(User.USERNAME_FIELD)
|
||||
self.assertEqual(form.fields['username'].label, capfirst(username_field.verbose_name))
|
||||
|
||||
def test_username_field_label_empty_string(self):
|
||||
|
||||
class CustomAuthenticationForm(AuthenticationForm):
|
||||
username = CharField(label='')
|
||||
|
||||
form = CustomAuthenticationForm()
|
||||
self.assertEqual(form.fields['username'].label, "")
|
||||
|
||||
|
||||
@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())
|
||||
validator = next(v for v in User._meta.get_field('username').validators if v.code == 'invalid')
|
||||
self.assertEqual(form["username"].errors, [force_text(validator.message)])
|
||||
|
||||
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
|
||||
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(
|
||||
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||
TEMPLATES=AUTH_TEMPLATES,
|
||||
USE_TZ=False,
|
||||
)
|
||||
class PasswordResetFormTest(TestCase):
|
||||
|
||||
fixtures = ['authtestdata.json']
|
||||
|
||||
def create_dummy_user(self):
|
||||
"""
|
||||
Create a user and return 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_nonexistent_email(self):
|
||||
"""
|
||||
Test nonexistent 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)
|
||||
|
||||
def test_cleaned_data(self):
|
||||
(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)
|
||||
|
||||
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_custom_email_constructor(self):
|
||||
data = {'email': 'testclient@example.com'}
|
||||
|
||||
class CustomEmailPasswordResetForm(PasswordResetForm):
|
||||
def send_mail(self, subject_template_name, email_template_name,
|
||||
context, from_email, to_email,
|
||||
html_email_template_name=None):
|
||||
EmailMultiAlternatives(
|
||||
"Forgot your password?",
|
||||
"Sorry to hear you forgot your password.",
|
||||
None, [to_email],
|
||||
['site_monitor@example.com'],
|
||||
headers={'Reply-To': 'webmaster@example.com'},
|
||||
alternatives=[("Really sorry to hear you forgot your password.",
|
||||
"text/html")]).send()
|
||||
|
||||
form = CustomEmailPasswordResetForm(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, 'Forgot your password?')
|
||||
self.assertEqual(mail.outbox[0].bcc, ['site_monitor@example.com'])
|
||||
self.assertEqual(mail.outbox[0].content_subtype, "plain")
|
||||
|
||||
def test_preserve_username_case(self):
|
||||
"""
|
||||
Preserve the case of the user name (before the @ in the email address)
|
||||
when creating a user (#5605).
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Test 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())
|
||||
form.save()
|
||||
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)
|
||||
|
||||
def test_save_plaintext_email(self):
|
||||
"""
|
||||
Test the PasswordResetForm.save() method with no html_email_template_name
|
||||
parameter passed in.
|
||||
Test to ensure original behavior is unchanged after the parameter was added.
|
||||
"""
|
||||
(user, username, email) = self.create_dummy_user()
|
||||
form = PasswordResetForm({"email": email})
|
||||
self.assertTrue(form.is_valid())
|
||||
form.save()
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
message = mail.outbox[0].message()
|
||||
self.assertFalse(message.is_multipart())
|
||||
self.assertEqual(message.get_content_type(), 'text/plain')
|
||||
self.assertEqual(message.get('subject'), 'Custom password reset on example.com')
|
||||
self.assertEqual(len(mail.outbox[0].alternatives), 0)
|
||||
self.assertEqual(message.get_all('to'), [email])
|
||||
self.assertTrue(re.match(r'^http://example.com/reset/[\w+/-]', message.get_payload()))
|
||||
|
||||
def test_save_html_email_template_name(self):
|
||||
"""
|
||||
Test the PasswordResetFOrm.save() method with html_email_template_name
|
||||
parameter specified.
|
||||
Test to ensure that a multipart email is sent with both text/plain
|
||||
and text/html parts.
|
||||
"""
|
||||
(user, username, email) = self.create_dummy_user()
|
||||
form = PasswordResetForm({"email": email})
|
||||
self.assertTrue(form.is_valid())
|
||||
form.save(html_email_template_name='registration/html_password_reset_email.html')
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(len(mail.outbox[0].alternatives), 1)
|
||||
message = mail.outbox[0].message()
|
||||
self.assertEqual(message.get('subject'), 'Custom password reset on example.com')
|
||||
self.assertEqual(len(message.get_payload()), 2)
|
||||
self.assertTrue(message.is_multipart())
|
||||
self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain')
|
||||
self.assertEqual(message.get_payload(1).get_content_type(), 'text/html')
|
||||
self.assertEqual(message.get_all('to'), [email])
|
||||
self.assertTrue(re.match(r'^http://example.com/reset/[\w/-]+', message.get_payload(0).get_payload()))
|
||||
self.assertTrue(
|
||||
re.match(r'^<html><a href="http://example.com/reset/[\w/-]+/">Link</a></html>$',
|
||||
message.get_payload(1).get_payload())
|
||||
)
|
||||
|
||||
|
||||
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'))
|
||||
@@ -1,80 +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 Group, User
|
||||
from django.contrib.auth.tests.custom_user import CustomUser
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.test import TransactionTestCase, override_settings
|
||||
|
||||
|
||||
# This must be a TransactionTestCase because the WSGI auth handler performs
|
||||
# its own transaction management.
|
||||
class ModWsgiHandlerTestCase(TransactionTestCase):
|
||||
"""
|
||||
Tests for the mod_wsgi authentication handler
|
||||
"""
|
||||
|
||||
available_apps = [
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
]
|
||||
|
||||
@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.assertIsNone(check_password({}, 'unknown', ''))
|
||||
|
||||
# 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.assertIsNone(check_password({}, 'unknown', ''))
|
||||
|
||||
# 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'), [])
|
||||
@@ -1,327 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.conf.global_settings import PASSWORD_HASHERS
|
||||
from django.contrib.auth.hashers import (
|
||||
UNUSABLE_PASSWORD_PREFIX, UNUSABLE_PASSWORD_SUFFIX_LENGTH,
|
||||
BasePasswordHasher, PBKDF2PasswordHasher, PBKDF2SHA1PasswordHasher,
|
||||
check_password, get_hasher, identify_hasher, is_password_usable,
|
||||
make_password,
|
||||
)
|
||||
from django.test import SimpleTestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.utils import six
|
||||
|
||||
try:
|
||||
import crypt
|
||||
except ImportError:
|
||||
crypt = None
|
||||
|
||||
try:
|
||||
import bcrypt
|
||||
except ImportError:
|
||||
bcrypt = None
|
||||
|
||||
|
||||
class PBKDF2SingleIterationHasher(PBKDF2PasswordHasher):
|
||||
iterations = 1
|
||||
|
||||
|
||||
@override_settings(PASSWORD_HASHERS=PASSWORD_HASHERS)
|
||||
class TestUtilsHashPass(SimpleTestCase):
|
||||
|
||||
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))
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('')
|
||||
self.assertTrue(blank_encoded.startswith('pbkdf2_sha256$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_encoded))
|
||||
|
||||
def test_pkbdf2(self):
|
||||
encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256')
|
||||
self.assertEqual(encoded,
|
||||
'pbkdf2_sha256$20000$seasalt$oBSd886ysm3AqYun62DOdin8YcfbU1z9cksZSuLP9r0=')
|
||||
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")
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', 'seasalt', 'pbkdf2_sha256')
|
||||
self.assertTrue(blank_encoded.startswith('pbkdf2_sha256$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_encoded))
|
||||
|
||||
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")
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', 'seasalt', 'sha1')
|
||||
self.assertTrue(blank_encoded.startswith('sha1$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_encoded))
|
||||
|
||||
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")
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', 'seasalt', 'md5')
|
||||
self.assertTrue(blank_encoded.startswith('md5$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_encoded))
|
||||
|
||||
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))
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', '', 'unsalted_md5')
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_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))
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', '', 'unsalted_sha1')
|
||||
self.assertTrue(blank_encoded.startswith('sha1$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_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")
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', 'ab', 'crypt')
|
||||
self.assertTrue(blank_encoded.startswith('crypt$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_encoded))
|
||||
|
||||
@skipUnless(bcrypt, "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))
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', hasher='bcrypt_sha256')
|
||||
self.assertTrue(blank_encoded.startswith('bcrypt_sha256$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_encoded))
|
||||
|
||||
@skipUnless(bcrypt, "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")
|
||||
# Blank passwords
|
||||
blank_encoded = make_password('', hasher='bcrypt')
|
||||
self.assertTrue(blank_encoded.startswith('bcrypt$'))
|
||||
self.assertTrue(is_password_usable(blank_encoded))
|
||||
self.assertTrue(check_password('', blank_encoded))
|
||||
self.assertFalse(check_password(' ', blank_encoded))
|
||||
|
||||
def test_unusable(self):
|
||||
encoded = make_password(None)
|
||||
self.assertEqual(len(encoded), len(UNUSABLE_PASSWORD_PREFIX) + UNUSABLE_PASSWORD_SUFFIX_LENGTH)
|
||||
self.assertFalse(is_password_usable(encoded))
|
||||
self.assertFalse(check_password(None, encoded))
|
||||
self.assertFalse(check_password(encoded, encoded))
|
||||
self.assertFalse(check_password(UNUSABLE_PASSWORD_PREFIX, 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)
|
||||
# Assert that the unusable passwords actually contain a random part.
|
||||
# This might fail one day due to a hash collision.
|
||||
self.assertNotEqual(encoded, make_password(None), "Random password collision?")
|
||||
|
||||
def test_unspecified_password(self):
|
||||
"""
|
||||
Makes sure specifying no plain password with a valid encoded password
|
||||
returns `False`.
|
||||
"""
|
||||
self.assertFalse(check_password(None, make_password('lètmein')))
|
||||
|
||||
def test_bad_algorithm(self):
|
||||
with self.assertRaises(ValueError):
|
||||
make_password('lètmein', hasher='lolcat')
|
||||
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', 'seasalt2')
|
||||
self.assertEqual(encoded,
|
||||
'pbkdf2_sha256$20000$seasalt2$Flpve/uAcyo6+IFI6YAhjeABGPVbRQjzHDxRhqxewgw=')
|
||||
self.assertTrue(hasher.verify('lètmein', encoded))
|
||||
|
||||
def test_low_level_pbkdf2_sha1(self):
|
||||
hasher = PBKDF2SHA1PasswordHasher()
|
||||
encoded = hasher.encode('lètmein', 'seasalt2')
|
||||
self.assertEqual(encoded,
|
||||
'pbkdf2_sha1$20000$seasalt2$pJt86NmjAweBY1StBvxCu7l1o9o=')
|
||||
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'])
|
||||
|
||||
def test_pbkdf2_upgrade(self):
|
||||
hasher = get_hasher('default')
|
||||
self.assertEqual('pbkdf2_sha256', hasher.algorithm)
|
||||
self.assertNotEqual(hasher.iterations, 1)
|
||||
|
||||
old_iterations = hasher.iterations
|
||||
try:
|
||||
# Generate a password with 1 iteration.
|
||||
hasher.iterations = 1
|
||||
encoded = make_password('letmein')
|
||||
algo, iterations, salt, hash = encoded.split('$', 3)
|
||||
self.assertEqual(iterations, '1')
|
||||
|
||||
state = {'upgraded': False}
|
||||
|
||||
def setter(password):
|
||||
state['upgraded'] = True
|
||||
|
||||
# Check that no upgrade is triggered
|
||||
self.assertTrue(check_password('letmein', encoded, setter))
|
||||
self.assertFalse(state['upgraded'])
|
||||
|
||||
# Revert to the old iteration count and ...
|
||||
hasher.iterations = old_iterations
|
||||
|
||||
# ... check if the password would get updated to the new iteration count.
|
||||
self.assertTrue(check_password('letmein', encoded, setter))
|
||||
self.assertTrue(state['upgraded'])
|
||||
finally:
|
||||
hasher.iterations = old_iterations
|
||||
|
||||
def test_pbkdf2_upgrade_new_hasher(self):
|
||||
hasher = get_hasher('default')
|
||||
self.assertEqual('pbkdf2_sha256', hasher.algorithm)
|
||||
self.assertNotEqual(hasher.iterations, 1)
|
||||
|
||||
state = {'upgraded': False}
|
||||
|
||||
def setter(password):
|
||||
state['upgraded'] = True
|
||||
|
||||
with self.settings(PASSWORD_HASHERS=[
|
||||
'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']):
|
||||
encoded = make_password('letmein')
|
||||
algo, iterations, salt, hash = encoded.split('$', 3)
|
||||
self.assertEqual(iterations, '1')
|
||||
|
||||
# Check that no upgrade is triggered
|
||||
self.assertTrue(check_password('letmein', encoded, setter))
|
||||
self.assertFalse(state['upgraded'])
|
||||
|
||||
# Revert to the old iteration count and check if the password would get
|
||||
# updated to the new iteration count.
|
||||
with self.settings(PASSWORD_HASHERS=[
|
||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||
'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']):
|
||||
self.assertTrue(check_password('letmein', encoded, setter))
|
||||
self.assertTrue(state['upgraded'])
|
||||
|
||||
def test_load_library_no_algorithm(self):
|
||||
with self.assertRaises(ValueError) as e:
|
||||
BasePasswordHasher()._load_library()
|
||||
self.assertEqual("Hasher 'BasePasswordHasher' doesn't specify a "
|
||||
"library attribute", str(e.exception))
|
||||
|
||||
def test_load_library_importerror(self):
|
||||
PlainHasher = type(str('PlainHasher'), (BasePasswordHasher,),
|
||||
{'algorithm': 'plain', 'library': 'plain'})
|
||||
# Python 3.3 adds quotes around module name
|
||||
with six.assertRaisesRegex(self, ValueError,
|
||||
"Couldn't load 'PlainHasher' algorithm library: No module named '?plain'?"):
|
||||
PlainHasher()._load_library()
|
||||
@@ -1,577 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import locale
|
||||
import sys
|
||||
from datetime import date
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import management, models
|
||||
from django.contrib.auth.checks import check_user_model
|
||||
from django.contrib.auth.management import create_permissions
|
||||
from django.contrib.auth.management.commands import (
|
||||
changepassword, createsuperuser,
|
||||
)
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.auth.tests.custom_user import (
|
||||
CustomUser, CustomUserWithFK, Email,
|
||||
)
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core import checks, exceptions
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import CommandError
|
||||
from django.test import TestCase, override_settings, override_system_checks
|
||||
from django.utils import six
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
|
||||
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 six.PY2:
|
||||
# getpass on Windows only supports prompt as bytestring (#19807)
|
||||
assert isinstance(prompt, six.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
|
||||
|
||||
|
||||
class MockTTY(object):
|
||||
"""
|
||||
A fake stdin object that pretends to be a TTY to be used in conjunction
|
||||
with mock_inputs.
|
||||
"""
|
||||
def isatty(self):
|
||||
return True
|
||||
|
||||
|
||||
@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 = six.StringIO()
|
||||
self.stderr = six.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(username="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(username="joe", stdout=self.stdout, stderr=self.stderr)
|
||||
|
||||
def test_that_changepassword_command_works_with_nonascii_output(self):
|
||||
"""
|
||||
#21627 -- Executing the changepassword management command should allow
|
||||
non-ASCII characters from the User object representation.
|
||||
"""
|
||||
# 'Julia' with accented 'u':
|
||||
models.User.objects.create_user(username='J\xfalia', password='qwerty')
|
||||
|
||||
command = changepassword.Command()
|
||||
command._get_pass = lambda *args: 'not qwerty'
|
||||
|
||||
command.execute(username="J\xfalia", stdout=self.stdout)
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
@override_settings(SILENCED_SYSTEM_CHECKS=['fields.W342']) # ForeignKey(unique=True)
|
||||
class CreatesuperuserManagementCommandTestCase(TestCase):
|
||||
|
||||
def test_basic_usage(self):
|
||||
"Check the operation of the createsuperuser management command"
|
||||
# We can use the management command to create a superuser
|
||||
new_io = six.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())
|
||||
|
||||
@mock_inputs({'password': "nopasswd"})
|
||||
def test_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,
|
||||
stdin=MockTTY(),
|
||||
)
|
||||
|
||||
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\u017eivatel': 'foo', # username (cz)
|
||||
'email': 'nolocale@somewhere.org'})
|
||||
def test_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\u017eivatel')
|
||||
new_io = six.StringIO()
|
||||
try:
|
||||
call_command(
|
||||
"createsuperuser",
|
||||
interactive=True,
|
||||
stdout=new_io,
|
||||
stdin=MockTTY(),
|
||||
)
|
||||
finally:
|
||||
username_field.verbose_name = old_verbose_name
|
||||
|
||||
command_output = new_io.getvalue().strip()
|
||||
self.assertEqual(command_output, 'Superuser created successfully.')
|
||||
|
||||
def test_verbosity_zero(self):
|
||||
# We can suppress output on the management command
|
||||
new_io = six.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 = six.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 = six.StringIO()
|
||||
call_command(
|
||||
"createsuperuser",
|
||||
interactive=False,
|
||||
email="joe@somewhere.org",
|
||||
date_of_birth="1976-04-01",
|
||||
stdout=new_io,
|
||||
)
|
||||
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 = six.StringIO()
|
||||
with self.assertRaises(CommandError):
|
||||
call_command(
|
||||
"createsuperuser",
|
||||
interactive=False,
|
||||
username="joe@somewhere.org",
|
||||
stdout=new_io,
|
||||
stderr=new_io,
|
||||
)
|
||||
|
||||
self.assertEqual(CustomUser._default_manager.count(), 0)
|
||||
|
||||
def test_skip_if_not_in_TTY(self):
|
||||
"""
|
||||
If the command is not called from a TTY, it should be skipped and a
|
||||
message should be displayed (#7423).
|
||||
"""
|
||||
class FakeStdin(object):
|
||||
"""A fake stdin object that has isatty() return False."""
|
||||
def isatty(self):
|
||||
return False
|
||||
|
||||
out = six.StringIO()
|
||||
call_command(
|
||||
"createsuperuser",
|
||||
stdin=FakeStdin(),
|
||||
stdout=out,
|
||||
interactive=True,
|
||||
)
|
||||
|
||||
self.assertEqual(User._default_manager.count(), 0)
|
||||
self.assertIn("Superuser creation skipped", out.getvalue())
|
||||
|
||||
def test_passing_stdin(self):
|
||||
"""
|
||||
You can pass a stdin object as an option and it should be
|
||||
available on self.stdin.
|
||||
If no such option is passed, it defaults to sys.stdin.
|
||||
"""
|
||||
sentinel = object()
|
||||
command = createsuperuser.Command()
|
||||
command.check = lambda: []
|
||||
command.execute(
|
||||
stdin=sentinel,
|
||||
stdout=six.StringIO(),
|
||||
stderr=six.StringIO(),
|
||||
interactive=False,
|
||||
verbosity=0,
|
||||
username='janet',
|
||||
email='janet@example.com',
|
||||
)
|
||||
self.assertIs(command.stdin, sentinel)
|
||||
|
||||
command = createsuperuser.Command()
|
||||
command.check = lambda: []
|
||||
command.execute(
|
||||
stdout=six.StringIO(),
|
||||
stderr=six.StringIO(),
|
||||
interactive=False,
|
||||
verbosity=0,
|
||||
username='joe',
|
||||
email='joe@example.com',
|
||||
)
|
||||
self.assertIs(command.stdin, sys.stdin)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserWithFK')
|
||||
def test_fields_with_fk(self):
|
||||
new_io = six.StringIO()
|
||||
group = Group.objects.create(name='mygroup')
|
||||
email = Email.objects.create(email='mymail@gmail.com')
|
||||
call_command(
|
||||
'createsuperuser',
|
||||
interactive=False,
|
||||
username=email.pk,
|
||||
email=email.email,
|
||||
group=group.pk,
|
||||
stdout=new_io,
|
||||
)
|
||||
command_output = new_io.getvalue().strip()
|
||||
self.assertEqual(command_output, 'Superuser created successfully.')
|
||||
u = CustomUserWithFK._default_manager.get(email=email)
|
||||
self.assertEqual(u.username, email)
|
||||
self.assertEqual(u.group, group)
|
||||
|
||||
non_existent_email = 'mymail2@gmail.com'
|
||||
with self.assertRaisesMessage(CommandError,
|
||||
'email instance with email %r does not exist.' % non_existent_email):
|
||||
call_command(
|
||||
'createsuperuser',
|
||||
interactive=False,
|
||||
username=email.pk,
|
||||
email=non_existent_email,
|
||||
stdout=new_io,
|
||||
)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserWithFK')
|
||||
def test_fields_with_fk_interactive(self):
|
||||
new_io = six.StringIO()
|
||||
group = Group.objects.create(name='mygroup')
|
||||
email = Email.objects.create(email='mymail@gmail.com')
|
||||
|
||||
@mock_inputs({
|
||||
'password': 'nopasswd',
|
||||
'username (email.id)': email.pk,
|
||||
'email (email.email)': email.email,
|
||||
'group (group.id)': group.pk,
|
||||
})
|
||||
def test(self):
|
||||
call_command(
|
||||
'createsuperuser',
|
||||
interactive=True,
|
||||
stdout=new_io,
|
||||
stdin=MockTTY(),
|
||||
)
|
||||
|
||||
command_output = new_io.getvalue().strip()
|
||||
self.assertEqual(command_output, 'Superuser created successfully.')
|
||||
u = CustomUserWithFK._default_manager.get(email=email)
|
||||
self.assertEqual(u.username, email)
|
||||
self.assertEqual(u.group, group)
|
||||
|
||||
test(self)
|
||||
|
||||
|
||||
class CustomUserModelValidationTestCase(TestCase):
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserNonListRequiredFields')
|
||||
@override_system_checks([check_user_model])
|
||||
def test_required_fields_is_list(self):
|
||||
"REQUIRED_FIELDS should be a list."
|
||||
|
||||
from .custom_user import CustomUserNonListRequiredFields
|
||||
errors = checks.run_checks()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"'REQUIRED_FIELDS' must be a list or tuple.",
|
||||
hint=None,
|
||||
obj=CustomUserNonListRequiredFields,
|
||||
id='auth.E001',
|
||||
),
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields')
|
||||
@override_system_checks([check_user_model])
|
||||
def test_username_not_in_required_fields(self):
|
||||
"USERNAME_FIELD should not appear in REQUIRED_FIELDS."
|
||||
|
||||
from .custom_user import CustomUserBadRequiredFields
|
||||
errors = checks.run_checks()
|
||||
expected = [
|
||||
checks.Error(
|
||||
("The field named as the 'USERNAME_FIELD' for a custom user model "
|
||||
"must not be included in 'REQUIRED_FIELDS'."),
|
||||
hint=None,
|
||||
obj=CustomUserBadRequiredFields,
|
||||
id='auth.E002',
|
||||
),
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername')
|
||||
@override_system_checks([check_user_model])
|
||||
def test_username_non_unique(self):
|
||||
"A non-unique USERNAME_FIELD should raise a model validation error."
|
||||
|
||||
from .custom_user import CustomUserNonUniqueUsername
|
||||
errors = checks.run_checks()
|
||||
expected = [
|
||||
checks.Error(
|
||||
("'CustomUserNonUniqueUsername.username' must be "
|
||||
"unique because it is named as the 'USERNAME_FIELD'."),
|
||||
hint=None,
|
||||
obj=CustomUserNonUniqueUsername,
|
||||
id='auth.E003',
|
||||
),
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername',
|
||||
AUTHENTICATION_BACKENDS=[
|
||||
'my.custom.backend',
|
||||
])
|
||||
@override_system_checks([check_user_model])
|
||||
def test_username_non_unique_with_custom_backend(self):
|
||||
""" A non-unique USERNAME_FIELD should raise an error only if we use the
|
||||
default authentication backend. Otherwise, an warning should be raised.
|
||||
"""
|
||||
|
||||
from .custom_user import CustomUserNonUniqueUsername
|
||||
errors = checks.run_checks()
|
||||
expected = [
|
||||
checks.Warning(
|
||||
("'CustomUserNonUniqueUsername.username' is named as "
|
||||
"the 'USERNAME_FIELD', but it is not unique."),
|
||||
hint=('Ensure that your authentication backend(s) can handle '
|
||||
'non-unique usernames.'),
|
||||
obj=CustomUserNonUniqueUsername,
|
||||
id='auth.W004',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
|
||||
class PermissionTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._original_permissions = models.Permission._meta.permissions[:]
|
||||
self._original_default_permissions = models.Permission._meta.default_permissions
|
||||
self._original_verbose_name = models.Permission._meta.verbose_name
|
||||
|
||||
def tearDown(self):
|
||||
models.Permission._meta.permissions = self._original_permissions
|
||||
models.Permission._meta.default_permissions = self._original_default_permissions
|
||||
models.Permission._meta.verbose_name = self._original_verbose_name
|
||||
ContentType.objects.clear_cache()
|
||||
|
||||
def test_duplicated_permissions(self):
|
||||
"""
|
||||
Test that we show proper error message if we are trying to create
|
||||
duplicate permissions.
|
||||
"""
|
||||
auth_app_config = apps.get_app_config('auth')
|
||||
|
||||
# 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, auth_app_config, 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, auth_app_config, verbosity=0)
|
||||
|
||||
# should not raise anything
|
||||
models.Permission._meta.permissions = [
|
||||
('my_custom_permission', 'Some permission'),
|
||||
('other_one', 'Some other permission'),
|
||||
]
|
||||
create_permissions(auth_app_config, verbosity=0)
|
||||
|
||||
def test_default_permissions(self):
|
||||
auth_app_config = apps.get_app_config('auth')
|
||||
|
||||
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
|
||||
models.Permission._meta.permissions = [
|
||||
('my_custom_permission', 'Some permission'),
|
||||
]
|
||||
create_permissions(auth_app_config, verbosity=0)
|
||||
|
||||
# add/change/delete permission by default + custom permission
|
||||
self.assertEqual(models.Permission.objects.filter(
|
||||
content_type=permission_content_type,
|
||||
).count(), 4)
|
||||
|
||||
models.Permission.objects.filter(content_type=permission_content_type).delete()
|
||||
models.Permission._meta.default_permissions = []
|
||||
create_permissions(auth_app_config, verbosity=0)
|
||||
|
||||
# custom permission only since default permissions is empty
|
||||
self.assertEqual(models.Permission.objects.filter(
|
||||
content_type=permission_content_type,
|
||||
).count(), 1)
|
||||
|
||||
def test_verbose_name_length(self):
|
||||
auth_app_config = apps.get_app_config('auth')
|
||||
|
||||
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
|
||||
models.Permission.objects.filter(content_type=permission_content_type).delete()
|
||||
models.Permission._meta.verbose_name = "some ridiculously long verbose name that is out of control" * 5
|
||||
|
||||
six.assertRaisesRegex(self, exceptions.ValidationError,
|
||||
"The verbose_name of auth.permission is longer than 244 characters",
|
||||
create_permissions, auth_app_config, verbosity=0)
|
||||
@@ -1,49 +0,0 @@
|
||||
from django.contrib.auth.middleware import AuthenticationMiddleware
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpRequest
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class TestSessionAuthenticationMiddleware(TestCase):
|
||||
def setUp(self):
|
||||
self.user_password = 'test_password'
|
||||
self.user = User.objects.create_user('test_user',
|
||||
'test@example.com',
|
||||
self.user_password)
|
||||
|
||||
self.middleware = AuthenticationMiddleware()
|
||||
self.assertTrue(self.client.login(
|
||||
username=self.user.username,
|
||||
password=self.user_password,
|
||||
))
|
||||
self.request = HttpRequest()
|
||||
self.request.session = self.client.session
|
||||
|
||||
def test_changed_password_doesnt_invalidate_session(self):
|
||||
"""
|
||||
Changing a user's password shouldn't invalidate the session if session
|
||||
verification isn't activated.
|
||||
"""
|
||||
session_key = self.request.session.session_key
|
||||
self.middleware.process_request(self.request)
|
||||
self.assertIsNotNone(self.request.user)
|
||||
self.assertFalse(self.request.user.is_anonymous())
|
||||
|
||||
# After password change, user should remain logged in.
|
||||
self.user.set_password('new_password')
|
||||
self.user.save()
|
||||
self.middleware.process_request(self.request)
|
||||
self.assertIsNotNone(self.request.user)
|
||||
self.assertFalse(self.request.user.is_anonymous())
|
||||
self.assertEqual(session_key, self.request.session.session_key)
|
||||
|
||||
def test_changed_password_invalidates_session_with_middleware(self):
|
||||
with self.modify_settings(MIDDLEWARE_CLASSES={'append': ['django.contrib.auth.middleware.SessionAuthenticationMiddleware']}):
|
||||
# After password change, user should be anonymous
|
||||
self.user.set_password('new_password')
|
||||
self.user.save()
|
||||
self.middleware.process_request(self.request)
|
||||
self.assertIsNotNone(self.request.user)
|
||||
self.assertTrue(self.request.user.is_anonymous())
|
||||
# session should be flushed
|
||||
self.assertIsNone(self.request.session.session_key)
|
||||
@@ -1,219 +0,0 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import (
|
||||
AbstractUser, Group, Permission, User, UserManager,
|
||||
)
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core import mail
|
||||
from django.db.models.signals import post_save
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
@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())
|
||||
|
||||
|
||||
class LoadDataWithNaturalKeysAndMultipleDatabasesTestCase(TestCase):
|
||||
multi_db = True
|
||||
|
||||
def test_load_data_with_user_permissions(self):
|
||||
# Create test contenttypes for both databases
|
||||
default_objects = [
|
||||
ContentType.objects.db_manager('default').create(
|
||||
model='examplemodela',
|
||||
app_label='app_a',
|
||||
),
|
||||
ContentType.objects.db_manager('default').create(
|
||||
model='examplemodelb',
|
||||
app_label='app_b',
|
||||
),
|
||||
]
|
||||
other_objects = [
|
||||
ContentType.objects.db_manager('other').create(
|
||||
model='examplemodelb',
|
||||
app_label='app_b',
|
||||
),
|
||||
ContentType.objects.db_manager('other').create(
|
||||
model='examplemodela',
|
||||
app_label='app_a',
|
||||
),
|
||||
]
|
||||
|
||||
# Now we create the test UserPermission
|
||||
Permission.objects.db_manager("default").create(
|
||||
name="Can delete example model b",
|
||||
codename="delete_examplemodelb",
|
||||
content_type=default_objects[1],
|
||||
)
|
||||
Permission.objects.db_manager("other").create(
|
||||
name="Can delete example model b",
|
||||
codename="delete_examplemodelb",
|
||||
content_type=other_objects[0],
|
||||
)
|
||||
|
||||
perm_default = Permission.objects.get_by_natural_key(
|
||||
'delete_examplemodelb',
|
||||
'app_b',
|
||||
'examplemodelb',
|
||||
)
|
||||
|
||||
perm_other = Permission.objects.db_manager('other').get_by_natural_key(
|
||||
'delete_examplemodelb',
|
||||
'app_b',
|
||||
'examplemodelb',
|
||||
)
|
||||
|
||||
self.assertEqual(perm_default.content_type_id, default_objects[1].id)
|
||||
self.assertEqual(perm_other.content_type_id, other_objects[0].id)
|
||||
|
||||
|
||||
@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.assertFalse(user.has_usable_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 AbstractUserTestCase(TestCase):
|
||||
def test_email_user(self):
|
||||
# valid send_mail parameters
|
||||
kwargs = {
|
||||
"fail_silently": False,
|
||||
"auth_user": None,
|
||||
"auth_password": None,
|
||||
"connection": None,
|
||||
"html_message": None,
|
||||
}
|
||||
abstract_user = AbstractUser(email='foo@bar.com')
|
||||
abstract_user.email_user(subject="Subject here",
|
||||
message="This is a message", from_email="from@domain.com", **kwargs)
|
||||
# Test that one message has been sent.
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
# Verify that test email contains the correct attributes:
|
||||
message = mail.outbox[0]
|
||||
self.assertEqual(message.subject, "Subject here")
|
||||
self.assertEqual(message.body, "This is a message")
|
||||
self.assertEqual(message.from_email, "from@domain.com")
|
||||
self.assertEqual(message.to, [abstract_user.email])
|
||||
|
||||
def test_last_login_default(self):
|
||||
user1 = User.objects.create(username='user1')
|
||||
self.assertIsNone(user1.last_login)
|
||||
|
||||
user2 = User.objects.create_user(username='user2')
|
||||
self.assertIsNone(user2.last_login)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class TestCreateSuperUserSignals(TestCase):
|
||||
"""
|
||||
Simple test case for ticket #20541
|
||||
"""
|
||||
def post_save_listener(self, *args, **kwargs):
|
||||
self.signals_count += 1
|
||||
|
||||
def setUp(self):
|
||||
self.signals_count = 0
|
||||
post_save.connect(self.post_save_listener, sender=User)
|
||||
|
||||
def tearDown(self):
|
||||
post_save.disconnect(self.post_save_listener, sender=User)
|
||||
|
||||
def test_create_user(self):
|
||||
User.objects.create_user("JohnDoe")
|
||||
self.assertEqual(self.signals_count, 1)
|
||||
|
||||
def test_create_superuser(self):
|
||||
User.objects.create_superuser("JohnDoe", "mail@example.com", "1")
|
||||
self.assertEqual(self.signals_count, 1)
|
||||
@@ -1,240 +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.middleware import RemoteUserMiddleware
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
@override_settings(ROOT_URLCONF='django.contrib.auth.tests.urls')
|
||||
class RemoteUserTest(TestCase):
|
||||
|
||||
middleware = 'django.contrib.auth.middleware.RemoteUserMiddleware'
|
||||
backend = 'django.contrib.auth.backends.RemoteUserBackend'
|
||||
header = 'REMOTE_USER'
|
||||
|
||||
# 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/', **{self.header: None})
|
||||
self.assertTrue(response.context['user'].is_anonymous())
|
||||
self.assertEqual(User.objects.count(), num_users)
|
||||
|
||||
response = self.client.get('/remote_user/', **{self.header: ''})
|
||||
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/', **{self.header: '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/', **{self.header: '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/',
|
||||
**{self.header: 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/',
|
||||
**{self.header: 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/',
|
||||
**{self.header: 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/',
|
||||
**{self.header: 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/',
|
||||
**{self.header: 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 test_user_switch_forces_new_login(self):
|
||||
"""
|
||||
Tests that if the username in the header changes between requests
|
||||
that the original user is logged out
|
||||
"""
|
||||
User.objects.create(username='knownuser')
|
||||
# Known user authenticates
|
||||
response = self.client.get('/remote_user/',
|
||||
**{self.header: self.known_user})
|
||||
self.assertEqual(response.context['user'].username, 'knownuser')
|
||||
# During the session, the REMOTE_USER changes to a different user.
|
||||
response = self.client.get('/remote_user/',
|
||||
**{self.header: "newnewuser"})
|
||||
# Ensure that the current user is not the prior remote_user
|
||||
# In backends that create a new user, username is "newnewuser"
|
||||
# In backends that do not create new users, it is '' (anonymous user)
|
||||
self.assertNotEqual(response.context['user'].username, 'knownuser')
|
||||
|
||||
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/', **{self.header: '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')
|
||||
|
||||
|
||||
class CustomHeaderMiddleware(RemoteUserMiddleware):
|
||||
"""
|
||||
Middleware that overrides custom HTTP auth user header.
|
||||
"""
|
||||
header = 'HTTP_AUTHUSER'
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class CustomHeaderRemoteUserTest(RemoteUserTest):
|
||||
"""
|
||||
Tests a custom RemoteUserMiddleware subclass with custom HTTP auth user
|
||||
header.
|
||||
"""
|
||||
middleware = (
|
||||
'django.contrib.auth.tests.test_remote_user.CustomHeaderMiddleware'
|
||||
)
|
||||
header = 'HTTP_AUTHUSER'
|
||||
@@ -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, override_settings
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
@override_settings(USE_TZ=False,
|
||||
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||
ROOT_URLCONF='django.contrib.auth.tests.urls')
|
||||
class SignalTestCase(TestCase):
|
||||
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.assertIn('***', 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)
|
||||
@@ -1,59 +0,0 @@
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||
from django.contrib.auth.views import (
|
||||
password_change, password_change_done, password_reset,
|
||||
password_reset_complete, password_reset_confirm, password_reset_done,
|
||||
)
|
||||
from django.test import RequestFactory, TestCase, override_settings
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
@override_settings(
|
||||
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||
ROOT_URLCONF='django.contrib.auth.tests.urls',
|
||||
)
|
||||
class AuthTemplateTests(TestCase):
|
||||
|
||||
def test_titles(self):
|
||||
rf = RequestFactory()
|
||||
user = User.objects.create_user('jsmith', 'jsmith@example.com', 'pass')
|
||||
user = authenticate(username=user.username, password='pass')
|
||||
request = rf.get('/somepath/')
|
||||
request.user = user
|
||||
|
||||
response = password_reset(request, post_reset_redirect='dummy/')
|
||||
self.assertContains(response, '<title>Password reset</title>')
|
||||
self.assertContains(response, '<h1>Password reset</h1>')
|
||||
|
||||
response = password_reset_done(request)
|
||||
self.assertContains(response, '<title>Password reset sent</title>')
|
||||
self.assertContains(response, '<h1>Password reset sent</h1>')
|
||||
|
||||
# password_reset_confirm invalid token
|
||||
response = password_reset_confirm(request, uidb64='Bad', token='Bad', post_reset_redirect='dummy/')
|
||||
self.assertContains(response, '<title>Password reset unsuccessful</title>')
|
||||
self.assertContains(response, '<h1>Password reset unsuccessful</h1>')
|
||||
|
||||
# password_reset_confirm valid token
|
||||
default_token_generator = PasswordResetTokenGenerator()
|
||||
token = default_token_generator.make_token(user)
|
||||
uidb64 = force_text(urlsafe_base64_encode(force_bytes(user.pk)))
|
||||
response = password_reset_confirm(request, uidb64, token, post_reset_redirect='dummy/')
|
||||
self.assertContains(response, '<title>Enter new password</title>')
|
||||
self.assertContains(response, '<h1>Enter new password</h1>')
|
||||
|
||||
response = password_reset_complete(request)
|
||||
self.assertContains(response, '<title>Password reset complete</title>')
|
||||
self.assertContains(response, '<h1>Password reset complete</h1>')
|
||||
|
||||
response = password_change(request, post_change_redirect='dummy/')
|
||||
self.assertContains(response, '<title>Password change</title>')
|
||||
self.assertContains(response, '<h1>Password change</h1>')
|
||||
|
||||
response = password_change_done(request)
|
||||
self.assertContains(response, '<title>Password change successful</title>')
|
||||
self.assertContains(response, '<h1>Password change successful</h1>')
|
||||
@@ -1,70 +0,0 @@
|
||||
import sys
|
||||
import unittest
|
||||
from datetime import date, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
@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)
|
||||
@@ -1,903 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
import re
|
||||
from importlib import import_module
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.models import LogEntry
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME, SESSION_KEY
|
||||
from django.contrib.auth.forms import (
|
||||
AuthenticationForm, PasswordChangeForm, SetPasswordForm,
|
||||
)
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.views import login as login_view, redirect_to_login
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.contrib.sites.requests import RequestSite
|
||||
from django.core import mail
|
||||
from django.core.urlresolvers import NoReverseMatch, reverse, reverse_lazy
|
||||
from django.http import HttpRequest, QueryDict
|
||||
from django.middleware.csrf import CsrfViewMiddleware
|
||||
from django.test import TestCase, ignore_warnings, override_settings
|
||||
from django.test.utils import patch_logger
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.http import urlquote
|
||||
from django.utils.six.moves.urllib.parse import ParseResult, urlparse
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
|
||||
# Needed so model is installed when tests are run independently:
|
||||
from .custom_user import CustomUser # NOQA
|
||||
from .settings import AUTH_TEMPLATES
|
||||
from .utils import skipIfCustomUser
|
||||
|
||||
|
||||
@override_settings(
|
||||
LANGUAGES=(
|
||||
('en', 'English'),
|
||||
),
|
||||
LANGUAGE_CODE='en',
|
||||
TEMPLATES=AUTH_TEMPLATES,
|
||||
USE_TZ=False,
|
||||
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||
ROOT_URLCONF='django.contrib.auth.tests.urls',
|
||||
)
|
||||
class AuthViewsTestCase(TestCase):
|
||||
"""
|
||||
Helper base class for all the follow test cases.
|
||||
"""
|
||||
fixtures = ['authtestdata.json']
|
||||
|
||||
def login(self, username='testclient', password='password'):
|
||||
response = self.client.post('/login/', {
|
||||
'username': username,
|
||||
'password': password,
|
||||
})
|
||||
self.assertIn(SESSION_KEY, self.client.session)
|
||||
return response
|
||||
|
||||
def logout(self):
|
||||
response = self.client.get('/admin/logout/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotIn(SESSION_KEY, 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)
|
||||
|
||||
def assertURLEqual(self, url, expected, parse_qs=False):
|
||||
"""
|
||||
Given two URLs, make sure all their components (the ones given by
|
||||
urlparse) are equal, only comparing components that are present in both
|
||||
URLs.
|
||||
If `parse_qs` is True, then the querystrings are parsed with QueryDict.
|
||||
This is useful if you don't want the order of parameters to matter.
|
||||
Otherwise, the query strings are compared as-is.
|
||||
"""
|
||||
fields = ParseResult._fields
|
||||
|
||||
for attr, x, y in zip(fields, urlparse(url), urlparse(expected)):
|
||||
if parse_qs and attr == 'query':
|
||||
x, y = QueryDict(x), QueryDict(y)
|
||||
if x and y and x != y:
|
||||
self.fail("%r != %r (%s doesn't match)" % (url, expected, attr))
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
@override_settings(ROOT_URLCONF='django.contrib.auth.urls')
|
||||
class AuthViewNamedURLTests(AuthViewsTestCase):
|
||||
|
||||
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', [], {
|
||||
'uidb64': '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.assertIn("http://", mail.outbox[0].body)
|
||||
self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email)
|
||||
# optional multipart text/html email has been added. Make sure original,
|
||||
# default functionality is 100% the same
|
||||
self.assertFalse(mail.outbox[0].message().is_multipart())
|
||||
|
||||
def test_html_mail_template(self):
|
||||
"""
|
||||
A multipart email with text/plain and text/html is sent
|
||||
if the html_email_template parameter is passed to the view
|
||||
"""
|
||||
response = self.client.post('/password_reset/html_email_template/', {'email': 'staffmember@example.com'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
message = mail.outbox[0].message()
|
||||
self.assertEqual(len(message.get_payload()), 2)
|
||||
self.assertTrue(message.is_multipart())
|
||||
self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain')
|
||||
self.assertEqual(message.get_payload(1).get_content_type(), 'text/html')
|
||||
self.assertNotIn('<html>', message.get_payload(0).get_payload())
|
||||
self.assertIn('<html>', message.get_payload(1).get_payload())
|
||||
|
||||
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)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
@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.assertIn("http://adminsite.com", 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 patch_logger('django.security.DisallowedHost', 'error') as logger_calls:
|
||||
response = self.client.post(
|
||||
'/password_reset/',
|
||||
{'email': 'staffmember@example.com'},
|
||||
HTTP_HOST='www.example:dr.frankenstein@evil.tld'
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
self.assertEqual(len(logger_calls), 1)
|
||||
|
||||
# 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 patch_logger('django.security.DisallowedHost', 'error') as logger_calls:
|
||||
response = self.client.post(
|
||||
'/admin_password_reset/',
|
||||
{'email': 'staffmember@example.com'},
|
||||
HTTP_HOST='www.example:dr.frankenstein@evil.tld'
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
self.assertEqual(len(logger_calls), 1)
|
||||
|
||||
def _test_confirm_start(self):
|
||||
# Start by creating the email
|
||||
self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
|
||||
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.assertIsNotNone(urlmatch, "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-existent 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'})
|
||||
# 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'])
|
||||
|
||||
def test_reset_redirect_default(self):
|
||||
response = self.client.post('/password_reset/',
|
||||
{'email': 'staffmember@example.com'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/password_reset/done/')
|
||||
|
||||
def test_reset_custom_redirect(self):
|
||||
response = self.client.post('/password_reset/custom_redirect/',
|
||||
{'email': 'staffmember@example.com'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/custom/')
|
||||
|
||||
def test_reset_custom_redirect_named(self):
|
||||
response = self.client.post('/password_reset/custom_redirect/named/',
|
||||
{'email': 'staffmember@example.com'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/password_reset/')
|
||||
|
||||
def test_confirm_redirect_default(self):
|
||||
url, path = self._test_confirm_start()
|
||||
response = self.client.post(path, {'new_password1': 'anewpassword',
|
||||
'new_password2': 'anewpassword'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/reset/done/')
|
||||
|
||||
def test_confirm_redirect_custom(self):
|
||||
url, path = self._test_confirm_start()
|
||||
path = path.replace('/reset/', '/reset/custom/')
|
||||
response = self.client.post(path, {'new_password1': 'anewpassword',
|
||||
'new_password2': 'anewpassword'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/custom/')
|
||||
|
||||
def test_confirm_redirect_custom_named(self):
|
||||
url, path = self._test_confirm_start()
|
||||
path = path.replace('/reset/', '/reset/custom/named/')
|
||||
response = self.client.post(path, {'new_password1': 'anewpassword',
|
||||
'new_password2': 'anewpassword'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/password_reset/')
|
||||
|
||||
def test_confirm_display_user_from_form(self):
|
||||
url, path = self._test_confirm_start()
|
||||
response = self.client.get(path)
|
||||
|
||||
# #16919 -- The ``password_reset_confirm`` view should pass the user
|
||||
# object to the ``SetPasswordForm``, even on GET requests.
|
||||
# For this test, we render ``{{ form.user }}`` in the template
|
||||
# ``registration/password_reset_confirm.html`` so that we can test this.
|
||||
username = User.objects.get(email='staffmember@example.com').username
|
||||
self.assertContains(response, "Hello, %s." % username)
|
||||
|
||||
# However, the view should NOT pass any user object on a form if the
|
||||
# password reset link was invalid.
|
||||
response = self.client.get('/reset/zzzzzzzzzzzzz/1-1/')
|
||||
self.assertContains(response, "Hello, .")
|
||||
|
||||
|
||||
@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.assertIsNotNone(urlmatch, "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):
|
||||
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()
|
||||
self.client.post('/password_change/', {
|
||||
'old_password': 'password',
|
||||
'new_password1': 'password1',
|
||||
'new_password2': 'password1',
|
||||
})
|
||||
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.assertURLEqual(response.url, '/password_change/done/')
|
||||
|
||||
@override_settings(LOGIN_URL='/login/')
|
||||
def test_password_change_done_fails(self):
|
||||
response = self.client.get('/password_change/done/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/login/?next=/password_change/done/')
|
||||
|
||||
def test_password_change_redirect_default(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.assertURLEqual(response.url, '/password_change/done/')
|
||||
|
||||
def test_password_change_redirect_custom(self):
|
||||
self.login()
|
||||
response = self.client.post('/password_change/custom/', {
|
||||
'old_password': 'password',
|
||||
'new_password1': 'password1',
|
||||
'new_password2': 'password1',
|
||||
})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/custom/')
|
||||
|
||||
def test_password_change_redirect_custom_named(self):
|
||||
self.login()
|
||||
response = self.client.post('/password_change/custom/named/', {
|
||||
'old_password': 'password',
|
||||
'new_password1': 'password1',
|
||||
'new_password2': 'password1',
|
||||
})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/password_reset/')
|
||||
|
||||
|
||||
@override_settings(MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) + [
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware'
|
||||
])
|
||||
class SessionAuthenticationTests(AuthViewsTestCase):
|
||||
def test_user_password_change_updates_session(self):
|
||||
"""
|
||||
#21649 - Ensure contrib.auth.views.password_change updates the user's
|
||||
session auth hash after a password change so the session isn't logged out.
|
||||
"""
|
||||
self.login()
|
||||
response = self.client.post('/password_change/', {
|
||||
'old_password': 'password',
|
||||
'new_password1': 'password1',
|
||||
'new_password2': 'password1',
|
||||
})
|
||||
# if the hash isn't updated, retrieving the redirection page will fail.
|
||||
self.assertRedirects(response, '/password_change/done/')
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class LoginTest(AuthViewsTestCase):
|
||||
|
||||
def test_current_site_in_context_after_login(self):
|
||||
response = self.client.get(reverse('login'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
if apps.is_installed('django.contrib.sites'):
|
||||
Site = apps.get_model('sites.Site')
|
||||
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.assertIsInstance(response.context['form'], AuthenticationForm)
|
||||
|
||||
def test_security_check(self, password='password'):
|
||||
login_url = reverse('login')
|
||||
|
||||
# Those URLs should not pass the security check
|
||||
for bad_url in ('http://example.com',
|
||||
'http:///example.com',
|
||||
'https://example.com',
|
||||
'ftp://exampel.com',
|
||||
'///example.com',
|
||||
'//example.com',
|
||||
'javascript:alert("XSS")'):
|
||||
|
||||
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.assertNotIn(bad_url, 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/',
|
||||
'HTTPS://testserver/',
|
||||
'//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.assertIn(good_url, response.url, "%s should be allowed" % good_url)
|
||||
|
||||
def test_login_form_contains_request(self):
|
||||
# 15198
|
||||
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.
|
||||
|
||||
def test_login_csrf_rotate(self, password='password'):
|
||||
"""
|
||||
Makes sure that a login rotates the currently-used CSRF token.
|
||||
"""
|
||||
# Do a GET to establish a CSRF token
|
||||
# TestClient isn't used here as we're testing middleware, essentially.
|
||||
req = HttpRequest()
|
||||
CsrfViewMiddleware().process_view(req, login_view, (), {})
|
||||
req.META["CSRF_COOKIE_USED"] = True
|
||||
resp = login_view(req)
|
||||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
|
||||
token1 = csrf_cookie.coded_value
|
||||
|
||||
# Prepare the POST request
|
||||
req = HttpRequest()
|
||||
req.COOKIES[settings.CSRF_COOKIE_NAME] = token1
|
||||
req.method = "POST"
|
||||
req.POST = {'username': 'testclient', 'password': password, 'csrfmiddlewaretoken': token1}
|
||||
|
||||
# Use POST request to log in
|
||||
SessionMiddleware().process_request(req)
|
||||
CsrfViewMiddleware().process_view(req, login_view, (), {})
|
||||
req.META["SERVER_NAME"] = "testserver" # Required to have redirect work in login view
|
||||
req.META["SERVER_PORT"] = 80
|
||||
resp = login_view(req)
|
||||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
|
||||
token2 = csrf_cookie.coded_value
|
||||
|
||||
# Check the CSRF token switched
|
||||
self.assertNotEqual(token1, token2)
|
||||
|
||||
def test_session_key_flushed_on_login(self):
|
||||
"""
|
||||
To avoid reusing another user's session, ensure a new, empty session is
|
||||
created if the existing session corresponds to a different authenticated
|
||||
user.
|
||||
"""
|
||||
self.login()
|
||||
original_session_key = self.client.session.session_key
|
||||
|
||||
self.login(username='staff')
|
||||
self.assertNotEqual(original_session_key, self.client.session.session_key)
|
||||
|
||||
def test_session_key_flushed_on_login_after_password_change(self):
|
||||
"""
|
||||
As above, but same user logging in after a password change.
|
||||
"""
|
||||
self.login()
|
||||
original_session_key = self.client.session.session_key
|
||||
|
||||
# If no password change, session key should not be flushed.
|
||||
self.login()
|
||||
self.assertEqual(original_session_key, self.client.session.session_key)
|
||||
|
||||
user = User.objects.get(username='testclient')
|
||||
user.set_password('foobar')
|
||||
user.save()
|
||||
|
||||
self.login(password='foobar')
|
||||
self.assertNotEqual(original_session_key, self.client.session.session_key)
|
||||
|
||||
def test_login_session_without_hash_session_key(self):
|
||||
"""
|
||||
Session without django.contrib.auth.HASH_SESSION_KEY should login
|
||||
without an exception.
|
||||
"""
|
||||
user = User.objects.get(username='testclient')
|
||||
engine = import_module(settings.SESSION_ENGINE)
|
||||
session = engine.SessionStore()
|
||||
session[SESSION_KEY] = user.id
|
||||
session.save()
|
||||
original_session_key = session.session_key
|
||||
self.client.cookies[settings.SESSION_COOKIE_NAME] = original_session_key
|
||||
|
||||
self.login()
|
||||
self.assertNotEqual(original_session_key, self.client.session.session_key)
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class LoginURLSettings(AuthViewsTestCase):
|
||||
"""Tests for settings.LOGIN_URL."""
|
||||
def assertLoginURLEquals(self, url, parse_qs=False):
|
||||
response = self.client.get('/login_required/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, url, parse_qs=parse_qs)
|
||||
|
||||
@override_settings(LOGIN_URL='/login/')
|
||||
def test_standard_login_url(self):
|
||||
self.assertLoginURLEquals('/login/?next=/login_required/')
|
||||
|
||||
@override_settings(LOGIN_URL='login')
|
||||
def test_named_login_url(self):
|
||||
self.assertLoginURLEquals('/login/?next=/login_required/')
|
||||
|
||||
@override_settings(LOGIN_URL='http://remote.example.com/login')
|
||||
def test_remote_login_url(self):
|
||||
quoted_next = urlquote('http://testserver/login_required/')
|
||||
expected = 'http://remote.example.com/login?next=%s' % quoted_next
|
||||
self.assertLoginURLEquals(expected)
|
||||
|
||||
@override_settings(LOGIN_URL='https:///login/')
|
||||
def test_https_login_url(self):
|
||||
quoted_next = urlquote('http://testserver/login_required/')
|
||||
expected = 'https:///login/?next=%s' % quoted_next
|
||||
self.assertLoginURLEquals(expected)
|
||||
|
||||
@override_settings(LOGIN_URL='/login/?pretty=1')
|
||||
def test_login_url_with_querystring(self):
|
||||
self.assertLoginURLEquals('/login/?pretty=1&next=/login_required/', parse_qs=True)
|
||||
|
||||
@override_settings(LOGIN_URL='http://remote.example.com/login/?next=/default/')
|
||||
def test_remote_login_url_with_next_querystring(self):
|
||||
quoted_next = urlquote('http://testserver/login_required/')
|
||||
expected = 'http://remote.example.com/login/?next=%s' % quoted_next
|
||||
self.assertLoginURLEquals(expected)
|
||||
|
||||
@override_settings(LOGIN_URL=reverse_lazy('login'))
|
||||
def test_lazy_login_url(self):
|
||||
self.assertLoginURLEquals('/login/?next=/login_required/')
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class LoginRedirectUrlTest(AuthViewsTestCase):
|
||||
"""Tests for settings.LOGIN_REDIRECT_URL."""
|
||||
def assertLoginRedirectURLEqual(self, url):
|
||||
response = self.login()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, url)
|
||||
|
||||
def test_default(self):
|
||||
self.assertLoginRedirectURLEqual('/accounts/profile/')
|
||||
|
||||
@override_settings(LOGIN_REDIRECT_URL='/custom/')
|
||||
def test_custom(self):
|
||||
self.assertLoginRedirectURLEqual('/custom/')
|
||||
|
||||
@override_settings(LOGIN_REDIRECT_URL='password_reset')
|
||||
def test_named(self):
|
||||
self.assertLoginRedirectURLEqual('/password_reset/')
|
||||
|
||||
@override_settings(LOGIN_REDIRECT_URL='http://remote.example.com/welcome/')
|
||||
def test_remote(self):
|
||||
self.assertLoginRedirectURLEqual('http://remote.example.com/welcome/')
|
||||
|
||||
|
||||
class RedirectToLoginTests(AuthViewsTestCase):
|
||||
"""Tests for the redirect_to_login view"""
|
||||
@override_settings(LOGIN_URL=reverse_lazy('login'))
|
||||
def test_redirect_to_login_with_lazy(self):
|
||||
login_redirect_response = redirect_to_login(next='/else/where/')
|
||||
expected = '/login/?next=/else/where/'
|
||||
self.assertEqual(expected, login_redirect_response.url)
|
||||
|
||||
@override_settings(LOGIN_URL=reverse_lazy('login'))
|
||||
def test_redirect_to_login_with_lazy_and_unicode(self):
|
||||
login_redirect_response = redirect_to_login(next='/else/where/झ/')
|
||||
expected = '/login/?next=/else/where/%E0%A4%9D/'
|
||||
self.assertEqual(expected, login_redirect_response.url)
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class LogoutTest(AuthViewsTestCase):
|
||||
|
||||
def confirm_logged_out(self):
|
||||
self.assertNotIn(SESSION_KEY, 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.assertIn('site', 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.assertURLEqual(response.url, '/somewhere/')
|
||||
|
||||
response = self.client.get('/logout/next_page/?next=/login/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/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.assertURLEqual(response.url, '/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.assertURLEqual(response.url, '/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.assertURLEqual(response.url, '/somewhere/')
|
||||
self.confirm_logged_out()
|
||||
|
||||
def test_logout_with_named_redirect(self):
|
||||
"Logout resolves names or URLs passed as next_page."
|
||||
self.login()
|
||||
response = self.client.get('/logout/next_page/named/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertURLEqual(response.url, '/password_reset/')
|
||||
self.confirm_logged_out()
|
||||
|
||||
def test_security_check(self, password='password'):
|
||||
logout_url = reverse('logout')
|
||||
|
||||
# Those URLs should not pass the security check
|
||||
for bad_url in ('http://example.com',
|
||||
'http:///example.com',
|
||||
'https://example.com',
|
||||
'ftp://exampel.com',
|
||||
'///example.com',
|
||||
'//example.com',
|
||||
'javascript:alert("XSS")'):
|
||||
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.assertNotIn(bad_url, 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/',
|
||||
'HTTPS://testserver/',
|
||||
'//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.assertIn(good_url, response.url, "%s should be allowed" % good_url)
|
||||
self.confirm_logged_out()
|
||||
|
||||
def test_logout_preserve_language(self):
|
||||
"""Check that language stored in session is preserved after logout"""
|
||||
# Create a new session with language
|
||||
engine = import_module(settings.SESSION_ENGINE)
|
||||
session = engine.SessionStore()
|
||||
session[LANGUAGE_SESSION_KEY] = 'pl'
|
||||
session.save()
|
||||
self.client.cookies[settings.SESSION_COOKIE_NAME] = session.session_key
|
||||
|
||||
self.client.get('/logout/')
|
||||
self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], 'pl')
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
@override_settings(
|
||||
# Redirect in test_user_change_password will fail if session auth hash
|
||||
# isn't updated after password change (#21649)
|
||||
MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) + [
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware'
|
||||
],
|
||||
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||
ROOT_URLCONF='django.contrib.auth.tests.urls_admin',
|
||||
)
|
||||
class ChangelistTests(AuthViewsTestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Make me a superuser before logging in.
|
||||
User.objects.filter(username='testclient').update(is_staff=True, is_superuser=True)
|
||||
self.login()
|
||||
self.admin = User.objects.get(pk=1)
|
||||
|
||||
def get_user_data(self, user):
|
||||
return {
|
||||
'username': user.username,
|
||||
'password': user.password,
|
||||
'email': user.email,
|
||||
'is_active': user.is_active,
|
||||
'is_staff': user.is_staff,
|
||||
'is_superuser': user.is_superuser,
|
||||
'last_login_0': user.last_login.strftime('%Y-%m-%d'),
|
||||
'last_login_1': user.last_login.strftime('%H:%M:%S'),
|
||||
'initial-last_login_0': user.last_login.strftime('%Y-%m-%d'),
|
||||
'initial-last_login_1': user.last_login.strftime('%H:%M:%S'),
|
||||
'date_joined_0': user.date_joined.strftime('%Y-%m-%d'),
|
||||
'date_joined_1': user.date_joined.strftime('%H:%M:%S'),
|
||||
'initial-date_joined_0': user.date_joined.strftime('%Y-%m-%d'),
|
||||
'initial-date_joined_1': user.date_joined.strftime('%H:%M:%S'),
|
||||
'first_name': user.first_name,
|
||||
'last_name': user.last_name,
|
||||
}
|
||||
|
||||
# #20078 - users shouldn't be allowed to guess password hashes via
|
||||
# repeated password__startswith queries.
|
||||
def test_changelist_disallows_password_lookups(self):
|
||||
# A lookup that tries to filter on password isn't OK
|
||||
with patch_logger('django.security.DisallowedModelAdminLookup', 'error') as logger_calls:
|
||||
response = self.client.get('/admin/auth/user/?password__startswith=sha1$')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(len(logger_calls), 1)
|
||||
|
||||
def test_user_change_email(self):
|
||||
data = self.get_user_data(self.admin)
|
||||
data['email'] = 'new_' + data['email']
|
||||
response = self.client.post('/admin/auth/user/%s/' % self.admin.pk, data)
|
||||
self.assertRedirects(response, '/admin/auth/user/')
|
||||
row = LogEntry.objects.latest('id')
|
||||
self.assertEqual(row.change_message, 'Changed email.')
|
||||
|
||||
def test_user_not_change(self):
|
||||
response = self.client.post('/admin/auth/user/%s/' % self.admin.pk,
|
||||
self.get_user_data(self.admin)
|
||||
)
|
||||
self.assertRedirects(response, '/admin/auth/user/')
|
||||
row = LogEntry.objects.latest('id')
|
||||
self.assertEqual(row.change_message, 'No fields changed.')
|
||||
|
||||
def test_user_change_password(self):
|
||||
response = self.client.post('/admin/auth/user/%s/password/' % self.admin.pk, {
|
||||
'password1': 'password1',
|
||||
'password2': 'password1',
|
||||
})
|
||||
self.assertRedirects(response, '/admin/auth/user/%s/' % self.admin.pk)
|
||||
row = LogEntry.objects.latest('id')
|
||||
self.assertEqual(row.change_message, 'Changed password.')
|
||||
self.logout()
|
||||
self.login(password='password1')
|
||||
|
||||
def test_user_change_different_user_password(self):
|
||||
u = User.objects.get(email='staffmember@example.com')
|
||||
response = self.client.post('/admin/auth/user/%s/password/' % u.pk, {
|
||||
'password1': 'password1',
|
||||
'password2': 'password1',
|
||||
})
|
||||
self.assertRedirects(response, '/admin/auth/user/%s/' % u.pk)
|
||||
row = LogEntry.objects.latest('id')
|
||||
self.assertEqual(row.user_id, self.admin.pk)
|
||||
self.assertEqual(row.object_id, str(u.pk))
|
||||
self.assertEqual(row.change_message, 'Changed password.')
|
||||
@@ -1,101 +0,0 @@
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import views
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.contrib.auth.urls import urlpatterns
|
||||
from django.contrib.messages.api import info
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.shortcuts import render
|
||||
from django.template import RequestContext, Template
|
||||
from django.views.decorators.cache import never_cache
|
||||
|
||||
|
||||
class CustomRequestAuthenticationForm(AuthenticationForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
assert isinstance(request, HttpRequest)
|
||||
super(CustomRequestAuthenticationForm, self).__init__(request, *args, **kwargs)
|
||||
|
||||
|
||||
@never_cache
|
||||
def remote_user_auth_view(request):
|
||||
"Dummy view for remote user tests"
|
||||
t = Template("Username is {{ user }}.")
|
||||
c = RequestContext(request, {})
|
||||
return HttpResponse(t.render(c))
|
||||
|
||||
|
||||
def auth_processor_no_attr_access(request):
|
||||
render(request, 'context_processors/auth_attrs_no_access.html')
|
||||
# *After* rendering, we check whether the session was accessed
|
||||
return render(request,
|
||||
'context_processors/auth_attrs_test_access.html',
|
||||
{'session_accessed': request.session.accessed})
|
||||
|
||||
|
||||
def auth_processor_attr_access(request):
|
||||
render(request, 'context_processors/auth_attrs_access.html')
|
||||
return render(request,
|
||||
'context_processors/auth_attrs_test_access.html',
|
||||
{'session_accessed': request.session.accessed})
|
||||
|
||||
|
||||
def auth_processor_user(request):
|
||||
return render(request, 'context_processors/auth_attrs_user.html')
|
||||
|
||||
|
||||
def auth_processor_perms(request):
|
||||
return render(request, 'context_processors/auth_attrs_perms.html')
|
||||
|
||||
|
||||
def auth_processor_perm_in_perms(request):
|
||||
return render(request, 'context_processors/auth_attrs_perm_in_perms.html')
|
||||
|
||||
|
||||
def auth_processor_messages(request):
|
||||
info(request, "Message 1")
|
||||
return render(request, 'context_processors/auth_attrs_messages.html')
|
||||
|
||||
|
||||
def userpage(request):
|
||||
pass
|
||||
|
||||
|
||||
def custom_request_auth_login(request):
|
||||
return views.login(request, authentication_form=CustomRequestAuthenticationForm)
|
||||
|
||||
# special urls for auth test cases
|
||||
urlpatterns += [
|
||||
url(r'^logout/custom_query/$', views.logout, dict(redirect_field_name='follow')),
|
||||
url(r'^logout/next_page/$', views.logout, dict(next_page='/somewhere/')),
|
||||
url(r'^logout/next_page/named/$', views.logout, dict(next_page='password_reset')),
|
||||
url(r'^remote_user/$', remote_user_auth_view),
|
||||
url(r'^password_reset_from_email/$', views.password_reset, dict(from_email='staffmember@example.com')),
|
||||
url(r'^password_reset/custom_redirect/$', views.password_reset, dict(post_reset_redirect='/custom/')),
|
||||
url(r'^password_reset/custom_redirect/named/$', views.password_reset, dict(post_reset_redirect='password_reset')),
|
||||
url(r'^password_reset/html_email_template/$', views.password_reset,
|
||||
dict(html_email_template_name='registration/html_password_reset_email.html')),
|
||||
url(r'^reset/custom/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
|
||||
views.password_reset_confirm,
|
||||
dict(post_reset_redirect='/custom/')),
|
||||
url(r'^reset/custom/named/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
|
||||
views.password_reset_confirm,
|
||||
dict(post_reset_redirect='password_reset')),
|
||||
url(r'^password_change/custom/$', views.password_change, dict(post_change_redirect='/custom/')),
|
||||
url(r'^password_change/custom/named/$', views.password_change, dict(post_change_redirect='password_reset')),
|
||||
url(r'^admin_password_reset/$', views.password_reset, dict(is_admin_site=True)),
|
||||
url(r'^login_required/$', login_required(views.password_reset)),
|
||||
url(r'^login_required_login_url/$', login_required(views.password_reset, login_url='/somewhere/')),
|
||||
|
||||
url(r'^auth_processor_no_attr_access/$', auth_processor_no_attr_access),
|
||||
url(r'^auth_processor_attr_access/$', auth_processor_attr_access),
|
||||
url(r'^auth_processor_user/$', auth_processor_user),
|
||||
url(r'^auth_processor_perms/$', auth_processor_perms),
|
||||
url(r'^auth_processor_perm_in_perms/$', auth_processor_perm_in_perms),
|
||||
url(r'^auth_processor_messages/$', auth_processor_messages),
|
||||
url(r'^custom_request_auth_login/$', custom_request_auth_login),
|
||||
url(r'^userpage/(.+)/$', userpage, name="userpage"),
|
||||
|
||||
# This line is only required to render the password reset with is_admin=True
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
"""
|
||||
Test URLs for auth admins.
|
||||
"""
|
||||
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import GroupAdmin, UserAdmin
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.auth.urls import urlpatterns
|
||||
|
||||
# Create a silo'd admin site for just the user/group admins.
|
||||
site = admin.AdminSite(name='auth_test_admin')
|
||||
site.register(User, UserAdmin)
|
||||
site.register(Group, GroupAdmin)
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^admin/', include(site.urls)),
|
||||
]
|
||||
Reference in New Issue
Block a user