perform caseinsensitive comparison of two identifiers using the recommended algorithm from unicode technical report 36 section 2 11 2b2 normalization can increase the string length e g ff 12 but cannot reduce it so there is no point in normalizing invalid data moreover unicode normalization is very slow on windows and can be a dos attack vector a form that creates a user with no privileges from the given username and password validate the password after self instance is updated with form data by super reject usernames that differ only in case username self cleaneddata getusername if username and self meta model objects filterusernameiexactusername exists self updateerrors validationerror username self instance uniqueerrormessage self meta model username else return username class userchangeformforms modelform password readonlypasswordhashfield labelpassword helptext raw passwords are not stored so there is no way to see this users password but you can change the password using a hrefthis forma class meta model user fields all fieldclasses username usernamefield def initself args kwargs super initargs kwargs password self fields getpassword if password password helptext password helptext format f self instance pkpassword userpermissions self fields getuserpermissions if userpermissions userpermissions queryset userpermissions queryset selectrelated contenttype class authenticationformforms form username usernamefieldwidgetforms textinputattrsautofocus true password forms charfield labelpassword stripfalse widgetforms passwordinputattrsautocomplete currentpassword errormessages invalidlogin please enter a correct usernames and password note that both fields may be casesensitive inactive this account is inactive def initself requestnone args kwargs self request request self usercache none super initargs kwargs set the max length and label for the username field self usernamefield usermodel meta getfieldusermodel usernamefield usernamemaxlength self usernamefield maxlength or 254 self fieldsusername maxlength usernamemaxlength self fieldsusername widget attrsmaxlength usernamemaxlength if self fieldsusername label is none self fieldsusername label capfirstself usernamefield verbosename def cleanself username self cleaneddata getusername password self cleaneddata getpassword if username is not none and password self usercache authenticate self request usernameusername passwordpassword if self usercache is none raise self getinvalidloginerror else self confirmloginallowedself usercache return self cleaneddata def confirmloginallowedself user if not user isactive raise validationerror self errormessagesinactive codeinactive def getuserself return self usercache def getinvalidloginerrorself return validationerror self errormessagesinvalidlogin codeinvalidlogin paramsusername self usernamefield verbosename class passwordresetformforms form email forms emailfield labelemail maxlength254 widgetforms emailinputattrsautocomplete email def sendmail self subjecttemplatename emailtemplatename context fromemail toemail htmlemailtemplatenamenone subject loader rendertostringsubjecttemplatename context email subject must not contain newlines subject joinsubject splitlines body loader rendertostringemailtemplatename context emailmessage emailmultialternativessubject body fromemail toemail if htmlemailtemplatename is not none htmlemail loader rendertostringhtmlemailtemplatename context emailmessage attachalternativehtmlemail texthtml emailmessage send def getusersself email emailfieldname usermodel getemailfieldname activeusers usermodel defaultmanager filter siexact emailfieldname email isactive true return u for u in activeusers if u hasusablepassword and unicodecicompareemail getattru emailfieldname def save self domainoverridenone subjecttemplatenameregistrationpasswordresetsubject txt emailtemplatenameregistrationpasswordresetemail html usehttpsfalse tokengeneratordefaulttokengenerator fromemailnone requestnone htmlemailtemplatenamenone extraemailcontextnone email self cleaneddataemail if not domainoverride currentsite getcurrentsiterequest sitename currentsite name domain currentsite domain else sitename domain domainoverride emailfieldname usermodel getemailfieldname for user in self getusersemail useremail getattruser emailfieldname context email useremail domain domain sitename sitename uid urlsafebase64encodeforcebytesuser pk user user token tokengenerator maketokenuser protocol https if usehttps else http extraemailcontext or self sendmail subjecttemplatename emailtemplatename context fromemail useremail htmlemailtemplatenamehtmlemailtemplatename class setpasswordformforms form errormessages passwordmismatch the two password fields didnt match newpassword1 forms charfield labelnew password widgetforms passwordinputattrsautocomplete newpassword stripfalse helptextpasswordvalidation passwordvalidatorshelptexthtml newpassword2 forms charfield labelnew password confirmation stripfalse widgetforms passwordinputattrsautocomplete newpassword def initself user args kwargs self user user super initargs kwargs def cleannewpassword2self password1 self cleaneddata getnewpassword1 password2 self cleaneddata getnewpassword2 if password1 and password2 and password1 password2 raise validationerror self errormessagespasswordmismatch codepasswordmismatch passwordvalidation validatepasswordpassword2 self user return password2 def saveself committrue password self cleaneddatanewpassword1 self user setpasswordpassword if commit self user save return self user class passwordchangeformsetpasswordform errormessages setpasswordform errormessages passwordincorrect your old password was entered incorrectly please enter it again oldpassword forms charfield labelold password stripfalse widgetforms passwordinput attrsautocomplete currentpassword autofocus true fieldorder oldpassword newpassword1 newpassword2 def cleanoldpasswordself oldpassword self cleaneddataoldpassword if not self user checkpasswordoldpassword raise validationerror self errormessagespasswordincorrect codepasswordincorrect return oldpassword class adminpasswordchangeformforms form errormessages passwordmismatch the two password fields didnt match requiredcssclass required password1 forms charfield labelpassword widgetforms passwordinput attrsautocomplete newpassword autofocus true stripfalse helptextpasswordvalidation passwordvalidatorshelptexthtml password2 forms charfield labelpassword again widgetforms passwordinputattrsautocomplete newpassword stripfalse helptextenter the same password as before for verification def initself user args kwargs self user user super initargs kwargs def cleanpassword2self password1 self cleaneddata getpassword1 password2 self cleaneddata getpassword2 if password1 and password2 and password1 password2 raise validationerror self errormessagespasswordmismatch codepasswordmismatch passwordvalidation validatepasswordpassword2 self user return password2 def saveself committrue perform case insensitive comparison of two identifiers using the recommended algorithm from unicode technical report 36 section 2 11 2 b 2 normalization can increase the string length e g ﬀ ff ½ 1 2 but cannot reduce it so there is no point in normalizing invalid data moreover unicode normalization is very slow on windows and can be a dos attack vector a form that creates a user with no privileges from the given username and password validate the password after self instance is updated with form data by super reject usernames that differ only in case base class for authenticating users extend this to get a form that accepts username password logins the request parameter is set for custom auth use by subclasses the form data comes in via the standard data kwarg set the max length and label for the username field controls whether the given user may log in this is a policy setting independent of end user authentication this default behavior is to allow login by active users and reject login by inactive users if the given user cannot log in this method should raise a validationerror if the given user may log in this method should return none send a django core mail emailmultialternatives to to_email email subject must not contain newlines given an email return matching user s who should receive a reset this allows subclasses to more easily customize the default policies that prevent inactive users and users with unusable passwords from resetting their password generate a one use only link for resetting password and send it to the user a form that lets a user set their password without entering the old password a form that lets a user change their password by entering their old password validate that the old_password field is correct a form used to change the password of a user in the admin interface save the new password
import unicodedata

from django import forms
from django.contrib.auth import authenticate, get_user_model, password_validation
from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, identify_hasher
from django.contrib.auth.models import User
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ValidationError
from django.core.mail import EmailMultiAlternatives
from django.template import loader
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.utils.text import capfirst
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _

UserModel = get_user_model()


def _unicode_ci_compare(s1, s2):
    
    return (
        unicodedata.normalize("NFKC", s1).casefold()
        == unicodedata.normalize("NFKC", s2).casefold()
    )


class ReadOnlyPasswordHashWidget(forms.Widget):
    template_name = "auth/widgets/read_only_password_hash.html"
    read_only = True

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        summary = []
        if not value or value.startswith(UNUSABLE_PASSWORD_PREFIX):
            summary.append({"label": gettext("No password set.")})
        else:
            try:
                hasher = identify_hasher(value)
            except ValueError:
                summary.append(
                    {
                        "label": gettext(
                            "Invalid password format or unknown hashing algorithm."
                        )
                    }
                )
            else:
                for key, value_ in hasher.safe_summary(value).items():
                    summary.append({"label": gettext(key), "value": value_})
        context["summary"] = summary
        return context

    def id_for_label(self, id_):
        return None


class ReadOnlyPasswordHashField(forms.Field):
    widget = ReadOnlyPasswordHashWidget

    def __init__(self, *args, **kwargs):
        kwargs.setdefault("required", False)
        kwargs.setdefault("disabled", True)
        super().__init__(*args, **kwargs)


class UsernameField(forms.CharField):
    def to_python(self, value):
        value = super().to_python(value)
        if self.max_length is not None and len(value) > self.max_length:
            
            
            
            
            
            return value
        return unicodedata.normalize("NFKC", value)

    def widget_attrs(self, widget):
        return {
            **super().widget_attrs(widget),
            "autocapitalize": "none",
            "autocomplete": "username",
        }


class BaseUserCreationForm(forms.ModelForm):
    

    error_messages = {
        "password_mismatch": _("The two password fields didn’t 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."),
    )

    class Meta:
        model = User
        fields = ("username",)
        field_classes = {"username": UsernameField}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self._meta.model.USERNAME_FIELD in self.fields:
            self.fields[self._meta.model.USERNAME_FIELD].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",
            )
        return password2

    def _post_clean(self):
        super()._post_clean()
        
        
        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)

    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()
        return user


class UserCreationForm(BaseUserCreationForm):
    def clean_username(self):
        
        username = self.cleaned_data.get("username")
        if (
            username
            and self._meta.model.objects.filter(username__iexact=username).exists()
        ):
            self._update_errors(
                ValidationError(
                    {
                        "username": self.instance.unique_error_message(
                            self._meta.model, ["username"]
                        )
                    }
                )
            )
        else:
            return username


class UserChangeForm(forms.ModelForm):
    password = ReadOnlyPasswordHashField(
        label=_("Password"),
        help_text=_(
            "Raw passwords are not stored, so there is no way to see this "
            "user’s password, but you can change the password using "
            '<a href="{}">this form</a>.'
        ),
    )

    class Meta:
        model = User
        fields = "__all__"
        field_classes = {"username": UsernameField}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        password = self.fields.get("password")
        if password:
            password.help_text = password.help_text.format(
                f"../../{self.instance.pk}/password/"
            )
        user_permissions = self.fields.get("user_permissions")
        if user_permissions:
            user_permissions.queryset = user_permissions.queryset.select_related(
                "content_type"
            )


class AuthenticationForm(forms.Form):
    

    username = UsernameField(widget=forms.TextInput(attrs={"autofocus": True}))
    password = forms.CharField(
        label=_("Password"),
        strip=False,
        widget=forms.PasswordInput(attrs={"autocomplete": "current-password"}),
    )

    error_messages = {
        "invalid_login": _(
            "Please enter a correct %(username)s and password. Note that both "
            "fields may be case-sensitive."
        ),
        "inactive": _("This account is inactive."),
    }

    def __init__(self, request=None, *args, **kwargs):
        
        self.request = request
        self.user_cache = None
        super().__init__(*args, **kwargs)

        
        self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
        username_max_length = self.username_field.max_length or 254
        self.fields["username"].max_length = username_max_length
        self.fields["username"].widget.attrs["maxlength"] = username_max_length
        if self.fields["username"].label is None:
            self.fields["username"].label = capfirst(self.username_field.verbose_name)

    def clean(self):
        username = self.cleaned_data.get("username")
        password = self.cleaned_data.get("password")

        if username is not None and password:
            self.user_cache = authenticate(
                self.request, username=username, password=password
            )
            if self.user_cache is None:
                raise self.get_invalid_login_error()
            else:
                self.confirm_login_allowed(self.user_cache)

        return self.cleaned_data

    def confirm_login_allowed(self, user):
        
        if not user.is_active:
            raise ValidationError(
                self.error_messages["inactive"],
                code="inactive",
            )

    def get_user(self):
        return self.user_cache

    def get_invalid_login_error(self):
        return ValidationError(
            self.error_messages["invalid_login"],
            code="invalid_login",
            params={"username": self.username_field.verbose_name},
        )


class PasswordResetForm(forms.Form):
    email = forms.EmailField(
        label=_("Email"),
        max_length=254,
        widget=forms.EmailInput(attrs={"autocomplete": "email"}),
    )

    def send_mail(
        self,
        subject_template_name,
        email_template_name,
        context,
        from_email,
        to_email,
        html_email_template_name=None,
    ):
        
        subject = loader.render_to_string(subject_template_name, context)
        
        subject = "".join(subject.splitlines())
        body = loader.render_to_string(email_template_name, context)

        email_message = EmailMultiAlternatives(subject, body, from_email, [to_email])
        if html_email_template_name is not None:
            html_email = loader.render_to_string(html_email_template_name, context)
            email_message.attach_alternative(html_email, "text/html")

        email_message.send()

    def get_users(self, email):
        
        email_field_name = UserModel.get_email_field_name()
        active_users = UserModel._default_manager.filter(
            **{
                "%s__iexact" % email_field_name: email,
                "is_active": True,
            }
        )
        return (
            u
            for u in active_users
            if u.has_usable_password()
            and _unicode_ci_compare(email, getattr(u, email_field_name))
        )

    def save(
        self,
        domain_override=None,
        subject_template_name="registration/password_reset_subject.txt",
        email_template_name="registration/password_reset_email.html",
        use_https=False,
        token_generator=default_token_generator,
        from_email=None,
        request=None,
        html_email_template_name=None,
        extra_email_context=None,
    ):
        
        email = self.cleaned_data["email"]
        if not domain_override:
            current_site = get_current_site(request)
            site_name = current_site.name
            domain = current_site.domain
        else:
            site_name = domain = domain_override
        email_field_name = UserModel.get_email_field_name()
        for user in self.get_users(email):
            user_email = getattr(user, email_field_name)
            context = {
                "email": user_email,
                "domain": domain,
                "site_name": site_name,
                "uid": urlsafe_base64_encode(force_bytes(user.pk)),
                "user": user,
                "token": token_generator.make_token(user),
                "protocol": "https" if use_https else "http",
                **(extra_email_context or {}),
            }
            self.send_mail(
                subject_template_name,
                email_template_name,
                context,
                from_email,
                user_email,
                html_email_template_name=html_email_template_name,
            )


class SetPasswordForm(forms.Form):
    

    error_messages = {
        "password_mismatch": _("The two password fields didn’t 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"}),
    )

    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 save(self, commit=True):
        password = self.cleaned_data["new_password1"]
        self.user.set_password(password)
        if commit:
            self.user.save()
        return self.user


class PasswordChangeForm(SetPasswordForm):
    

    error_messages = {
        **SetPasswordForm.error_messages,
        "password_incorrect": _(
            "Your old password was entered incorrectly. Please enter it again."
        ),
    }
    old_password = forms.CharField(
        label=_("Old password"),
        strip=False,
        widget=forms.PasswordInput(
            attrs={"autocomplete": "current-password", "autofocus": True}
        ),
    )

    field_order = ["old_password", "new_password1", "new_password2"]

    def clean_old_password(self):
        
        old_password = self.cleaned_data["old_password"]
        if not self.user.check_password(old_password):
            raise ValidationError(
                self.error_messages["password_incorrect"],
                code="password_incorrect",
            )
        return old_password


class AdminPasswordChangeForm(forms.Form):
    

    error_messages = {
        "password_mismatch": _("The two password fields didn’t 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."),
    )

    def __init__(self, user, *args, **kwargs):
        self.user = user
        super().__init__(*args, **kwargs)

    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 save(self, commit=True):
        
        password = self.cleaned_data["password1"]
        self.user.set_password(password)
        if commit:
            self.user.save()
        return self.user

    @property
    def changed_data(self):
        data = super().changed_data
        for name in self.fields:
            if name not in data:
                return []
        return ["password"]