1. import datetime
    
  2. import re
    
  3. from unittest import mock
    
  4. 
    
  5. from django.contrib.auth.forms import (
    
  6.     AdminPasswordChangeForm,
    
  7.     AuthenticationForm,
    
  8.     PasswordChangeForm,
    
  9.     PasswordResetForm,
    
  10.     ReadOnlyPasswordHashField,
    
  11.     ReadOnlyPasswordHashWidget,
    
  12.     SetPasswordForm,
    
  13.     UserChangeForm,
    
  14.     UserCreationForm,
    
  15.     UsernameField,
    
  16. )
    
  17. from django.contrib.auth.models import User
    
  18. from django.contrib.auth.signals import user_login_failed
    
  19. from django.contrib.sites.models import Site
    
  20. from django.core import mail
    
  21. from django.core.exceptions import ValidationError
    
  22. from django.core.mail import EmailMultiAlternatives
    
  23. from django.forms import forms
    
  24. from django.forms.fields import CharField, Field, IntegerField
    
  25. from django.test import SimpleTestCase, TestCase, override_settings
    
  26. from django.utils import translation
    
  27. from django.utils.text import capfirst
    
  28. from django.utils.translation import gettext as _
    
  29. 
    
  30. from .models.custom_user import (
    
  31.     CustomUser,
    
  32.     CustomUserWithoutIsActiveField,
    
  33.     ExtensionUser,
    
  34. )
    
  35. from .models.with_custom_email_field import CustomEmailField
    
  36. from .models.with_integer_username import IntegerUsernameUser
    
  37. from .settings import AUTH_TEMPLATES
    
  38. 
    
  39. 
    
  40. class TestDataMixin:
    
  41.     @classmethod
    
  42.     def setUpTestData(cls):
    
  43.         cls.u1 = User.objects.create_user(
    
  44.             username="testclient", password="password", email="[email protected]"
    
  45.         )
    
  46.         cls.u2 = User.objects.create_user(
    
  47.             username="inactive", password="password", is_active=False
    
  48.         )
    
  49.         cls.u3 = User.objects.create_user(username="staff", password="password")
    
  50.         cls.u4 = User.objects.create(username="empty_password", password="")
    
  51.         cls.u5 = User.objects.create(username="unmanageable_password", password="$")
    
  52.         cls.u6 = User.objects.create(username="unknown_password", password="foo$bar")
    
  53. 
    
  54. 
    
  55. class UserCreationFormTest(TestDataMixin, TestCase):
    
  56.     def test_user_already_exists(self):
    
  57.         data = {
    
  58.             "username": "testclient",
    
  59.             "password1": "test123",
    
  60.             "password2": "test123",
    
  61.         }
    
  62.         form = UserCreationForm(data)
    
  63.         self.assertFalse(form.is_valid())
    
  64.         self.assertEqual(
    
  65.             form["username"].errors,
    
  66.             [str(User._meta.get_field("username").error_messages["unique"])],
    
  67.         )
    
  68. 
    
  69.     def test_invalid_data(self):
    
  70.         data = {
    
  71.             "username": "jsmith!",
    
  72.             "password1": "test123",
    
  73.             "password2": "test123",
    
  74.         }
    
  75.         form = UserCreationForm(data)
    
  76.         self.assertFalse(form.is_valid())
    
  77.         validator = next(
    
  78.             v
    
  79.             for v in User._meta.get_field("username").validators
    
  80.             if v.code == "invalid"
    
  81.         )
    
  82.         self.assertEqual(form["username"].errors, [str(validator.message)])
    
  83. 
    
  84.     def test_password_verification(self):
    
  85.         # The verification password is incorrect.
    
  86.         data = {
    
  87.             "username": "jsmith",
    
  88.             "password1": "test123",
    
  89.             "password2": "test",
    
  90.         }
    
  91.         form = UserCreationForm(data)
    
  92.         self.assertFalse(form.is_valid())
    
  93.         self.assertEqual(
    
  94.             form["password2"].errors, [str(form.error_messages["password_mismatch"])]
    
  95.         )
    
  96. 
    
  97.     def test_both_passwords(self):
    
  98.         # One (or both) passwords weren't given
    
  99.         data = {"username": "jsmith"}
    
  100.         form = UserCreationForm(data)
    
  101.         required_error = [str(Field.default_error_messages["required"])]
    
  102.         self.assertFalse(form.is_valid())
    
  103.         self.assertEqual(form["password1"].errors, required_error)
    
  104.         self.assertEqual(form["password2"].errors, required_error)
    
  105. 
    
  106.         data["password2"] = "test123"
    
  107.         form = UserCreationForm(data)
    
  108.         self.assertFalse(form.is_valid())
    
  109.         self.assertEqual(form["password1"].errors, required_error)
    
  110.         self.assertEqual(form["password2"].errors, [])
    
  111. 
    
  112.     @mock.patch("django.contrib.auth.password_validation.password_changed")
    
  113.     def test_success(self, password_changed):
    
  114.         # The success case.
    
  115.         data = {
    
  116.             "username": "[email protected]",
    
  117.             "password1": "test123",
    
  118.             "password2": "test123",
    
  119.         }
    
  120.         form = UserCreationForm(data)
    
  121.         self.assertTrue(form.is_valid())
    
  122.         form.save(commit=False)
    
  123.         self.assertEqual(password_changed.call_count, 0)
    
  124.         u = form.save()
    
  125.         self.assertEqual(password_changed.call_count, 1)
    
  126.         self.assertEqual(repr(u), "<User: [email protected]>")
    
  127. 
    
  128.     def test_unicode_username(self):
    
  129.         data = {
    
  130.             "username": "",
    
  131.             "password1": "test123",
    
  132.             "password2": "test123",
    
  133.         }
    
  134.         form = UserCreationForm(data)
    
  135.         self.assertTrue(form.is_valid())
    
  136.         u = form.save()
    
  137.         self.assertEqual(u.username, "")
    
  138. 
    
  139.     def test_normalize_username(self):
    
  140.         # The normalization happens in AbstractBaseUser.clean() and ModelForm
    
  141.         # validation calls Model.clean().
    
  142.         ohm_username = "testΩ"  # U+2126 OHM SIGN
    
  143.         data = {
    
  144.             "username": ohm_username,
    
  145.             "password1": "pwd2",
    
  146.             "password2": "pwd2",
    
  147.         }
    
  148.         form = UserCreationForm(data)
    
  149.         self.assertTrue(form.is_valid())
    
  150.         user = form.save()
    
  151.         self.assertNotEqual(user.username, ohm_username)
    
  152.         self.assertEqual(user.username, "testΩ")  # U+03A9 GREEK CAPITAL LETTER OMEGA
    
  153. 
    
  154.     def test_invalid_username_no_normalize(self):
    
  155.         field = UsernameField(max_length=254)
    
  156.         # Usernames are not normalized if they are too long.
    
  157.         self.assertEqual(field.to_python("½" * 255), "½" * 255)
    
  158.         self.assertEqual(field.to_python("" * 254), "ff" * 254)
    
  159. 
    
  160.     def test_duplicate_normalized_unicode(self):
    
  161.         """
    
  162.         To prevent almost identical usernames, visually identical but differing
    
  163.         by their unicode code points only, Unicode NFKC normalization should
    
  164.         make appear them equal to Django.
    
  165.         """
    
  166.         omega_username = "iamtheΩ"  # U+03A9 GREEK CAPITAL LETTER OMEGA
    
  167.         ohm_username = "iamtheΩ"  # U+2126 OHM SIGN
    
  168.         self.assertNotEqual(omega_username, ohm_username)
    
  169.         User.objects.create_user(username=omega_username, password="pwd")
    
  170.         data = {
    
  171.             "username": ohm_username,
    
  172.             "password1": "pwd2",
    
  173.             "password2": "pwd2",
    
  174.         }
    
  175.         form = UserCreationForm(data)
    
  176.         self.assertFalse(form.is_valid())
    
  177.         self.assertEqual(
    
  178.             form.errors["username"], ["A user with that username already exists."]
    
  179.         )
    
  180. 
    
  181.     @override_settings(
    
  182.         AUTH_PASSWORD_VALIDATORS=[
    
  183.             {
    
  184.                 "NAME": (
    
  185.                     "django.contrib.auth.password_validation."
    
  186.                     "UserAttributeSimilarityValidator"
    
  187.                 )
    
  188.             },
    
  189.             {
    
  190.                 "NAME": (
    
  191.                     "django.contrib.auth.password_validation.MinimumLengthValidator"
    
  192.                 ),
    
  193.                 "OPTIONS": {
    
  194.                     "min_length": 12,
    
  195.                 },
    
  196.             },
    
  197.         ]
    
  198.     )
    
  199.     def test_validates_password(self):
    
  200.         data = {
    
  201.             "username": "testclient",
    
  202.             "password1": "testclient",
    
  203.             "password2": "testclient",
    
  204.         }
    
  205.         form = UserCreationForm(data)
    
  206.         self.assertFalse(form.is_valid())
    
  207.         self.assertEqual(len(form["password2"].errors), 2)
    
  208.         self.assertIn(
    
  209.             "The password is too similar to the username.", form["password2"].errors
    
  210.         )
    
  211.         self.assertIn(
    
  212.             "This password is too short. It must contain at least 12 characters.",
    
  213.             form["password2"].errors,
    
  214.         )
    
  215. 
    
  216.     def test_custom_form(self):
    
  217.         class CustomUserCreationForm(UserCreationForm):
    
  218.             class Meta(UserCreationForm.Meta):
    
  219.                 model = ExtensionUser
    
  220.                 fields = UserCreationForm.Meta.fields + ("date_of_birth",)
    
  221. 
    
  222.         data = {
    
  223.             "username": "testclient",
    
  224.             "password1": "testclient",
    
  225.             "password2": "testclient",
    
  226.             "date_of_birth": "1988-02-24",
    
  227.         }
    
  228.         form = CustomUserCreationForm(data)
    
  229.         self.assertTrue(form.is_valid())
    
  230. 
    
  231.     def test_custom_form_with_different_username_field(self):
    
  232.         class CustomUserCreationForm(UserCreationForm):
    
  233.             class Meta(UserCreationForm.Meta):
    
  234.                 model = CustomUser
    
  235.                 fields = ("email", "date_of_birth")
    
  236. 
    
  237.         data = {
    
  238.             "email": "[email protected]",
    
  239.             "password1": "testclient",
    
  240.             "password2": "testclient",
    
  241.             "date_of_birth": "1988-02-24",
    
  242.         }
    
  243.         form = CustomUserCreationForm(data)
    
  244.         self.assertTrue(form.is_valid())
    
  245. 
    
  246.     def test_custom_form_hidden_username_field(self):
    
  247.         class CustomUserCreationForm(UserCreationForm):
    
  248.             class Meta(UserCreationForm.Meta):
    
  249.                 model = CustomUserWithoutIsActiveField
    
  250.                 fields = ("email",)  # without USERNAME_FIELD
    
  251. 
    
  252.         data = {
    
  253.             "email": "[email protected]",
    
  254.             "password1": "testclient",
    
  255.             "password2": "testclient",
    
  256.         }
    
  257.         form = CustomUserCreationForm(data)
    
  258.         self.assertTrue(form.is_valid())
    
  259. 
    
  260.     def test_password_whitespace_not_stripped(self):
    
  261.         data = {
    
  262.             "username": "testuser",
    
  263.             "password1": "   testpassword   ",
    
  264.             "password2": "   testpassword   ",
    
  265.         }
    
  266.         form = UserCreationForm(data)
    
  267.         self.assertTrue(form.is_valid())
    
  268.         self.assertEqual(form.cleaned_data["password1"], data["password1"])
    
  269.         self.assertEqual(form.cleaned_data["password2"], data["password2"])
    
  270. 
    
  271.     @override_settings(
    
  272.         AUTH_PASSWORD_VALIDATORS=[
    
  273.             {
    
  274.                 "NAME": (
    
  275.                     "django.contrib.auth.password_validation."
    
  276.                     "UserAttributeSimilarityValidator"
    
  277.                 )
    
  278.             },
    
  279.         ]
    
  280.     )
    
  281.     def test_password_help_text(self):
    
  282.         form = UserCreationForm()
    
  283.         self.assertEqual(
    
  284.             form.fields["password1"].help_text,
    
  285.             "<ul><li>"
    
  286.             "Your password can’t be too similar to your other personal information."
    
  287.             "</li></ul>",
    
  288.         )
    
  289. 
    
  290.     @override_settings(
    
  291.         AUTH_PASSWORD_VALIDATORS=[
    
  292.             {
    
  293.                 "NAME": (
    
  294.                     "django.contrib.auth.password_validation."
    
  295.                     "UserAttributeSimilarityValidator"
    
  296.                 )
    
  297.             },
    
  298.         ]
    
  299.     )
    
  300.     def test_user_create_form_validates_password_with_all_data(self):
    
  301.         """UserCreationForm password validation uses all of the form's data."""
    
  302. 
    
  303.         class CustomUserCreationForm(UserCreationForm):
    
  304.             class Meta(UserCreationForm.Meta):
    
  305.                 model = User
    
  306.                 fields = ("username", "email", "first_name", "last_name")
    
  307. 
    
  308.         form = CustomUserCreationForm(
    
  309.             {
    
  310.                 "username": "testuser",
    
  311.                 "password1": "testpassword",
    
  312.                 "password2": "testpassword",
    
  313.                 "first_name": "testpassword",
    
  314.                 "last_name": "lastname",
    
  315.             }
    
  316.         )
    
  317.         self.assertFalse(form.is_valid())
    
  318.         self.assertEqual(
    
  319.             form.errors["password2"],
    
  320.             ["The password is too similar to the first name."],
    
  321.         )
    
  322. 
    
  323.     def test_username_field_autocapitalize_none(self):
    
  324.         form = UserCreationForm()
    
  325.         self.assertEqual(
    
  326.             form.fields["username"].widget.attrs.get("autocapitalize"), "none"
    
  327.         )
    
  328. 
    
  329.     def test_html_autocomplete_attributes(self):
    
  330.         form = UserCreationForm()
    
  331.         tests = (
    
  332.             ("username", "username"),
    
  333.             ("password1", "new-password"),
    
  334.             ("password2", "new-password"),
    
  335.         )
    
  336.         for field_name, autocomplete in tests:
    
  337.             with self.subTest(field_name=field_name, autocomplete=autocomplete):
    
  338.                 self.assertEqual(
    
  339.                     form.fields[field_name].widget.attrs["autocomplete"], autocomplete
    
  340.                 )
    
  341. 
    
  342. 
    
  343. # To verify that the login form rejects inactive users, use an authentication
    
  344. # backend that allows them.
    
  345. @override_settings(
    
  346.     AUTHENTICATION_BACKENDS=["django.contrib.auth.backends.AllowAllUsersModelBackend"]
    
  347. )
    
  348. class AuthenticationFormTest(TestDataMixin, TestCase):
    
  349.     def test_invalid_username(self):
    
  350.         # The user submits an invalid username.
    
  351. 
    
  352.         data = {
    
  353.             "username": "jsmith_does_not_exist",
    
  354.             "password": "test123",
    
  355.         }
    
  356.         form = AuthenticationForm(None, data)
    
  357.         self.assertFalse(form.is_valid())
    
  358.         self.assertEqual(
    
  359.             form.non_field_errors(),
    
  360.             [
    
  361.                 form.error_messages["invalid_login"]
    
  362.                 % {"username": User._meta.get_field("username").verbose_name}
    
  363.             ],
    
  364.         )
    
  365. 
    
  366.     def test_inactive_user(self):
    
  367.         # The user is inactive.
    
  368.         data = {
    
  369.             "username": "inactive",
    
  370.             "password": "password",
    
  371.         }
    
  372.         form = AuthenticationForm(None, data)
    
  373.         self.assertFalse(form.is_valid())
    
  374.         self.assertEqual(
    
  375.             form.non_field_errors(), [str(form.error_messages["inactive"])]
    
  376.         )
    
  377. 
    
  378.     # Use an authentication backend that rejects inactive users.
    
  379.     @override_settings(
    
  380.         AUTHENTICATION_BACKENDS=["django.contrib.auth.backends.ModelBackend"]
    
  381.     )
    
  382.     def test_inactive_user_incorrect_password(self):
    
  383.         """An invalid login doesn't leak the inactive status of a user."""
    
  384.         data = {
    
  385.             "username": "inactive",
    
  386.             "password": "incorrect",
    
  387.         }
    
  388.         form = AuthenticationForm(None, data)
    
  389.         self.assertFalse(form.is_valid())
    
  390.         self.assertEqual(
    
  391.             form.non_field_errors(),
    
  392.             [
    
  393.                 form.error_messages["invalid_login"]
    
  394.                 % {"username": User._meta.get_field("username").verbose_name}
    
  395.             ],
    
  396.         )
    
  397. 
    
  398.     def test_login_failed(self):
    
  399.         signal_calls = []
    
  400. 
    
  401.         def signal_handler(**kwargs):
    
  402.             signal_calls.append(kwargs)
    
  403. 
    
  404.         user_login_failed.connect(signal_handler)
    
  405.         fake_request = object()
    
  406.         try:
    
  407.             form = AuthenticationForm(
    
  408.                 fake_request,
    
  409.                 {
    
  410.                     "username": "testclient",
    
  411.                     "password": "incorrect",
    
  412.                 },
    
  413.             )
    
  414.             self.assertFalse(form.is_valid())
    
  415.             self.assertIs(signal_calls[0]["request"], fake_request)
    
  416.         finally:
    
  417.             user_login_failed.disconnect(signal_handler)
    
  418. 
    
  419.     def test_inactive_user_i18n(self):
    
  420.         with self.settings(USE_I18N=True), translation.override(
    
  421.             "pt-br", deactivate=True
    
  422.         ):
    
  423.             # The user is inactive.
    
  424.             data = {
    
  425.                 "username": "inactive",
    
  426.                 "password": "password",
    
  427.             }
    
  428.             form = AuthenticationForm(None, data)
    
  429.             self.assertFalse(form.is_valid())
    
  430.             self.assertEqual(
    
  431.                 form.non_field_errors(), [str(form.error_messages["inactive"])]
    
  432.             )
    
  433. 
    
  434.     # Use an authentication backend that allows inactive users.
    
  435.     @override_settings(
    
  436.         AUTHENTICATION_BACKENDS=[
    
  437.             "django.contrib.auth.backends.AllowAllUsersModelBackend"
    
  438.         ]
    
  439.     )
    
  440.     def test_custom_login_allowed_policy(self):
    
  441.         # The user is inactive, but our custom form policy allows them to log in.
    
  442.         data = {
    
  443.             "username": "inactive",
    
  444.             "password": "password",
    
  445.         }
    
  446. 
    
  447.         class AuthenticationFormWithInactiveUsersOkay(AuthenticationForm):
    
  448.             def confirm_login_allowed(self, user):
    
  449.                 pass
    
  450. 
    
  451.         form = AuthenticationFormWithInactiveUsersOkay(None, data)
    
  452.         self.assertTrue(form.is_valid())
    
  453. 
    
  454.         # Raise a ValidationError in the form to disallow some logins according
    
  455.         # to custom logic.
    
  456.         class PickyAuthenticationForm(AuthenticationForm):
    
  457.             def confirm_login_allowed(self, user):
    
  458.                 if user.username == "inactive":
    
  459.                     raise ValidationError("This user is disallowed.")
    
  460.                 raise ValidationError("Sorry, nobody's allowed in.")
    
  461. 
    
  462.         form = PickyAuthenticationForm(None, data)
    
  463.         self.assertFalse(form.is_valid())
    
  464.         self.assertEqual(form.non_field_errors(), ["This user is disallowed."])
    
  465. 
    
  466.         data = {
    
  467.             "username": "testclient",
    
  468.             "password": "password",
    
  469.         }
    
  470.         form = PickyAuthenticationForm(None, data)
    
  471.         self.assertFalse(form.is_valid())
    
  472.         self.assertEqual(form.non_field_errors(), ["Sorry, nobody's allowed in."])
    
  473. 
    
  474.     def test_success(self):
    
  475.         # The success case
    
  476.         data = {
    
  477.             "username": "testclient",
    
  478.             "password": "password",
    
  479.         }
    
  480.         form = AuthenticationForm(None, data)
    
  481.         self.assertTrue(form.is_valid())
    
  482.         self.assertEqual(form.non_field_errors(), [])
    
  483. 
    
  484.     def test_unicode_username(self):
    
  485.         User.objects.create_user(username="Σαρα", password="pwd")
    
  486.         data = {
    
  487.             "username": "Σαρα",
    
  488.             "password": "pwd",
    
  489.         }
    
  490.         form = AuthenticationForm(None, data)
    
  491.         self.assertTrue(form.is_valid())
    
  492.         self.assertEqual(form.non_field_errors(), [])
    
  493. 
    
  494.     @override_settings(AUTH_USER_MODEL="auth_tests.CustomEmailField")
    
  495.     def test_username_field_max_length_matches_user_model(self):
    
  496.         self.assertEqual(CustomEmailField._meta.get_field("username").max_length, 255)
    
  497.         data = {
    
  498.             "username": "u" * 255,
    
  499.             "password": "pwd",
    
  500.             "email": "[email protected]",
    
  501.         }
    
  502.         CustomEmailField.objects.create_user(**data)
    
  503.         form = AuthenticationForm(None, data)
    
  504.         self.assertEqual(form.fields["username"].max_length, 255)
    
  505.         self.assertEqual(form.fields["username"].widget.attrs.get("maxlength"), 255)
    
  506.         self.assertEqual(form.errors, {})
    
  507. 
    
  508.     @override_settings(AUTH_USER_MODEL="auth_tests.IntegerUsernameUser")
    
  509.     def test_username_field_max_length_defaults_to_254(self):
    
  510.         self.assertIsNone(IntegerUsernameUser._meta.get_field("username").max_length)
    
  511.         data = {
    
  512.             "username": "0123456",
    
  513.             "password": "password",
    
  514.         }
    
  515.         IntegerUsernameUser.objects.create_user(**data)
    
  516.         form = AuthenticationForm(None, data)
    
  517.         self.assertEqual(form.fields["username"].max_length, 254)
    
  518.         self.assertEqual(form.fields["username"].widget.attrs.get("maxlength"), 254)
    
  519.         self.assertEqual(form.errors, {})
    
  520. 
    
  521.     def test_username_field_label(self):
    
  522.         class CustomAuthenticationForm(AuthenticationForm):
    
  523.             username = CharField(label="Name", max_length=75)
    
  524. 
    
  525.         form = CustomAuthenticationForm()
    
  526.         self.assertEqual(form["username"].label, "Name")
    
  527. 
    
  528.     def test_username_field_label_not_set(self):
    
  529.         class CustomAuthenticationForm(AuthenticationForm):
    
  530.             username = CharField()
    
  531. 
    
  532.         form = CustomAuthenticationForm()
    
  533.         username_field = User._meta.get_field(User.USERNAME_FIELD)
    
  534.         self.assertEqual(
    
  535.             form.fields["username"].label, capfirst(username_field.verbose_name)
    
  536.         )
    
  537. 
    
  538.     def test_username_field_autocapitalize_none(self):
    
  539.         form = AuthenticationForm()
    
  540.         self.assertEqual(
    
  541.             form.fields["username"].widget.attrs.get("autocapitalize"), "none"
    
  542.         )
    
  543. 
    
  544.     def test_username_field_label_empty_string(self):
    
  545.         class CustomAuthenticationForm(AuthenticationForm):
    
  546.             username = CharField(label="")
    
  547. 
    
  548.         form = CustomAuthenticationForm()
    
  549.         self.assertEqual(form.fields["username"].label, "")
    
  550. 
    
  551.     def test_password_whitespace_not_stripped(self):
    
  552.         data = {
    
  553.             "username": "testuser",
    
  554.             "password": " pass ",
    
  555.         }
    
  556.         form = AuthenticationForm(None, data)
    
  557.         form.is_valid()  # Not necessary to have valid credentails for the test.
    
  558.         self.assertEqual(form.cleaned_data["password"], data["password"])
    
  559. 
    
  560.     @override_settings(AUTH_USER_MODEL="auth_tests.IntegerUsernameUser")
    
  561.     def test_integer_username(self):
    
  562.         class CustomAuthenticationForm(AuthenticationForm):
    
  563.             username = IntegerField()
    
  564. 
    
  565.         user = IntegerUsernameUser.objects.create_user(username=0, password="pwd")
    
  566.         data = {
    
  567.             "username": 0,
    
  568.             "password": "pwd",
    
  569.         }
    
  570.         form = CustomAuthenticationForm(None, data)
    
  571.         self.assertTrue(form.is_valid())
    
  572.         self.assertEqual(form.cleaned_data["username"], data["username"])
    
  573.         self.assertEqual(form.cleaned_data["password"], data["password"])
    
  574.         self.assertEqual(form.errors, {})
    
  575.         self.assertEqual(form.user_cache, user)
    
  576. 
    
  577.     def test_get_invalid_login_error(self):
    
  578.         error = AuthenticationForm().get_invalid_login_error()
    
  579.         self.assertIsInstance(error, ValidationError)
    
  580.         self.assertEqual(
    
  581.             error.message,
    
  582.             "Please enter a correct %(username)s and password. Note that both "
    
  583.             "fields may be case-sensitive.",
    
  584.         )
    
  585.         self.assertEqual(error.code, "invalid_login")
    
  586.         self.assertEqual(error.params, {"username": "username"})
    
  587. 
    
  588.     def test_html_autocomplete_attributes(self):
    
  589.         form = AuthenticationForm()
    
  590.         tests = (
    
  591.             ("username", "username"),
    
  592.             ("password", "current-password"),
    
  593.         )
    
  594.         for field_name, autocomplete in tests:
    
  595.             with self.subTest(field_name=field_name, autocomplete=autocomplete):
    
  596.                 self.assertEqual(
    
  597.                     form.fields[field_name].widget.attrs["autocomplete"], autocomplete
    
  598.                 )
    
  599. 
    
  600. 
    
  601. class SetPasswordFormTest(TestDataMixin, TestCase):
    
  602.     def test_password_verification(self):
    
  603.         # The two new passwords do not match.
    
  604.         user = User.objects.get(username="testclient")
    
  605.         data = {
    
  606.             "new_password1": "abc123",
    
  607.             "new_password2": "abc",
    
  608.         }
    
  609.         form = SetPasswordForm(user, data)
    
  610.         self.assertFalse(form.is_valid())
    
  611.         self.assertEqual(
    
  612.             form["new_password2"].errors,
    
  613.             [str(form.error_messages["password_mismatch"])],
    
  614.         )
    
  615. 
    
  616.     @mock.patch("django.contrib.auth.password_validation.password_changed")
    
  617.     def test_success(self, password_changed):
    
  618.         user = User.objects.get(username="testclient")
    
  619.         data = {
    
  620.             "new_password1": "abc123",
    
  621.             "new_password2": "abc123",
    
  622.         }
    
  623.         form = SetPasswordForm(user, data)
    
  624.         self.assertTrue(form.is_valid())
    
  625.         form.save(commit=False)
    
  626.         self.assertEqual(password_changed.call_count, 0)
    
  627.         form.save()
    
  628.         self.assertEqual(password_changed.call_count, 1)
    
  629. 
    
  630.     @override_settings(
    
  631.         AUTH_PASSWORD_VALIDATORS=[
    
  632.             {
    
  633.                 "NAME": (
    
  634.                     "django.contrib.auth.password_validation."
    
  635.                     "UserAttributeSimilarityValidator"
    
  636.                 )
    
  637.             },
    
  638.             {
    
  639.                 "NAME": (
    
  640.                     "django.contrib.auth.password_validation.MinimumLengthValidator"
    
  641.                 ),
    
  642.                 "OPTIONS": {
    
  643.                     "min_length": 12,
    
  644.                 },
    
  645.             },
    
  646.         ]
    
  647.     )
    
  648.     def test_validates_password(self):
    
  649.         user = User.objects.get(username="testclient")
    
  650.         data = {
    
  651.             "new_password1": "testclient",
    
  652.             "new_password2": "testclient",
    
  653.         }
    
  654.         form = SetPasswordForm(user, data)
    
  655.         self.assertFalse(form.is_valid())
    
  656.         self.assertEqual(len(form["new_password2"].errors), 2)
    
  657.         self.assertIn(
    
  658.             "The password is too similar to the username.", form["new_password2"].errors
    
  659.         )
    
  660.         self.assertIn(
    
  661.             "This password is too short. It must contain at least 12 characters.",
    
  662.             form["new_password2"].errors,
    
  663.         )
    
  664. 
    
  665.     def test_password_whitespace_not_stripped(self):
    
  666.         user = User.objects.get(username="testclient")
    
  667.         data = {
    
  668.             "new_password1": "   password   ",
    
  669.             "new_password2": "   password   ",
    
  670.         }
    
  671.         form = SetPasswordForm(user, data)
    
  672.         self.assertTrue(form.is_valid())
    
  673.         self.assertEqual(form.cleaned_data["new_password1"], data["new_password1"])
    
  674.         self.assertEqual(form.cleaned_data["new_password2"], data["new_password2"])
    
  675. 
    
  676.     @override_settings(
    
  677.         AUTH_PASSWORD_VALIDATORS=[
    
  678.             {
    
  679.                 "NAME": (
    
  680.                     "django.contrib.auth.password_validation."
    
  681.                     "UserAttributeSimilarityValidator"
    
  682.                 )
    
  683.             },
    
  684.             {
    
  685.                 "NAME": (
    
  686.                     "django.contrib.auth.password_validation.MinimumLengthValidator"
    
  687.                 ),
    
  688.                 "OPTIONS": {
    
  689.                     "min_length": 12,
    
  690.                 },
    
  691.             },
    
  692.         ]
    
  693.     )
    
  694.     def test_help_text_translation(self):
    
  695.         french_help_texts = [
    
  696.             "Votre mot de passe ne peut pas trop ressembler à vos autres informations "
    
  697.             "personnelles.",
    
  698.             "Votre mot de passe doit contenir au minimum 12 caractères.",
    
  699.         ]
    
  700.         form = SetPasswordForm(self.u1)
    
  701.         with translation.override("fr"):
    
  702.             html = form.as_p()
    
  703.             for french_text in french_help_texts:
    
  704.                 self.assertIn(french_text, html)
    
  705. 
    
  706.     def test_html_autocomplete_attributes(self):
    
  707.         form = SetPasswordForm(self.u1)
    
  708.         tests = (
    
  709.             ("new_password1", "new-password"),
    
  710.             ("new_password2", "new-password"),
    
  711.         )
    
  712.         for field_name, autocomplete in tests:
    
  713.             with self.subTest(field_name=field_name, autocomplete=autocomplete):
    
  714.                 self.assertEqual(
    
  715.                     form.fields[field_name].widget.attrs["autocomplete"], autocomplete
    
  716.                 )
    
  717. 
    
  718. 
    
  719. class PasswordChangeFormTest(TestDataMixin, TestCase):
    
  720.     def test_incorrect_password(self):
    
  721.         user = User.objects.get(username="testclient")
    
  722.         data = {
    
  723.             "old_password": "test",
    
  724.             "new_password1": "abc123",
    
  725.             "new_password2": "abc123",
    
  726.         }
    
  727.         form = PasswordChangeForm(user, data)
    
  728.         self.assertFalse(form.is_valid())
    
  729.         self.assertEqual(
    
  730.             form["old_password"].errors,
    
  731.             [str(form.error_messages["password_incorrect"])],
    
  732.         )
    
  733. 
    
  734.     def test_password_verification(self):
    
  735.         # The two new passwords do not match.
    
  736.         user = User.objects.get(username="testclient")
    
  737.         data = {
    
  738.             "old_password": "password",
    
  739.             "new_password1": "abc123",
    
  740.             "new_password2": "abc",
    
  741.         }
    
  742.         form = PasswordChangeForm(user, data)
    
  743.         self.assertFalse(form.is_valid())
    
  744.         self.assertEqual(
    
  745.             form["new_password2"].errors,
    
  746.             [str(form.error_messages["password_mismatch"])],
    
  747.         )
    
  748. 
    
  749.     @mock.patch("django.contrib.auth.password_validation.password_changed")
    
  750.     def test_success(self, password_changed):
    
  751.         # The success case.
    
  752.         user = User.objects.get(username="testclient")
    
  753.         data = {
    
  754.             "old_password": "password",
    
  755.             "new_password1": "abc123",
    
  756.             "new_password2": "abc123",
    
  757.         }
    
  758.         form = PasswordChangeForm(user, data)
    
  759.         self.assertTrue(form.is_valid())
    
  760.         form.save(commit=False)
    
  761.         self.assertEqual(password_changed.call_count, 0)
    
  762.         form.save()
    
  763.         self.assertEqual(password_changed.call_count, 1)
    
  764. 
    
  765.     def test_field_order(self):
    
  766.         # Regression test - check the order of fields:
    
  767.         user = User.objects.get(username="testclient")
    
  768.         self.assertEqual(
    
  769.             list(PasswordChangeForm(user, {}).fields),
    
  770.             ["old_password", "new_password1", "new_password2"],
    
  771.         )
    
  772. 
    
  773.     def test_password_whitespace_not_stripped(self):
    
  774.         user = User.objects.get(username="testclient")
    
  775.         user.set_password("   oldpassword   ")
    
  776.         data = {
    
  777.             "old_password": "   oldpassword   ",
    
  778.             "new_password1": " pass ",
    
  779.             "new_password2": " pass ",
    
  780.         }
    
  781.         form = PasswordChangeForm(user, data)
    
  782.         self.assertTrue(form.is_valid())
    
  783.         self.assertEqual(form.cleaned_data["old_password"], data["old_password"])
    
  784.         self.assertEqual(form.cleaned_data["new_password1"], data["new_password1"])
    
  785.         self.assertEqual(form.cleaned_data["new_password2"], data["new_password2"])
    
  786. 
    
  787.     def test_html_autocomplete_attributes(self):
    
  788.         user = User.objects.get(username="testclient")
    
  789.         form = PasswordChangeForm(user)
    
  790.         self.assertEqual(
    
  791.             form.fields["old_password"].widget.attrs["autocomplete"], "current-password"
    
  792.         )
    
  793. 
    
  794. 
    
  795. class UserChangeFormTest(TestDataMixin, TestCase):
    
  796.     def test_username_validity(self):
    
  797.         user = User.objects.get(username="testclient")
    
  798.         data = {"username": "not valid"}
    
  799.         form = UserChangeForm(data, instance=user)
    
  800.         self.assertFalse(form.is_valid())
    
  801.         validator = next(
    
  802.             v
    
  803.             for v in User._meta.get_field("username").validators
    
  804.             if v.code == "invalid"
    
  805.         )
    
  806.         self.assertEqual(form["username"].errors, [str(validator.message)])
    
  807. 
    
  808.     def test_bug_14242(self):
    
  809.         # A regression test, introduce by adding an optimization for the
    
  810.         # UserChangeForm.
    
  811. 
    
  812.         class MyUserForm(UserChangeForm):
    
  813.             def __init__(self, *args, **kwargs):
    
  814.                 super().__init__(*args, **kwargs)
    
  815.                 self.fields[
    
  816.                     "groups"
    
  817.                 ].help_text = "These groups give users different permissions"
    
  818. 
    
  819.             class Meta(UserChangeForm.Meta):
    
  820.                 fields = ("groups",)
    
  821. 
    
  822.         # Just check we can create it
    
  823.         MyUserForm({})
    
  824. 
    
  825.     def test_unusable_password(self):
    
  826.         user = User.objects.get(username="empty_password")
    
  827.         user.set_unusable_password()
    
  828.         user.save()
    
  829.         form = UserChangeForm(instance=user)
    
  830.         self.assertIn(_("No password set."), form.as_table())
    
  831. 
    
  832.     def test_bug_17944_empty_password(self):
    
  833.         user = User.objects.get(username="empty_password")
    
  834.         form = UserChangeForm(instance=user)
    
  835.         self.assertIn(_("No password set."), form.as_table())
    
  836. 
    
  837.     def test_bug_17944_unmanageable_password(self):
    
  838.         user = User.objects.get(username="unmanageable_password")
    
  839.         form = UserChangeForm(instance=user)
    
  840.         self.assertIn(
    
  841.             _("Invalid password format or unknown hashing algorithm."), form.as_table()
    
  842.         )
    
  843. 
    
  844.     def test_bug_17944_unknown_password_algorithm(self):
    
  845.         user = User.objects.get(username="unknown_password")
    
  846.         form = UserChangeForm(instance=user)
    
  847.         self.assertIn(
    
  848.             _("Invalid password format or unknown hashing algorithm."), form.as_table()
    
  849.         )
    
  850. 
    
  851.     def test_bug_19133(self):
    
  852.         "The change form does not return the password value"
    
  853.         # Use the form to construct the POST data
    
  854.         user = User.objects.get(username="testclient")
    
  855.         form_for_data = UserChangeForm(instance=user)
    
  856.         post_data = form_for_data.initial
    
  857. 
    
  858.         # The password field should be readonly, so anything
    
  859.         # posted here should be ignored; the form will be
    
  860.         # valid, and give back the 'initial' value for the
    
  861.         # password field.
    
  862.         post_data["password"] = "new password"
    
  863.         form = UserChangeForm(instance=user, data=post_data)
    
  864. 
    
  865.         self.assertTrue(form.is_valid())
    
  866.         # original hashed password contains $
    
  867.         self.assertIn("$", form.cleaned_data["password"])
    
  868. 
    
  869.     def test_bug_19349_bound_password_field(self):
    
  870.         user = User.objects.get(username="testclient")
    
  871.         form = UserChangeForm(data={}, instance=user)
    
  872.         # When rendering the bound password field,
    
  873.         # ReadOnlyPasswordHashWidget needs the initial
    
  874.         # value to render correctly
    
  875.         self.assertEqual(form.initial["password"], form["password"].value())
    
  876. 
    
  877.     def test_custom_form(self):
    
  878.         class CustomUserChangeForm(UserChangeForm):
    
  879.             class Meta(UserChangeForm.Meta):
    
  880.                 model = ExtensionUser
    
  881.                 fields = (
    
  882.                     "username",
    
  883.                     "password",
    
  884.                     "date_of_birth",
    
  885.                 )
    
  886. 
    
  887.         user = User.objects.get(username="testclient")
    
  888.         data = {
    
  889.             "username": "testclient",
    
  890.             "password": "testclient",
    
  891.             "date_of_birth": "1998-02-24",
    
  892.         }
    
  893.         form = CustomUserChangeForm(data, instance=user)
    
  894.         self.assertTrue(form.is_valid())
    
  895.         form.save()
    
  896.         self.assertEqual(form.cleaned_data["username"], "testclient")
    
  897.         self.assertEqual(form.cleaned_data["date_of_birth"], datetime.date(1998, 2, 24))
    
  898. 
    
  899.     def test_password_excluded(self):
    
  900.         class UserChangeFormWithoutPassword(UserChangeForm):
    
  901.             password = None
    
  902. 
    
  903.             class Meta:
    
  904.                 model = User
    
  905.                 exclude = ["password"]
    
  906. 
    
  907.         form = UserChangeFormWithoutPassword()
    
  908.         self.assertNotIn("password", form.fields)
    
  909. 
    
  910.     def test_username_field_autocapitalize_none(self):
    
  911.         form = UserChangeForm()
    
  912.         self.assertEqual(
    
  913.             form.fields["username"].widget.attrs.get("autocapitalize"), "none"
    
  914.         )
    
  915. 
    
  916. 
    
  917. @override_settings(TEMPLATES=AUTH_TEMPLATES)
    
  918. class PasswordResetFormTest(TestDataMixin, TestCase):
    
  919.     @classmethod
    
  920.     def setUpClass(cls):
    
  921.         super().setUpClass()
    
  922.         # This cleanup is necessary because contrib.sites cache
    
  923.         # makes tests interfere with each other, see #11505
    
  924.         Site.objects.clear_cache()
    
  925. 
    
  926.     def create_dummy_user(self):
    
  927.         """
    
  928.         Create a user and return a tuple (user_object, username, email).
    
  929.         """
    
  930.         username = "jsmith"
    
  931.         email = "[email protected]"
    
  932.         user = User.objects.create_user(username, email, "test123")
    
  933.         return (user, username, email)
    
  934. 
    
  935.     def test_invalid_email(self):
    
  936.         data = {"email": "not valid"}
    
  937.         form = PasswordResetForm(data)
    
  938.         self.assertFalse(form.is_valid())
    
  939.         self.assertEqual(form["email"].errors, [_("Enter a valid email address.")])
    
  940. 
    
  941.     def test_user_email_unicode_collision(self):
    
  942.         User.objects.create_user("mike123", "[email protected]", "test123")
    
  943.         User.objects.create_user("mike456", "[email protected]", "test123")
    
  944.         data = {"email": "[email protected]"}
    
  945.         form = PasswordResetForm(data)
    
  946.         self.assertTrue(form.is_valid())
    
  947.         form.save()
    
  948.         self.assertEqual(len(mail.outbox), 1)
    
  949.         self.assertEqual(mail.outbox[0].to, ["[email protected]"])
    
  950. 
    
  951.     def test_user_email_domain_unicode_collision(self):
    
  952.         User.objects.create_user("mike123", "[email protected]", "test123")
    
  953.         User.objects.create_user("mike456", "mike@ıxample.org", "test123")
    
  954.         data = {"email": "mike@ıxample.org"}
    
  955.         form = PasswordResetForm(data)
    
  956.         self.assertTrue(form.is_valid())
    
  957.         form.save()
    
  958.         self.assertEqual(len(mail.outbox), 1)
    
  959.         self.assertEqual(mail.outbox[0].to, ["mike@ıxample.org"])
    
  960. 
    
  961.     def test_user_email_unicode_collision_nonexistent(self):
    
  962.         User.objects.create_user("mike123", "[email protected]", "test123")
    
  963.         data = {"email": "[email protected]"}
    
  964.         form = PasswordResetForm(data)
    
  965.         self.assertTrue(form.is_valid())
    
  966.         form.save()
    
  967.         self.assertEqual(len(mail.outbox), 0)
    
  968. 
    
  969.     def test_user_email_domain_unicode_collision_nonexistent(self):
    
  970.         User.objects.create_user("mike123", "[email protected]", "test123")
    
  971.         data = {"email": "mike@ıxample.org"}
    
  972.         form = PasswordResetForm(data)
    
  973.         self.assertTrue(form.is_valid())
    
  974.         form.save()
    
  975.         self.assertEqual(len(mail.outbox), 0)
    
  976. 
    
  977.     def test_nonexistent_email(self):
    
  978.         """
    
  979.         Test nonexistent email address. This should not fail because it would
    
  980.         expose information about registered users.
    
  981.         """
    
  982.         data = {"email": "[email protected]"}
    
  983.         form = PasswordResetForm(data)
    
  984.         self.assertTrue(form.is_valid())
    
  985.         self.assertEqual(len(mail.outbox), 0)
    
  986. 
    
  987.     def test_cleaned_data(self):
    
  988.         (user, username, email) = self.create_dummy_user()
    
  989.         data = {"email": email}
    
  990.         form = PasswordResetForm(data)
    
  991.         self.assertTrue(form.is_valid())
    
  992.         form.save(domain_override="example.com")
    
  993.         self.assertEqual(form.cleaned_data["email"], email)
    
  994.         self.assertEqual(len(mail.outbox), 1)
    
  995. 
    
  996.     def test_custom_email_subject(self):
    
  997.         data = {"email": "[email protected]"}
    
  998.         form = PasswordResetForm(data)
    
  999.         self.assertTrue(form.is_valid())
    
  1000.         # Since we're not providing a request object, we must provide a
    
  1001.         # domain_override to prevent the save operation from failing in the
    
  1002.         # potential case where contrib.sites is not installed. Refs #16412.
    
  1003.         form.save(domain_override="example.com")
    
  1004.         self.assertEqual(len(mail.outbox), 1)
    
  1005.         self.assertEqual(mail.outbox[0].subject, "Custom password reset on example.com")
    
  1006. 
    
  1007.     def test_custom_email_constructor(self):
    
  1008.         data = {"email": "[email protected]"}
    
  1009. 
    
  1010.         class CustomEmailPasswordResetForm(PasswordResetForm):
    
  1011.             def send_mail(
    
  1012.                 self,
    
  1013.                 subject_template_name,
    
  1014.                 email_template_name,
    
  1015.                 context,
    
  1016.                 from_email,
    
  1017.                 to_email,
    
  1018.                 html_email_template_name=None,
    
  1019.             ):
    
  1020.                 EmailMultiAlternatives(
    
  1021.                     "Forgot your password?",
    
  1022.                     "Sorry to hear you forgot your password.",
    
  1023.                     None,
    
  1024.                     [to_email],
    
  1025.                     ["[email protected]"],
    
  1026.                     headers={"Reply-To": "[email protected]"},
    
  1027.                     alternatives=[
    
  1028.                         ("Really sorry to hear you forgot your password.", "text/html")
    
  1029.                     ],
    
  1030.                 ).send()
    
  1031. 
    
  1032.         form = CustomEmailPasswordResetForm(data)
    
  1033.         self.assertTrue(form.is_valid())
    
  1034.         # Since we're not providing a request object, we must provide a
    
  1035.         # domain_override to prevent the save operation from failing in the
    
  1036.         # potential case where contrib.sites is not installed. Refs #16412.
    
  1037.         form.save(domain_override="example.com")
    
  1038.         self.assertEqual(len(mail.outbox), 1)
    
  1039.         self.assertEqual(mail.outbox[0].subject, "Forgot your password?")
    
  1040.         self.assertEqual(mail.outbox[0].bcc, ["[email protected]"])
    
  1041.         self.assertEqual(mail.outbox[0].content_subtype, "plain")
    
  1042. 
    
  1043.     def test_preserve_username_case(self):
    
  1044.         """
    
  1045.         Preserve the case of the user name (before the @ in the email address)
    
  1046.         when creating a user (#5605).
    
  1047.         """
    
  1048.         user = User.objects.create_user("forms_test2", "[email protected]", "test")
    
  1049.         self.assertEqual(user.email, "[email protected]")
    
  1050.         user = User.objects.create_user("forms_test3", "tesT", "test")
    
  1051.         self.assertEqual(user.email, "tesT")
    
  1052. 
    
  1053.     def test_inactive_user(self):
    
  1054.         """
    
  1055.         Inactive user cannot receive password reset email.
    
  1056.         """
    
  1057.         (user, username, email) = self.create_dummy_user()
    
  1058.         user.is_active = False
    
  1059.         user.save()
    
  1060.         form = PasswordResetForm({"email": email})
    
  1061.         self.assertTrue(form.is_valid())
    
  1062.         form.save()
    
  1063.         self.assertEqual(len(mail.outbox), 0)
    
  1064. 
    
  1065.     def test_unusable_password(self):
    
  1066.         user = User.objects.create_user("testuser", "[email protected]", "test")
    
  1067.         data = {"email": "[email protected]"}
    
  1068.         form = PasswordResetForm(data)
    
  1069.         self.assertTrue(form.is_valid())
    
  1070.         user.set_unusable_password()
    
  1071.         user.save()
    
  1072.         form = PasswordResetForm(data)
    
  1073.         # The form itself is valid, but no email is sent
    
  1074.         self.assertTrue(form.is_valid())
    
  1075.         form.save()
    
  1076.         self.assertEqual(len(mail.outbox), 0)
    
  1077. 
    
  1078.     def test_save_plaintext_email(self):
    
  1079.         """
    
  1080.         Test the PasswordResetForm.save() method with no html_email_template_name
    
  1081.         parameter passed in.
    
  1082.         Test to ensure original behavior is unchanged after the parameter was added.
    
  1083.         """
    
  1084.         (user, username, email) = self.create_dummy_user()
    
  1085.         form = PasswordResetForm({"email": email})
    
  1086.         self.assertTrue(form.is_valid())
    
  1087.         form.save()
    
  1088.         self.assertEqual(len(mail.outbox), 1)
    
  1089.         message = mail.outbox[0].message()
    
  1090.         self.assertFalse(message.is_multipart())
    
  1091.         self.assertEqual(message.get_content_type(), "text/plain")
    
  1092.         self.assertEqual(message.get("subject"), "Custom password reset on example.com")
    
  1093.         self.assertEqual(len(mail.outbox[0].alternatives), 0)
    
  1094.         self.assertEqual(message.get_all("to"), [email])
    
  1095.         self.assertTrue(
    
  1096.             re.match(r"^http://example.com/reset/[\w+/-]", message.get_payload())
    
  1097.         )
    
  1098. 
    
  1099.     def test_save_html_email_template_name(self):
    
  1100.         """
    
  1101.         Test the PasswordResetForm.save() method with html_email_template_name
    
  1102.         parameter specified.
    
  1103.         Test to ensure that a multipart email is sent with both text/plain
    
  1104.         and text/html parts.
    
  1105.         """
    
  1106.         (user, username, email) = self.create_dummy_user()
    
  1107.         form = PasswordResetForm({"email": email})
    
  1108.         self.assertTrue(form.is_valid())
    
  1109.         form.save(
    
  1110.             html_email_template_name="registration/html_password_reset_email.html"
    
  1111.         )
    
  1112.         self.assertEqual(len(mail.outbox), 1)
    
  1113.         self.assertEqual(len(mail.outbox[0].alternatives), 1)
    
  1114.         message = mail.outbox[0].message()
    
  1115.         self.assertEqual(message.get("subject"), "Custom password reset on example.com")
    
  1116.         self.assertEqual(len(message.get_payload()), 2)
    
  1117.         self.assertTrue(message.is_multipart())
    
  1118.         self.assertEqual(message.get_payload(0).get_content_type(), "text/plain")
    
  1119.         self.assertEqual(message.get_payload(1).get_content_type(), "text/html")
    
  1120.         self.assertEqual(message.get_all("to"), [email])
    
  1121.         self.assertTrue(
    
  1122.             re.match(
    
  1123.                 r"^http://example.com/reset/[\w/-]+",
    
  1124.                 message.get_payload(0).get_payload(),
    
  1125.             )
    
  1126.         )
    
  1127.         self.assertTrue(
    
  1128.             re.match(
    
  1129.                 r'^<html><a href="http://example.com/reset/[\w/-]+/">Link</a></html>$',
    
  1130.                 message.get_payload(1).get_payload(),
    
  1131.             )
    
  1132.         )
    
  1133. 
    
  1134.     @override_settings(AUTH_USER_MODEL="auth_tests.CustomEmailField")
    
  1135.     def test_custom_email_field(self):
    
  1136.         email = "[email protected]"
    
  1137.         CustomEmailField.objects.create_user("test name", "test password", email)
    
  1138.         form = PasswordResetForm({"email": email})
    
  1139.         self.assertTrue(form.is_valid())
    
  1140.         form.save()
    
  1141.         self.assertEqual(form.cleaned_data["email"], email)
    
  1142.         self.assertEqual(len(mail.outbox), 1)
    
  1143.         self.assertEqual(mail.outbox[0].to, [email])
    
  1144. 
    
  1145.     def test_html_autocomplete_attributes(self):
    
  1146.         form = PasswordResetForm()
    
  1147.         self.assertEqual(form.fields["email"].widget.attrs["autocomplete"], "email")
    
  1148. 
    
  1149. 
    
  1150. class ReadOnlyPasswordHashTest(SimpleTestCase):
    
  1151.     def test_bug_19349_render_with_none_value(self):
    
  1152.         # Rendering the widget with value set to None
    
  1153.         # mustn't raise an exception.
    
  1154.         widget = ReadOnlyPasswordHashWidget()
    
  1155.         html = widget.render(name="password", value=None, attrs={})
    
  1156.         self.assertIn(_("No password set."), html)
    
  1157. 
    
  1158.     @override_settings(
    
  1159.         PASSWORD_HASHERS=["django.contrib.auth.hashers.PBKDF2PasswordHasher"]
    
  1160.     )
    
  1161.     def test_render(self):
    
  1162.         widget = ReadOnlyPasswordHashWidget()
    
  1163.         value = (
    
  1164.             "pbkdf2_sha256$100000$a6Pucb1qSFcD$WmCkn9Hqidj48NVe5x0FEM6A9YiOqQcl/83m2Z5u"
    
  1165.             "dm0="
    
  1166.         )
    
  1167.         self.assertHTMLEqual(
    
  1168.             widget.render("name", value, {"id": "id_password"}),
    
  1169.             """
    
  1170.             <div id="id_password">
    
  1171.                 <strong>algorithm</strong>: pbkdf2_sha256
    
  1172.                 <strong>iterations</strong>: 100000
    
  1173.                 <strong>salt</strong>: a6Pucb******
    
  1174.                 <strong>hash</strong>: WmCkn9**************************************
    
  1175.             </div>
    
  1176.             """,
    
  1177.         )
    
  1178. 
    
  1179.     def test_readonly_field_has_changed(self):
    
  1180.         field = ReadOnlyPasswordHashField()
    
  1181.         self.assertIs(field.disabled, True)
    
  1182.         self.assertFalse(field.has_changed("aaa", "bbb"))
    
  1183. 
    
  1184.     def test_label(self):
    
  1185.         """
    
  1186.         ReadOnlyPasswordHashWidget doesn't contain a for attribute in the
    
  1187.         <label> because it doesn't have any labelable elements.
    
  1188.         """
    
  1189. 
    
  1190.         class TestForm(forms.Form):
    
  1191.             hash_field = ReadOnlyPasswordHashField()
    
  1192. 
    
  1193.         bound_field = TestForm()["hash_field"]
    
  1194.         self.assertIsNone(bound_field.field.widget.id_for_label("id"))
    
  1195.         self.assertEqual(bound_field.label_tag(), "<label>Hash field:</label>")
    
  1196. 
    
  1197. 
    
  1198. class AdminPasswordChangeFormTest(TestDataMixin, TestCase):
    
  1199.     @mock.patch("django.contrib.auth.password_validation.password_changed")
    
  1200.     def test_success(self, password_changed):
    
  1201.         user = User.objects.get(username="testclient")
    
  1202.         data = {
    
  1203.             "password1": "test123",
    
  1204.             "password2": "test123",
    
  1205.         }
    
  1206.         form = AdminPasswordChangeForm(user, data)
    
  1207.         self.assertTrue(form.is_valid())
    
  1208.         form.save(commit=False)
    
  1209.         self.assertEqual(password_changed.call_count, 0)
    
  1210.         form.save()
    
  1211.         self.assertEqual(password_changed.call_count, 1)
    
  1212. 
    
  1213.     def test_password_whitespace_not_stripped(self):
    
  1214.         user = User.objects.get(username="testclient")
    
  1215.         data = {
    
  1216.             "password1": " pass ",
    
  1217.             "password2": " pass ",
    
  1218.         }
    
  1219.         form = AdminPasswordChangeForm(user, data)
    
  1220.         self.assertTrue(form.is_valid())
    
  1221.         self.assertEqual(form.cleaned_data["password1"], data["password1"])
    
  1222.         self.assertEqual(form.cleaned_data["password2"], data["password2"])
    
  1223. 
    
  1224.     def test_non_matching_passwords(self):
    
  1225.         user = User.objects.get(username="testclient")
    
  1226.         data = {"password1": "password1", "password2": "password2"}
    
  1227.         form = AdminPasswordChangeForm(user, data)
    
  1228.         self.assertEqual(
    
  1229.             form.errors["password2"], [form.error_messages["password_mismatch"]]
    
  1230.         )
    
  1231. 
    
  1232.     def test_missing_passwords(self):
    
  1233.         user = User.objects.get(username="testclient")
    
  1234.         data = {"password1": "", "password2": ""}
    
  1235.         form = AdminPasswordChangeForm(user, data)
    
  1236.         required_error = [Field.default_error_messages["required"]]
    
  1237.         self.assertEqual(form.errors["password1"], required_error)
    
  1238.         self.assertEqual(form.errors["password2"], required_error)
    
  1239. 
    
  1240.     def test_one_password(self):
    
  1241.         user = User.objects.get(username="testclient")
    
  1242.         form1 = AdminPasswordChangeForm(user, {"password1": "", "password2": "test"})
    
  1243.         required_error = [Field.default_error_messages["required"]]
    
  1244.         self.assertEqual(form1.errors["password1"], required_error)
    
  1245.         self.assertNotIn("password2", form1.errors)
    
  1246.         form2 = AdminPasswordChangeForm(user, {"password1": "test", "password2": ""})
    
  1247.         self.assertEqual(form2.errors["password2"], required_error)
    
  1248.         self.assertNotIn("password1", form2.errors)
    
  1249. 
    
  1250.     def test_html_autocomplete_attributes(self):
    
  1251.         user = User.objects.get(username="testclient")
    
  1252.         form = AdminPasswordChangeForm(user)
    
  1253.         tests = (
    
  1254.             ("password1", "new-password"),
    
  1255.             ("password2", "new-password"),
    
  1256.         )
    
  1257.         for field_name, autocomplete in tests:
    
  1258.             with self.subTest(field_name=field_name, autocomplete=autocomplete):
    
  1259.                 self.assertEqual(
    
  1260.                     form.fields[field_name].widget.attrs["autocomplete"], autocomplete
    
  1261.                 )