Refs #34429 -- Created `SetPasswordMixin` to reuse password validation logic in auth forms.

Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
This commit is contained in:
Fabian Braun 2024-01-23 16:35:36 +01:00 committed by Natalia
parent 31314980be
commit f64c528c17
1 changed files with 76 additions and 97 deletions

View File

@ -89,27 +89,65 @@ class UsernameField(forms.CharField):
}
class BaseUserCreationForm(forms.ModelForm):
class SetPasswordMixin:
"""
A form that creates a user, with no privileges, from the given username and
password.
Form mixin that validates and sets a password for a user.
"""
error_messages = {
"password_mismatch": _("The two password fields didnt match."),
}
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
@staticmethod
def create_password_fields(label1=_("Password"), label2=_("Password confirmation")):
password1 = forms.CharField(
label=label1,
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=label2,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
return password1, password2
def validate_passwords(
self, password1_field_name="password1", password2_field_name="password2"
):
password1 = self.cleaned_data.get(password1_field_name)
password2 = self.cleaned_data.get(password2_field_name)
if password1 and password2 and password1 != password2:
error = ValidationError(
self.error_messages["password_mismatch"],
code="password_mismatch",
)
self.add_error(password2_field_name, error)
def validate_password_for_user(self, user, password_field_name="password2"):
password = self.cleaned_data.get(password_field_name)
if password:
try:
password_validation.validate_password(password, user)
except ValidationError as error:
self.add_error(password_field_name, error)
def set_password_and_save(self, user, password_field_name="password1", commit=True):
user.set_password(self.cleaned_data[password_field_name])
if commit:
user.save()
return user
class BaseUserCreationForm(SetPasswordMixin, forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
password1, password2 = SetPasswordMixin.create_password_fields()
class Meta:
model = User
@ -123,34 +161,21 @@ class BaseUserCreationForm(forms.ModelForm):
"autofocus"
] = True
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError(
self.error_messages["password_mismatch"],
code="password_mismatch",
)
return password2
def clean(self):
self.validate_passwords()
return super().clean()
def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
password = self.cleaned_data.get("password2")
if password:
try:
password_validation.validate_password(password, self.instance)
except ValidationError as error:
self.add_error("password2", error)
self.validate_password_for_user(self.instance)
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
if hasattr(self, "save_m2m"):
self.save_m2m()
user = self.set_password_and_save(user, commit=commit)
if commit and hasattr(self, "save_m2m"):
self.save_m2m()
return user
@ -383,48 +408,27 @@ class PasswordResetForm(forms.Form):
)
class SetPasswordForm(forms.Form):
class SetPasswordForm(SetPasswordMixin, forms.Form):
"""
A form that lets a user set their password without entering the old
password
"""
error_messages = {
"password_mismatch": _("The two password fields didnt match."),
}
new_password1 = forms.CharField(
label=_("New password"),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text=password_validation.password_validators_help_text_html(),
)
new_password2 = forms.CharField(
label=_("New password confirmation"),
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
new_password1, new_password2 = SetPasswordMixin.create_password_fields(
label1=_("New password"), label2=_("New password confirmation")
)
def __init__(self, user, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
def clean_new_password2(self):
password1 = self.cleaned_data.get("new_password1")
password2 = self.cleaned_data.get("new_password2")
if password1 and password2 and password1 != password2:
raise ValidationError(
self.error_messages["password_mismatch"],
code="password_mismatch",
)
password_validation.validate_password(password2, self.user)
return password2
def clean(self):
self.validate_passwords("new_password1", "new_password2")
self.validate_password_for_user(self.user, "new_password2")
return super().clean()
def save(self, commit=True):
password = self.cleaned_data["new_password1"]
self.user.set_password(password)
if commit:
self.user.save()
return self.user
return self.set_password_and_save(self.user, "new_password1", commit=commit)
class PasswordChangeForm(SetPasswordForm):
@ -462,52 +466,27 @@ class PasswordChangeForm(SetPasswordForm):
return old_password
class AdminPasswordChangeForm(forms.Form):
class AdminPasswordChangeForm(SetPasswordMixin, forms.Form):
"""
A form used to change the password of a user in the admin interface.
"""
error_messages = {
"password_mismatch": _("The two password fields didnt match."),
}
required_css_class = "required"
password1 = forms.CharField(
label=_("Password"),
widget=forms.PasswordInput(
attrs={"autocomplete": "new-password", "autofocus": True}
),
strip=False,
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password (again)"),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
password1, password2 = SetPasswordMixin.create_password_fields()
def __init__(self, user, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
self.fields["password1"].widget.attrs["autofocus"] = True
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError(
self.error_messages["password_mismatch"],
code="password_mismatch",
)
password_validation.validate_password(password2, self.user)
return password2
def clean(self):
self.validate_passwords()
self.validate_password_for_user(self.user)
return super().clean()
def save(self, commit=True):
"""Save the new password."""
password = self.cleaned_data["password1"]
self.user.set_password(password)
if commit:
self.user.save()
return self.user
return self.set_password_and_save(self.user, commit=commit)
@property
def changed_data(self):