Django - Validate password with AUTH_PASSWORD_VALIDATORS

Viewed 8187

I have a custom User model that only contains two mandatory fields: email and password.

I also have a custom UserCreationForm that prompts users for their email and one password.

Unfortunately, the form doesn't validate the password, aside from min_length.

How do I enable the password validators in settings.AUTH_PASSWORD_VALIDATORS? The object is a list of distc, not Validators, so I'm not sure how to use them.

class UserCreationForm(forms.ModelForm):
    password1 = forms.CharField(
        widget=forms.PasswordInput(attrs=form_attrs.password),
        min_length=8,
        strip=True,
    )

    class Meta:
        model = User
        fields = ('email',)
        widgets = {
            'email': forms.EmailInput(attrs=form_attrs.email),
        }

    def save(self, commit=True):
        user = super().save(commit=False)
        user.set_password(self.cleaned_data.get('password1'))
        if commit:
            user.save()
        return user
2 Answers

As another answerer mentioned, Django uses the django.contrib.auth.password_validation.validate_password method to validate the password. You can create a clean_password1 method and add this to it, like so:

class UserCreationForm(forms.ModelForm):
    password1 = forms.CharField(
        widget=forms.PasswordInput(attrs=form_attrs.password),
        min_length=8,
        strip=True,
    )

    class Meta:
        model = User
        fields = ('email',)
        widgets = {
            'email': forms.EmailInput(attrs=form_attrs.email),
        }

    def clean_password1(self):
        password1 = self.cleaned_data.get('password1')
        try:
            password_validation.validate_password(password1, self.instance)
        except forms.ValidationError as error:

            # Method inherited from BaseForm
            self.add_error('password1', error)
        return password1

The way django applies to it's built-in forms is

def get_password_validators(validator_config):
    validators = []
    for validator in validator_config:
        try:
            klass = import_string(validator['NAME'])
        except ImportError:
            msg = "The module in NAME could not be imported: %s. Check your AUTH_PASSWORD_VALIDATORS setting."
            raise ImproperlyConfigured(msg % validator['NAME'])
        validators.append(klass(**validator.get('OPTIONS', {})))

    return validators

this and after this, the validation is done inside the form manually.

This means you can import password_validation from django.contrib.auth and use

@functools.lru_cache(maxsize=None)
def get_default_password_validators():
    return get_password_validators(settings.AUTH_PASSWORD_VALIDATORS)

this function to get the validators and inside your form

class UserCreationForm(forms.ModelForm):
    password1 = forms.CharField(
        widget=forms.PasswordInput(attrs=form_attrs.password),
        min_length=8,
        strip=True,
    )
    def clean_password1(self):
        # Validate user password
        # self.cleaned_data['password1'] has the password put into the field
        # if the validation fails
        # raise forms.ValidationError with the args and kwargs of your 
        # validators' error messages.

You can take a look at how the built-in registration form do it.

Related