|
@@ -96,18 +96,11 @@ class UsernameField(forms.CharField):
|
|
|
class SetPasswordMixin:
|
|
|
"""
|
|
|
Form mixin that validates and sets a password for a user.
|
|
|
-
|
|
|
- This mixin also support setting an unusable password for a user.
|
|
|
"""
|
|
|
|
|
|
error_messages = {
|
|
|
"password_mismatch": _("The two password fields didn’t match."),
|
|
|
}
|
|
|
- usable_password_help_text = _(
|
|
|
- "Whether the user will be able to authenticate using a password or not. "
|
|
|
- "If disabled, they may still be able to authenticate using other backends, "
|
|
|
- "such as Single Sign-On or LDAP."
|
|
|
- )
|
|
|
|
|
|
@staticmethod
|
|
|
def create_password_fields(label1=_("Password"), label2=_("Password confirmation")):
|
|
@@ -127,33 +120,14 @@ class SetPasswordMixin:
|
|
|
)
|
|
|
return password1, password2
|
|
|
|
|
|
- @staticmethod
|
|
|
- def create_usable_password_field(help_text=usable_password_help_text):
|
|
|
- return forms.ChoiceField(
|
|
|
- label=_("Password-based authentication"),
|
|
|
- required=False,
|
|
|
- initial="true",
|
|
|
- choices={"true": _("Enabled"), "false": _("Disabled")},
|
|
|
- widget=forms.RadioSelect(attrs={"class": "radiolist inline"}),
|
|
|
- help_text=help_text,
|
|
|
- )
|
|
|
-
|
|
|
def validate_passwords(
|
|
|
self,
|
|
|
password1_field_name="password1",
|
|
|
password2_field_name="password2",
|
|
|
- usable_password_field_name="usable_password",
|
|
|
):
|
|
|
- usable_password = (
|
|
|
- self.cleaned_data.pop(usable_password_field_name, None) != "false"
|
|
|
- )
|
|
|
- self.cleaned_data["set_usable_password"] = usable_password
|
|
|
password1 = self.cleaned_data.get(password1_field_name)
|
|
|
password2 = self.cleaned_data.get(password2_field_name)
|
|
|
|
|
|
- if not usable_password:
|
|
|
- return self.cleaned_data
|
|
|
-
|
|
|
if not password1 and password1_field_name not in self.errors:
|
|
|
error = ValidationError(
|
|
|
self.fields[password1_field_name].error_messages["required"],
|
|
@@ -177,19 +151,68 @@ class SetPasswordMixin:
|
|
|
|
|
|
def validate_password_for_user(self, user, password_field_name="password2"):
|
|
|
password = self.cleaned_data.get(password_field_name)
|
|
|
- if password and self.cleaned_data["set_usable_password"]:
|
|
|
+ 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 SetUnusablePasswordMixin:
|
|
|
+ """
|
|
|
+ Form mixin that allows setting an unusable password for a user.
|
|
|
+
|
|
|
+ This mixin should be used in combination with `SetPasswordMixin`.
|
|
|
+ """
|
|
|
+
|
|
|
+ usable_password_help_text = _(
|
|
|
+ "Whether the user will be able to authenticate using a password or not. "
|
|
|
+ "If disabled, they may still be able to authenticate using other backends, "
|
|
|
+ "such as Single Sign-On or LDAP."
|
|
|
+ )
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def create_usable_password_field(help_text=usable_password_help_text):
|
|
|
+ return forms.ChoiceField(
|
|
|
+ label=_("Password-based authentication"),
|
|
|
+ required=False,
|
|
|
+ initial="true",
|
|
|
+ choices={"true": _("Enabled"), "false": _("Disabled")},
|
|
|
+ widget=forms.RadioSelect(attrs={"class": "radiolist inline"}),
|
|
|
+ help_text=help_text,
|
|
|
+ )
|
|
|
+
|
|
|
+ def validate_passwords(
|
|
|
+ self,
|
|
|
+ *args,
|
|
|
+ usable_password_field_name="usable_password",
|
|
|
+ **kwargs,
|
|
|
+ ):
|
|
|
+ usable_password = (
|
|
|
+ self.cleaned_data.pop(usable_password_field_name, None) != "false"
|
|
|
+ )
|
|
|
+ self.cleaned_data["set_usable_password"] = usable_password
|
|
|
+
|
|
|
+ if usable_password:
|
|
|
+ super().validate_passwords(*args, **kwargs)
|
|
|
+
|
|
|
+ def validate_password_for_user(self, user, **kwargs):
|
|
|
if self.cleaned_data["set_usable_password"]:
|
|
|
- user.set_password(self.cleaned_data[password_field_name])
|
|
|
+ super().validate_password_for_user(user, **kwargs)
|
|
|
+
|
|
|
+ def set_password_and_save(self, user, commit=True, **kwargs):
|
|
|
+ if self.cleaned_data["set_usable_password"]:
|
|
|
+ user = super().set_password_and_save(user, **kwargs, commit=commit)
|
|
|
else:
|
|
|
user.set_unusable_password()
|
|
|
- if commit:
|
|
|
- user.save()
|
|
|
+ if commit:
|
|
|
+ user.save()
|
|
|
return user
|
|
|
|
|
|
|
|
@@ -197,10 +220,12 @@ class BaseUserCreationForm(SetPasswordMixin, forms.ModelForm):
|
|
|
"""
|
|
|
A form that creates a user, with no privileges, from the given username and
|
|
|
password.
|
|
|
+
|
|
|
+ This is the documented base class for customizing the user creation form.
|
|
|
+ It should be kept mostly unchanged to ensure consistency and compatibility.
|
|
|
"""
|
|
|
|
|
|
password1, password2 = SetPasswordMixin.create_password_fields()
|
|
|
- usable_password = SetPasswordMixin.create_usable_password_field()
|
|
|
|
|
|
class Meta:
|
|
|
model = User
|
|
@@ -520,13 +545,13 @@ class PasswordChangeForm(SetPasswordForm):
|
|
|
return old_password
|
|
|
|
|
|
|
|
|
-class AdminPasswordChangeForm(SetPasswordMixin, forms.Form):
|
|
|
+class AdminPasswordChangeForm(SetUnusablePasswordMixin, SetPasswordMixin, forms.Form):
|
|
|
"""
|
|
|
A form used to change the password of a user in the admin interface.
|
|
|
"""
|
|
|
|
|
|
required_css_class = "required"
|
|
|
- usable_password_help_text = SetPasswordMixin.usable_password_help_text + (
|
|
|
+ usable_password_help_text = SetUnusablePasswordMixin.usable_password_help_text + (
|
|
|
'<ul id="id_unusable_warning" class="messagelist"><li class="warning">'
|
|
|
"If disabled, the current password for this user will be lost.</li></ul>"
|
|
|
)
|
|
@@ -538,7 +563,7 @@ class AdminPasswordChangeForm(SetPasswordMixin, forms.Form):
|
|
|
self.fields["password1"].widget.attrs["autofocus"] = True
|
|
|
if self.user.has_usable_password():
|
|
|
self.fields["usable_password"] = (
|
|
|
- SetPasswordMixin.create_usable_password_field(
|
|
|
+ SetUnusablePasswordMixin.create_usable_password_field(
|
|
|
self.usable_password_help_text
|
|
|
)
|
|
|
)
|
|
@@ -558,3 +583,8 @@ class AdminPasswordChangeForm(SetPasswordMixin, forms.Form):
|
|
|
if "set_usable_password" in data or "password1" in data and "password2" in data:
|
|
|
return ["password"]
|
|
|
return []
|
|
|
+
|
|
|
+
|
|
|
+class AdminUserCreationForm(SetUnusablePasswordMixin, UserCreationForm):
|
|
|
+
|
|
|
+ usable_password = SetUnusablePasswordMixin.create_usable_password_field()
|