2012-06-07 18:08:47 +02:00
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
2016-02-14 16:30:01 +01:00
|
|
|
import datetime
|
2013-07-31 04:29:34 +02:00
|
|
|
import re
|
2013-05-06 12:45:56 +02:00
|
|
|
|
2013-07-30 14:24:13 +02:00
|
|
|
from django import forms
|
2015-01-28 13:35:27 +01:00
|
|
|
from django.contrib.auth.forms import (
|
2015-07-07 00:12:26 +02:00
|
|
|
AdminPasswordChangeForm, AuthenticationForm, PasswordChangeForm,
|
|
|
|
PasswordResetForm, ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget,
|
|
|
|
SetPasswordForm, UserChangeForm, UserCreationForm,
|
2015-01-28 13:35:27 +01:00
|
|
|
)
|
2012-03-20 21:51:16 +01:00
|
|
|
from django.contrib.auth.models import User
|
2015-02-15 04:03:25 +01:00
|
|
|
from django.contrib.sites.models import Site
|
2011-06-19 13:24:39 +02:00
|
|
|
from django.core import mail
|
2014-05-09 08:48:41 +02:00
|
|
|
from django.core.mail import EmailMultiAlternatives
|
2015-01-28 13:35:27 +01:00
|
|
|
from django.forms.fields import CharField, Field
|
2015-07-07 00:12:26 +02:00
|
|
|
from django.test import SimpleTestCase, TestCase, mock, override_settings
|
2011-12-15 17:12:46 +01:00
|
|
|
from django.utils import translation
|
2015-01-28 13:35:27 +01:00
|
|
|
from django.utils.encoding import force_text
|
2013-05-06 12:45:56 +02:00
|
|
|
from django.utils.text import capfirst
|
2012-03-26 22:22:02 +02:00
|
|
|
from django.utils.translation import ugettext as _
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2016-02-14 16:30:01 +01:00
|
|
|
from .models.custom_user import ExtensionUser
|
2014-12-17 22:10:57 +01:00
|
|
|
from .settings import AUTH_TEMPLATES
|
|
|
|
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-02-23 01:53:57 +01:00
|
|
|
class TestDataMixin(object):
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def setUpTestData(cls):
|
2016-02-05 21:56:52 +01:00
|
|
|
cls.u1 = User.objects.create_user(username='testclient', password='password', email='testclient@example.com')
|
|
|
|
cls.u2 = User.objects.create_user(username='inactive', password='password', is_active=False)
|
|
|
|
cls.u3 = User.objects.create_user(username='staff', password='password')
|
|
|
|
cls.u4 = User.objects.create(username='empty_password', password='')
|
|
|
|
cls.u5 = User.objects.create(username='unmanageable_password', password='$')
|
|
|
|
cls.u6 = User.objects.create(username='unknown_password', password='foo$bar')
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-02-23 01:53:57 +01:00
|
|
|
|
|
|
|
class UserCreationFormTest(TestDataMixin, TestCase):
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_user_already_exists(self):
|
|
|
|
data = {
|
|
|
|
'username': 'testclient',
|
|
|
|
'password1': 'test123',
|
|
|
|
'password2': 'test123',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = UserCreationForm(data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form["username"].errors,
|
2014-04-06 17:30:46 +02:00
|
|
|
[force_text(User._meta.get_field('username').error_messages['unique'])])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_invalid_data(self):
|
|
|
|
data = {
|
|
|
|
'username': 'jsmith!',
|
|
|
|
'password1': 'test123',
|
|
|
|
'password2': 'test123',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = UserCreationForm(data)
|
|
|
|
self.assertFalse(form.is_valid())
|
2014-04-06 17:30:46 +02:00
|
|
|
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)])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_password_verification(self):
|
|
|
|
# The verification password is incorrect.
|
|
|
|
data = {
|
|
|
|
'username': 'jsmith',
|
|
|
|
'password1': 'test123',
|
|
|
|
'password2': 'test',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = UserCreationForm(data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form["password2"].errors,
|
2012-07-21 10:00:10 +02:00
|
|
|
[force_text(form.error_messages['password_mismatch'])])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_both_passwords(self):
|
|
|
|
# One (or both) passwords weren't given
|
|
|
|
data = {'username': 'jsmith'}
|
|
|
|
form = UserCreationForm(data)
|
2012-07-21 10:00:10 +02:00
|
|
|
required_error = [force_text(Field.default_error_messages['required'])]
|
2010-09-10 01:21:16 +02:00
|
|
|
self.assertFalse(form.is_valid())
|
2011-12-15 17:12:46 +01:00
|
|
|
self.assertEqual(form['password1'].errors, required_error)
|
|
|
|
self.assertEqual(form['password2'].errors, required_error)
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
data['password2'] = 'test123'
|
|
|
|
form = UserCreationForm(data)
|
|
|
|
self.assertFalse(form.is_valid())
|
2011-12-15 17:12:46 +01:00
|
|
|
self.assertEqual(form['password1'].errors, required_error)
|
2012-08-04 14:55:13 +02:00
|
|
|
self.assertEqual(form['password2'].errors, [])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-07-07 00:12:26 +02:00
|
|
|
@mock.patch('django.contrib.auth.password_validation.password_changed')
|
|
|
|
def test_success(self, password_changed):
|
2010-09-10 01:21:16 +02:00
|
|
|
# The success case.
|
|
|
|
data = {
|
|
|
|
'username': 'jsmith@example.com',
|
|
|
|
'password1': 'test123',
|
|
|
|
'password2': 'test123',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = UserCreationForm(data)
|
|
|
|
self.assertTrue(form.is_valid())
|
2015-07-07 00:12:26 +02:00
|
|
|
form.save(commit=False)
|
|
|
|
self.assertEqual(password_changed.call_count, 0)
|
2010-09-10 01:21:16 +02:00
|
|
|
u = form.save()
|
2015-07-07 00:12:26 +02:00
|
|
|
self.assertEqual(password_changed.call_count, 1)
|
2010-09-10 01:21:16 +02:00
|
|
|
self.assertEqual(repr(u), '<User: jsmith@example.com>')
|
|
|
|
|
2015-07-06 20:14:59 +02:00
|
|
|
@override_settings(AUTH_PASSWORD_VALIDATORS=[
|
|
|
|
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
|
|
|
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {
|
|
|
|
'min_length': 12,
|
|
|
|
}},
|
|
|
|
])
|
|
|
|
def test_validates_password(self):
|
|
|
|
data = {
|
|
|
|
'username': 'testclient',
|
|
|
|
'password1': 'testclient',
|
|
|
|
'password2': 'testclient',
|
|
|
|
}
|
|
|
|
form = UserCreationForm(data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(len(form['password2'].errors), 2)
|
|
|
|
self.assertIn('The password is too similar to the username.', form['password2'].errors)
|
|
|
|
self.assertIn(
|
|
|
|
'This password is too short. It must contain at least 12 characters.',
|
|
|
|
form['password2'].errors
|
|
|
|
)
|
|
|
|
|
2016-02-14 16:30:01 +01:00
|
|
|
def test_custom_form(self):
|
|
|
|
class CustomUserCreationForm(UserCreationForm):
|
|
|
|
class Meta(UserCreationForm.Meta):
|
|
|
|
model = ExtensionUser
|
|
|
|
fields = UserCreationForm.Meta.fields + ('date_of_birth',)
|
|
|
|
|
|
|
|
data = {
|
|
|
|
'username': 'testclient',
|
|
|
|
'password1': 'testclient',
|
|
|
|
'password2': 'testclient',
|
|
|
|
'date_of_birth': '1988-02-24',
|
|
|
|
}
|
|
|
|
form = CustomUserCreationForm(data)
|
|
|
|
self.assertTrue(form.is_valid())
|
|
|
|
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-02-23 01:53:57 +01:00
|
|
|
class AuthenticationFormTest(TestDataMixin, TestCase):
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_invalid_username(self):
|
|
|
|
# The user submits an invalid username.
|
|
|
|
|
|
|
|
data = {
|
|
|
|
'username': 'jsmith_does_not_exist',
|
|
|
|
'password': 'test123',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = AuthenticationForm(None, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form.non_field_errors(),
|
2012-12-16 00:18:45 +01:00
|
|
|
[force_text(form.error_messages['invalid_login'] % {
|
2013-10-18 00:27:45 +02:00
|
|
|
'username': User._meta.get_field('username').verbose_name
|
2012-12-16 00:18:45 +01:00
|
|
|
})])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_inactive_user(self):
|
|
|
|
# The user is inactive.
|
|
|
|
data = {
|
|
|
|
'username': 'inactive',
|
|
|
|
'password': 'password',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = AuthenticationForm(None, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form.non_field_errors(),
|
2012-07-21 10:00:10 +02:00
|
|
|
[force_text(form.error_messages['inactive'])])
|
2011-12-15 17:12:46 +01:00
|
|
|
|
|
|
|
def test_inactive_user_i18n(self):
|
2013-08-16 20:12:10 +02:00
|
|
|
with self.settings(USE_I18N=True), translation.override('pt-br', deactivate=True):
|
|
|
|
# The user is inactive.
|
|
|
|
data = {
|
|
|
|
'username': 'inactive',
|
|
|
|
'password': 'password',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2013-08-16 20:12:10 +02:00
|
|
|
form = AuthenticationForm(None, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form.non_field_errors(),
|
|
|
|
[force_text(form.error_messages['inactive'])])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2013-07-30 14:24:13 +02:00
|
|
|
def test_custom_login_allowed_policy(self):
|
2013-11-30 14:37:15 +01:00
|
|
|
# The user is inactive, but our custom form policy allows them to log in.
|
2013-07-30 14:24:13 +02:00
|
|
|
data = {
|
|
|
|
'username': 'inactive',
|
|
|
|
'password': 'password',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2013-07-30 14:24:13 +02:00
|
|
|
|
|
|
|
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":
|
2014-05-20 12:19:40 +02:00
|
|
|
raise forms.ValidationError("This user is disallowed.")
|
|
|
|
raise forms.ValidationError("Sorry, nobody's allowed in.")
|
2013-07-30 14:24:13 +02:00
|
|
|
|
|
|
|
form = PickyAuthenticationForm(None, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form.non_field_errors(), ['This user is disallowed.'])
|
|
|
|
|
|
|
|
data = {
|
|
|
|
'username': 'testclient',
|
|
|
|
'password': 'password',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2013-07-30 14:24:13 +02:00
|
|
|
form = PickyAuthenticationForm(None, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form.non_field_errors(), ["Sorry, nobody's allowed in."])
|
|
|
|
|
2010-09-10 01:21:16 +02:00
|
|
|
def test_success(self):
|
|
|
|
# The success case
|
|
|
|
data = {
|
|
|
|
'username': 'testclient',
|
|
|
|
'password': 'password',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = AuthenticationForm(None, data)
|
|
|
|
self.assertTrue(form.is_valid())
|
|
|
|
self.assertEqual(form.non_field_errors(), [])
|
|
|
|
|
2013-01-10 09:05:01 +01:00
|
|
|
def test_username_field_label(self):
|
|
|
|
|
|
|
|
class CustomAuthenticationForm(AuthenticationForm):
|
|
|
|
username = CharField(label="Name", max_length=75)
|
|
|
|
|
|
|
|
form = CustomAuthenticationForm()
|
|
|
|
self.assertEqual(form['username'].label, "Name")
|
|
|
|
|
2013-05-06 12:45:56 +02:00
|
|
|
def test_username_field_label_not_set(self):
|
|
|
|
|
|
|
|
class CustomAuthenticationForm(AuthenticationForm):
|
|
|
|
username = CharField()
|
|
|
|
|
|
|
|
form = CustomAuthenticationForm()
|
2014-04-06 17:30:46 +02:00
|
|
|
username_field = User._meta.get_field(User.USERNAME_FIELD)
|
2013-05-06 12:45:56 +02:00
|
|
|
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, "")
|
|
|
|
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-02-23 01:53:57 +01:00
|
|
|
class SetPasswordFormTest(TestDataMixin, TestCase):
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
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',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = SetPasswordForm(user, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form["new_password2"].errors,
|
2012-07-21 10:00:10 +02:00
|
|
|
[force_text(form.error_messages['password_mismatch'])])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-07-07 00:12:26 +02:00
|
|
|
@mock.patch('django.contrib.auth.password_validation.password_changed')
|
|
|
|
def test_success(self, password_changed):
|
2010-09-10 01:21:16 +02:00
|
|
|
user = User.objects.get(username='testclient')
|
|
|
|
data = {
|
|
|
|
'new_password1': 'abc123',
|
|
|
|
'new_password2': 'abc123',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = SetPasswordForm(user, data)
|
|
|
|
self.assertTrue(form.is_valid())
|
2015-07-07 00:12:26 +02:00
|
|
|
form.save(commit=False)
|
|
|
|
self.assertEqual(password_changed.call_count, 0)
|
|
|
|
form.save()
|
|
|
|
self.assertEqual(password_changed.call_count, 1)
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-03-08 15:07:57 +01:00
|
|
|
@override_settings(AUTH_PASSWORD_VALIDATORS=[
|
|
|
|
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
|
|
|
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {
|
|
|
|
'min_length': 12,
|
|
|
|
}},
|
|
|
|
])
|
|
|
|
def test_validates_password(self):
|
|
|
|
user = User.objects.get(username='testclient')
|
|
|
|
data = {
|
|
|
|
'new_password1': 'testclient',
|
|
|
|
'new_password2': 'testclient',
|
|
|
|
}
|
|
|
|
form = SetPasswordForm(user, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(len(form["new_password2"].errors), 2)
|
2015-06-08 19:27:47 +02:00
|
|
|
self.assertIn('The password is too similar to the username.', form["new_password2"].errors)
|
|
|
|
self.assertIn(
|
|
|
|
'This password is too short. It must contain at least 12 characters.',
|
|
|
|
form["new_password2"].errors
|
|
|
|
)
|
2015-03-08 15:07:57 +01:00
|
|
|
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-02-23 01:53:57 +01:00
|
|
|
class PasswordChangeFormTest(TestDataMixin, TestCase):
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_incorrect_password(self):
|
|
|
|
user = User.objects.get(username='testclient')
|
|
|
|
data = {
|
|
|
|
'old_password': 'test',
|
|
|
|
'new_password1': 'abc123',
|
|
|
|
'new_password2': 'abc123',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = PasswordChangeForm(user, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form["old_password"].errors,
|
2012-07-21 10:00:10 +02:00
|
|
|
[force_text(form.error_messages['password_incorrect'])])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
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',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = PasswordChangeForm(user, data)
|
|
|
|
self.assertFalse(form.is_valid())
|
|
|
|
self.assertEqual(form["new_password2"].errors,
|
2012-07-21 10:00:10 +02:00
|
|
|
[force_text(form.error_messages['password_mismatch'])])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-07-07 00:12:26 +02:00
|
|
|
@mock.patch('django.contrib.auth.password_validation.password_changed')
|
|
|
|
def test_success(self, password_changed):
|
2010-09-10 01:21:16 +02:00
|
|
|
# The success case.
|
|
|
|
user = User.objects.get(username='testclient')
|
|
|
|
data = {
|
|
|
|
'old_password': 'password',
|
|
|
|
'new_password1': 'abc123',
|
|
|
|
'new_password2': 'abc123',
|
2013-10-18 11:02:43 +02:00
|
|
|
}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = PasswordChangeForm(user, data)
|
|
|
|
self.assertTrue(form.is_valid())
|
2015-07-07 00:12:26 +02:00
|
|
|
form.save(commit=False)
|
|
|
|
self.assertEqual(password_changed.call_count, 0)
|
|
|
|
form.save()
|
|
|
|
self.assertEqual(password_changed.call_count, 1)
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_field_order(self):
|
|
|
|
# Regression test - check the order of fields:
|
|
|
|
user = User.objects.get(username='testclient')
|
2012-08-08 16:33:15 +02:00
|
|
|
self.assertEqual(list(PasswordChangeForm(user, {}).fields),
|
2010-09-10 01:21:16 +02:00
|
|
|
['old_password', 'new_password1', 'new_password2'])
|
|
|
|
|
2011-12-15 17:12:46 +01:00
|
|
|
|
2015-02-23 01:53:57 +01:00
|
|
|
class UserChangeFormTest(TestDataMixin, TestCase):
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
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())
|
2014-04-06 17:30:46 +02:00
|
|
|
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)])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2010-09-10 01:31:54 +02:00
|
|
|
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
|
2013-08-04 18:17:10 +02:00
|
|
|
MyUserForm({})
|
2010-09-10 01:31:54 +02:00
|
|
|
|
2015-12-03 00:55:50 +01:00
|
|
|
def test_unusable_password(self):
|
2012-09-12 11:21:58 +02:00
|
|
|
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())
|
|
|
|
|
2012-03-22 09:10:19 +01:00
|
|
|
def test_bug_17944_empty_password(self):
|
|
|
|
user = User.objects.get(username='empty_password')
|
|
|
|
form = UserChangeForm(instance=user)
|
2012-09-12 11:56:58 +02:00
|
|
|
self.assertIn(_("No password set."), form.as_table())
|
2012-03-22 09:10:19 +01:00
|
|
|
|
|
|
|
def test_bug_17944_unmanageable_password(self):
|
|
|
|
user = User.objects.get(username='unmanageable_password')
|
|
|
|
form = UserChangeForm(instance=user)
|
2012-09-12 11:21:58 +02:00
|
|
|
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
|
|
|
|
form.as_table())
|
2012-03-22 09:10:19 +01:00
|
|
|
|
|
|
|
def test_bug_17944_unknown_password_algorithm(self):
|
|
|
|
user = User.objects.get(username='unknown_password')
|
|
|
|
form = UserChangeForm(instance=user)
|
2012-09-12 11:21:58 +02:00
|
|
|
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
|
|
|
|
form.as_table())
|
2012-03-22 09:10:19 +01:00
|
|
|
|
2012-10-20 05:41:54 +02:00
|
|
|
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())
|
2016-02-05 21:56:52 +01:00
|
|
|
# original hashed password contains $
|
|
|
|
self.assertIn('$', form.cleaned_data['password'])
|
2012-10-20 05:41:54 +02:00
|
|
|
|
2012-12-01 12:17:00 +01:00
|
|
|
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())
|
|
|
|
|
2016-02-14 16:30:01 +01:00
|
|
|
def test_custom_form(self):
|
|
|
|
class CustomUserChangeForm(UserChangeForm):
|
|
|
|
class Meta(UserChangeForm.Meta):
|
|
|
|
model = ExtensionUser
|
|
|
|
fields = ('username', 'password', 'date_of_birth',)
|
|
|
|
|
|
|
|
user = User.objects.get(username='testclient')
|
|
|
|
data = {
|
|
|
|
'username': 'testclient',
|
|
|
|
'password': 'testclient',
|
|
|
|
'date_of_birth': '1998-02-24',
|
|
|
|
}
|
|
|
|
form = CustomUserChangeForm(data, instance=user)
|
|
|
|
self.assertTrue(form.is_valid())
|
|
|
|
form.save()
|
|
|
|
self.assertEqual(form.cleaned_data['username'], 'testclient')
|
|
|
|
self.assertEqual(form.cleaned_data['date_of_birth'], datetime.date(1998, 2, 24))
|
|
|
|
|
2012-03-22 09:10:19 +01:00
|
|
|
|
2016-02-05 21:56:52 +01:00
|
|
|
@override_settings(TEMPLATES=AUTH_TEMPLATES)
|
2015-02-23 01:53:57 +01:00
|
|
|
class PasswordResetFormTest(TestDataMixin, TestCase):
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2015-02-15 04:03:25 +01:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
|
|
|
super(PasswordResetFormTest, cls).setUpClass()
|
|
|
|
# This cleanup is necessary because contrib.sites cache
|
|
|
|
# makes tests interfere with each other, see #11505
|
|
|
|
Site.objects.clear_cache()
|
|
|
|
|
2011-03-14 22:14:10 +01:00
|
|
|
def create_dummy_user(self):
|
2013-10-19 10:29:17 +02:00
|
|
|
"""
|
|
|
|
Create a user and return a tuple (user_object, username, email).
|
2011-03-14 22:14:10 +01:00
|
|
|
"""
|
|
|
|
username = 'jsmith'
|
|
|
|
email = 'jsmith@example.com'
|
|
|
|
user = User.objects.create_user(username, email, 'test123')
|
|
|
|
return (user, username, email)
|
|
|
|
|
2010-09-10 01:21:16 +02:00
|
|
|
def test_invalid_email(self):
|
2011-12-15 17:12:46 +01:00
|
|
|
data = {'email': 'not valid'}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = PasswordResetForm(data)
|
|
|
|
self.assertFalse(form.is_valid())
|
2013-03-14 16:19:59 +01:00
|
|
|
self.assertEqual(form['email'].errors, [_('Enter a valid email address.')])
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2014-11-03 23:48:03 +01:00
|
|
|
def test_nonexistent_email(self):
|
2013-10-19 10:29:17 +02:00
|
|
|
"""
|
2014-04-26 19:18:45 +02:00
|
|
|
Test nonexistent email address. This should not fail because it would
|
2013-10-19 10:29:17 +02:00
|
|
|
expose information about registered users.
|
|
|
|
"""
|
2011-12-15 17:12:46 +01:00
|
|
|
data = {'email': 'foo@bar.com'}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = PasswordResetForm(data)
|
2013-02-23 13:39:21 +01:00
|
|
|
self.assertTrue(form.is_valid())
|
2013-02-24 11:15:17 +01:00
|
|
|
self.assertEqual(len(mail.outbox), 0)
|
2010-09-10 01:21:16 +02:00
|
|
|
|
|
|
|
def test_cleaned_data(self):
|
2011-03-14 22:14:10 +01:00
|
|
|
(user, username, email) = self.create_dummy_user()
|
|
|
|
data = {'email': email}
|
2010-09-10 01:21:16 +02:00
|
|
|
form = PasswordResetForm(data)
|
|
|
|
self.assertTrue(form.is_valid())
|
2013-02-23 13:39:21 +01:00
|
|
|
form.save(domain_override='example.com')
|
2011-03-14 22:14:10 +01:00
|
|
|
self.assertEqual(form.cleaned_data['email'], email)
|
2013-02-23 13:39:21 +01:00
|
|
|
self.assertEqual(len(mail.outbox), 1)
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2011-06-19 13:24:39 +02:00
|
|
|
def test_custom_email_subject(self):
|
2013-02-18 09:20:26 +01:00
|
|
|
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')
|
2010-09-10 01:21:16 +02:00
|
|
|
|
2014-05-09 08:48:41 +02:00
|
|
|
def test_custom_email_constructor(self):
|
2014-12-17 22:10:57 +01:00
|
|
|
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")
|
2014-05-09 08:48:41 +02:00
|
|
|
|
2013-10-19 10:29:17 +02:00
|
|
|
def test_preserve_username_case(self):
|
|
|
|
"""
|
|
|
|
Preserve the case of the user name (before the @ in the email address)
|
|
|
|
when creating a user (#5605).
|
|
|
|
"""
|
2010-09-10 01:21:16 +02:00
|
|
|
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')
|
2011-03-14 22:14:10 +01:00
|
|
|
|
|
|
|
def test_inactive_user(self):
|
2013-10-19 10:29:17 +02:00
|
|
|
"""
|
|
|
|
Test that inactive user cannot receive password reset email.
|
|
|
|
"""
|
2011-03-14 22:14:10 +01:00
|
|
|
(user, username, email) = self.create_dummy_user()
|
|
|
|
user.is_active = False
|
|
|
|
user.save()
|
|
|
|
form = PasswordResetForm({'email': email})
|
2013-02-23 13:39:21 +01:00
|
|
|
self.assertTrue(form.is_valid())
|
2013-10-19 10:40:20 +02:00
|
|
|
form.save()
|
2013-02-23 13:39:21 +01:00
|
|
|
self.assertEqual(len(mail.outbox), 0)
|
2011-06-26 18:51:34 +02:00
|
|
|
|
|
|
|
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)
|
2013-02-23 13:39:21 +01:00
|
|
|
# The form itself is valid, but no email is sent
|
|
|
|
self.assertTrue(form.is_valid())
|
|
|
|
form.save()
|
2013-02-24 11:15:17 +01:00
|
|
|
self.assertEqual(len(mail.outbox), 0)
|
2012-12-01 12:17:00 +01:00
|
|
|
|
2013-07-31 04:29:34 +02:00
|
|
|
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()))
|
2014-09-04 14:15:09 +02:00
|
|
|
self.assertTrue(
|
|
|
|
re.match(r'^<html><a href="http://example.com/reset/[\w/-]+/">Link</a></html>$',
|
|
|
|
message.get_payload(1).get_payload())
|
|
|
|
)
|
2013-07-31 04:29:34 +02:00
|
|
|
|
2012-12-01 12:17:00 +01:00
|
|
|
|
2015-04-17 23:38:20 +02:00
|
|
|
class ReadOnlyPasswordHashTest(SimpleTestCase):
|
2012-12-01 12:17:00 +01:00
|
|
|
|
|
|
|
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)
|
2013-01-25 21:27:49 +01:00
|
|
|
|
|
|
|
def test_readonly_field_has_changed(self):
|
|
|
|
field = ReadOnlyPasswordHashField()
|
2014-08-07 04:56:23 +02:00
|
|
|
self.assertFalse(field.has_changed('aaa', 'bbb'))
|
2015-07-07 00:12:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
class AdminPasswordChangeFormTest(TestDataMixin, TestCase):
|
|
|
|
|
|
|
|
@mock.patch('django.contrib.auth.password_validation.password_changed')
|
|
|
|
def test_success(self, password_changed):
|
|
|
|
user = User.objects.get(username='testclient')
|
|
|
|
data = {
|
|
|
|
'password1': 'test123',
|
|
|
|
'password2': 'test123',
|
|
|
|
}
|
|
|
|
form = AdminPasswordChangeForm(user, data)
|
|
|
|
self.assertTrue(form.is_valid())
|
|
|
|
form.save(commit=False)
|
|
|
|
self.assertEqual(password_changed.call_count, 0)
|
|
|
|
form.save()
|
|
|
|
self.assertEqual(password_changed.call_count, 1)
|