123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987 |
- import datetime
- import re
- from unittest import mock
- from django import forms
- from django.contrib.auth.forms import (
- AdminPasswordChangeForm, AuthenticationForm, PasswordChangeForm,
- PasswordResetForm, ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget,
- SetPasswordForm, UserChangeForm, UserCreationForm,
- )
- from django.contrib.auth.models import User
- from django.contrib.auth.signals import user_login_failed
- from django.contrib.sites.models import Site
- from django.core import mail
- from django.core.mail import EmailMultiAlternatives
- from django.forms.fields import CharField, Field, IntegerField
- from django.test import SimpleTestCase, TestCase, override_settings
- from django.utils import translation
- from django.utils.text import capfirst
- from django.utils.translation import gettext as _
- from .models.custom_user import (
- CustomUser, CustomUserWithoutIsActiveField, ExtensionUser,
- )
- from .models.with_custom_email_field import CustomEmailField
- from .models.with_integer_username import IntegerUsernameUser
- from .settings import AUTH_TEMPLATES
- class TestDataMixin:
- @classmethod
- def setUpTestData(cls):
- 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')
- class UserCreationFormTest(TestDataMixin, TestCase):
- 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,
- [str(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, [str(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,
- [str(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 = [str(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, [])
- @mock.patch('django.contrib.auth.password_validation.password_changed')
- def test_success(self, password_changed):
- # The success case.
- data = {
- 'username': 'jsmith@example.com',
- 'password1': 'test123',
- 'password2': 'test123',
- }
- form = UserCreationForm(data)
- self.assertTrue(form.is_valid())
- form.save(commit=False)
- self.assertEqual(password_changed.call_count, 0)
- u = form.save()
- self.assertEqual(password_changed.call_count, 1)
- self.assertEqual(repr(u), '<User: jsmith@example.com>')
- def test_unicode_username(self):
- data = {
- 'username': '宝',
- 'password1': 'test123',
- 'password2': 'test123',
- }
- form = UserCreationForm(data)
- self.assertTrue(form.is_valid())
- u = form.save()
- self.assertEqual(u.username, '宝')
- def test_normalize_username(self):
- # The normalization happens in AbstractBaseUser.clean() and ModelForm
- # validation calls Model.clean().
- ohm_username = 'testΩ' # U+2126 OHM SIGN
- data = {
- 'username': ohm_username,
- 'password1': 'pwd2',
- 'password2': 'pwd2',
- }
- form = UserCreationForm(data)
- self.assertTrue(form.is_valid())
- user = form.save()
- self.assertNotEqual(user.username, ohm_username)
- self.assertEqual(user.username, 'testΩ') # U+03A9 GREEK CAPITAL LETTER OMEGA
- def test_duplicate_normalized_unicode(self):
- """
- To prevent almost identical usernames, visually identical but differing
- by their unicode code points only, Unicode NFKC normalization should
- make appear them equal to Django.
- """
- omega_username = 'iamtheΩ' # U+03A9 GREEK CAPITAL LETTER OMEGA
- ohm_username = 'iamtheΩ' # U+2126 OHM SIGN
- self.assertNotEqual(omega_username, ohm_username)
- User.objects.create_user(username=omega_username, password='pwd')
- data = {
- 'username': ohm_username,
- 'password1': 'pwd2',
- 'password2': 'pwd2',
- }
- form = UserCreationForm(data)
- self.assertFalse(form.is_valid())
- self.assertEqual(
- form.errors['username'], ["A user with that username already exists."]
- )
- @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
- )
- 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())
- def test_custom_form_with_different_username_field(self):
- class CustomUserCreationForm(UserCreationForm):
- class Meta(UserCreationForm.Meta):
- model = CustomUser
- fields = ('email', 'date_of_birth')
- data = {
- 'email': 'test@client222.com',
- 'password1': 'testclient',
- 'password2': 'testclient',
- 'date_of_birth': '1988-02-24',
- }
- form = CustomUserCreationForm(data)
- self.assertTrue(form.is_valid())
- def test_custom_form_hidden_username_field(self):
- class CustomUserCreationForm(UserCreationForm):
- class Meta(UserCreationForm.Meta):
- model = CustomUserWithoutIsActiveField
- fields = ('email',) # without USERNAME_FIELD
- data = {
- 'email': 'testclient@example.com',
- 'password1': 'testclient',
- 'password2': 'testclient',
- }
- form = CustomUserCreationForm(data)
- self.assertTrue(form.is_valid())
- def test_password_whitespace_not_stripped(self):
- data = {
- 'username': 'testuser',
- 'password1': ' testpassword ',
- 'password2': ' testpassword ',
- }
- form = UserCreationForm(data)
- self.assertTrue(form.is_valid())
- self.assertEqual(form.cleaned_data['password1'], data['password1'])
- self.assertEqual(form.cleaned_data['password2'], data['password2'])
- @override_settings(AUTH_PASSWORD_VALIDATORS=[
- {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
- ])
- def test_password_help_text(self):
- form = UserCreationForm()
- self.assertEqual(
- form.fields['password1'].help_text,
- '<ul><li>Your password can't be too similar to your other personal information.</li></ul>'
- )
- @override_settings(AUTH_PASSWORD_VALIDATORS=[
- {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
- ])
- def test_user_create_form_validates_password_with_all_data(self):
- """UserCreationForm password validation uses all of the form's data."""
- class CustomUserCreationForm(UserCreationForm):
- class Meta(UserCreationForm.Meta):
- model = User
- fields = ('username', 'email', 'first_name', 'last_name')
- form = CustomUserCreationForm({
- 'username': 'testuser',
- 'password1': 'testpassword',
- 'password2': 'testpassword',
- 'first_name': 'testpassword',
- 'last_name': 'lastname',
- })
- self.assertFalse(form.is_valid())
- self.assertEqual(
- form.errors['password2'],
- ['The password is too similar to the first name.'],
- )
- # To verify that the login form rejects inactive users, use an authentication
- # backend that allows them.
- @override_settings(AUTHENTICATION_BACKENDS=['django.contrib.auth.backends.AllowAllUsersModelBackend'])
- class AuthenticationFormTest(TestDataMixin, TestCase):
- 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(), [
- 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(), [str(form.error_messages['inactive'])])
- # Use an authentication backend that rejects inactive users.
- @override_settings(AUTHENTICATION_BACKENDS=['django.contrib.auth.backends.ModelBackend'])
- def test_inactive_user_incorrect_password(self):
- """An invalid login doesn't leak the inactive status of a user."""
- data = {
- 'username': 'inactive',
- 'password': 'incorrect',
- }
- form = AuthenticationForm(None, data)
- self.assertFalse(form.is_valid())
- self.assertEqual(
- form.non_field_errors(), [
- form.error_messages['invalid_login'] % {
- 'username': User._meta.get_field('username').verbose_name
- }
- ]
- )
- def test_login_failed(self):
- signal_calls = []
- def signal_handler(**kwargs):
- signal_calls.append(kwargs)
- user_login_failed.connect(signal_handler)
- fake_request = object()
- try:
- form = AuthenticationForm(fake_request, {
- 'username': 'testclient',
- 'password': 'incorrect',
- })
- self.assertFalse(form.is_valid())
- self.assertIs(signal_calls[0]['request'], fake_request)
- finally:
- user_login_failed.disconnect(signal_handler)
- 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(), [str(form.error_messages['inactive'])])
- # Use an authentication backend that allows inactive users.
- @override_settings(AUTHENTICATION_BACKENDS=['django.contrib.auth.backends.AllowAllUsersModelBackend'])
- 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_unicode_username(self):
- User.objects.create_user(username='Σαρα', password='pwd')
- data = {
- 'username': 'Σαρα',
- 'password': 'pwd',
- }
- form = AuthenticationForm(None, data)
- self.assertTrue(form.is_valid())
- self.assertEqual(form.non_field_errors(), [])
- @override_settings(AUTH_USER_MODEL='auth_tests.CustomEmailField')
- def test_username_field_max_length_matches_user_model(self):
- self.assertEqual(CustomEmailField._meta.get_field('username').max_length, 255)
- data = {
- 'username': 'u' * 255,
- 'password': 'pwd',
- 'email': 'test@example.com',
- }
- CustomEmailField.objects.create_user(**data)
- form = AuthenticationForm(None, data)
- self.assertEqual(form.fields['username'].max_length, 255)
- self.assertEqual(form.errors, {})
- @override_settings(AUTH_USER_MODEL='auth_tests.IntegerUsernameUser')
- def test_username_field_max_length_defaults_to_254(self):
- self.assertIsNone(IntegerUsernameUser._meta.get_field('username').max_length)
- data = {
- 'username': '0123456',
- 'password': 'password',
- }
- IntegerUsernameUser.objects.create_user(**data)
- form = AuthenticationForm(None, data)
- self.assertEqual(form.fields['username'].max_length, 254)
- self.assertEqual(form.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, "")
- def test_password_whitespace_not_stripped(self):
- data = {
- 'username': 'testuser',
- 'password': ' pass ',
- }
- form = AuthenticationForm(None, data)
- form.is_valid() # Not necessary to have valid credentails for the test.
- self.assertEqual(form.cleaned_data['password'], data['password'])
- @override_settings(AUTH_USER_MODEL='auth_tests.IntegerUsernameUser')
- def test_integer_username(self):
- class CustomAuthenticationForm(AuthenticationForm):
- username = IntegerField()
- user = IntegerUsernameUser.objects.create_user(username=0, password='pwd')
- data = {
- 'username': 0,
- 'password': 'pwd',
- }
- form = CustomAuthenticationForm(None, data)
- self.assertTrue(form.is_valid())
- self.assertEqual(form.cleaned_data['username'], data['username'])
- self.assertEqual(form.cleaned_data['password'], data['password'])
- self.assertEqual(form.errors, {})
- self.assertEqual(form.user_cache, user)
- def test_get_invalid_login_error(self):
- error = AuthenticationForm().get_invalid_login_error()
- self.assertIsInstance(error, forms.ValidationError)
- self.assertEqual(
- error.message,
- 'Please enter a correct %(username)s and password. Note that both '
- 'fields may be case-sensitive.',
- )
- self.assertEqual(error.code, 'invalid_login')
- self.assertEqual(error.params, {'username': 'username'})
- class SetPasswordFormTest(TestDataMixin, TestCase):
- 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,
- [str(form.error_messages['password_mismatch'])]
- )
- @mock.patch('django.contrib.auth.password_validation.password_changed')
- def test_success(self, password_changed):
- user = User.objects.get(username='testclient')
- data = {
- 'new_password1': 'abc123',
- 'new_password2': 'abc123',
- }
- form = SetPasswordForm(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)
- @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)
- 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
- )
- def test_password_whitespace_not_stripped(self):
- user = User.objects.get(username='testclient')
- data = {
- 'new_password1': ' password ',
- 'new_password2': ' password ',
- }
- form = SetPasswordForm(user, data)
- self.assertTrue(form.is_valid())
- self.assertEqual(form.cleaned_data['new_password1'], data['new_password1'])
- self.assertEqual(form.cleaned_data['new_password2'], data['new_password2'])
- @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_help_text_translation(self):
- french_help_texts = [
- 'Votre mot de passe ne peut pas trop ressembler à vos autres informations personnelles.',
- 'Votre mot de passe doit contenir au minimum 12 caractères.',
- ]
- form = SetPasswordForm(self.u1)
- with translation.override('fr'):
- html = form.as_p()
- for french_text in french_help_texts:
- self.assertIn(french_text, html)
- class PasswordChangeFormTest(TestDataMixin, TestCase):
- 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, [str(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, [str(form.error_messages['password_mismatch'])])
- @mock.patch('django.contrib.auth.password_validation.password_changed')
- def test_success(self, password_changed):
- # 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())
- form.save(commit=False)
- self.assertEqual(password_changed.call_count, 0)
- form.save()
- self.assertEqual(password_changed.call_count, 1)
- 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'])
- def test_password_whitespace_not_stripped(self):
- user = User.objects.get(username='testclient')
- user.set_password(' oldpassword ')
- data = {
- 'old_password': ' oldpassword ',
- 'new_password1': ' pass ',
- 'new_password2': ' pass ',
- }
- form = PasswordChangeForm(user, data)
- self.assertTrue(form.is_valid())
- self.assertEqual(form.cleaned_data['old_password'], data['old_password'])
- self.assertEqual(form.cleaned_data['new_password1'], data['new_password1'])
- self.assertEqual(form.cleaned_data['new_password2'], data['new_password2'])
- class UserChangeFormTest(TestDataMixin, TestCase):
- 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, [str(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().__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_unusable_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())
- # original hashed password contains $
- self.assertIn('$', form.cleaned_data['password'])
- 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())
- 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))
- def test_password_excluded(self):
- class UserChangeFormWithoutPassword(UserChangeForm):
- password = None
- class Meta:
- model = User
- exclude = ['password']
- form = UserChangeFormWithoutPassword()
- self.assertNotIn('password', form.fields)
- @override_settings(TEMPLATES=AUTH_TEMPLATES)
- class PasswordResetFormTest(TestDataMixin, TestCase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- # This cleanup is necessary because contrib.sites cache
- # makes tests interfere with each other, see #11505
- Site.objects.clear_cache()
- 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):
- """
- 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()
- ))
- @override_settings(AUTH_USER_MODEL='auth_tests.CustomEmailField')
- def test_custom_email_field(self):
- email = 'test@mail.com'
- CustomEmailField.objects.create_user('test name', 'test password', email)
- form = PasswordResetForm({'email': email})
- self.assertTrue(form.is_valid())
- form.save()
- self.assertEqual(form.cleaned_data['email'], email)
- self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].to, [email])
- class ReadOnlyPasswordHashTest(SimpleTestCase):
- 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)
- @override_settings(PASSWORD_HASHERS=['django.contrib.auth.hashers.PBKDF2PasswordHasher'])
- def test_render(self):
- widget = ReadOnlyPasswordHashWidget()
- value = 'pbkdf2_sha256$100000$a6Pucb1qSFcD$WmCkn9Hqidj48NVe5x0FEM6A9YiOqQcl/83m2Z5udm0='
- self.assertHTMLEqual(
- widget.render('name', value, {'id': 'id_password'}),
- """
- <div id="id_password">
- <strong>algorithm</strong>: pbkdf2_sha256
- <strong>iterations</strong>: 100000
- <strong>salt</strong>: a6Pucb******
- <strong>hash</strong>: WmCkn9**************************************
- </div>
- """
- )
- def test_readonly_field_has_changed(self):
- field = ReadOnlyPasswordHashField()
- self.assertFalse(field.has_changed('aaa', 'bbb'))
- 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)
- def test_password_whitespace_not_stripped(self):
- user = User.objects.get(username='testclient')
- data = {
- 'password1': ' pass ',
- 'password2': ' pass ',
- }
- form = AdminPasswordChangeForm(user, data)
- self.assertTrue(form.is_valid())
- self.assertEqual(form.cleaned_data['password1'], data['password1'])
- self.assertEqual(form.cleaned_data['password2'], data['password2'])
- def test_non_matching_passwords(self):
- user = User.objects.get(username='testclient')
- data = {'password1': 'password1', 'password2': 'password2'}
- form = AdminPasswordChangeForm(user, data)
- self.assertEqual(form.errors['password2'], [form.error_messages['password_mismatch']])
- def test_missing_passwords(self):
- user = User.objects.get(username='testclient')
- data = {'password1': '', 'password2': ''}
- form = AdminPasswordChangeForm(user, data)
- required_error = [Field.default_error_messages['required']]
- self.assertEqual(form.errors['password1'], required_error)
- self.assertEqual(form.errors['password2'], required_error)
- def test_one_password(self):
- user = User.objects.get(username='testclient')
- form1 = AdminPasswordChangeForm(user, {'password1': '', 'password2': 'test'})
- required_error = [Field.default_error_messages['required']]
- self.assertEqual(form1.errors['password1'], required_error)
- self.assertNotIn('password2', form1.errors)
- form2 = AdminPasswordChangeForm(user, {'password1': 'test', 'password2': ''})
- self.assertEqual(form2.errors['password2'], required_error)
- self.assertNotIn('password1', form2.errors)
|