summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
Diffstat (limited to 'django')
-rw-r--r--django/contrib/auth/admin.py4
-rw-r--r--django/contrib/auth/forms.py98
2 files changed, 66 insertions, 36 deletions
diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py
index 8e1d63ef07..e977d3ded5 100644
--- a/django/contrib/auth/admin.py
+++ b/django/contrib/auth/admin.py
@@ -5,8 +5,8 @@ from django.contrib.admin.utils import unquote
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import (
AdminPasswordChangeForm,
+ AdminUserCreationForm,
UserChangeForm,
- UserCreationForm,
)
from django.contrib.auth.models import Group, User
from django.core.exceptions import PermissionDenied
@@ -71,7 +71,7 @@ class UserAdmin(admin.ModelAdmin):
),
)
form = UserChangeForm
- add_form = UserCreationForm
+ add_form = AdminUserCreationForm
change_password_form = AdminPasswordChangeForm
list_display = ("username", "email", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active", "groups")
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index 31e96ff91c..a11668944a 100644
--- a/django/contrib/auth/forms.py
+++ b/django/contrib/auth/forms.py
@@ -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()