3

In Django I created a custom User.

My issues is that the default validators are not working, any password is accepted.

I expected that the default password validators will work, because I'm inheriting the default Model/Form clases.

In Django settings I have:

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {
            'min_length': 12,
        }
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },

]

The Custom User implementation:

class User(AbstractBaseUser, MetaData, PermissionsMixin):

    account = models.ForeignKey(Account, blank=True, null=True, related_name='owner', on_delete=models.CASCADE)
    email = models.EmailField(max_length=255, unique=True)
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    is_confirmed = models.BooleanField(default=False)  # email confirmed
    is_active = models.BooleanField(default=False)  # can't login
    is_staff = models.BooleanField(default=False)  # staff user non super
    is_superuser = models.BooleanField(default=False)  # superuser

    USERNAME_FIELD = 'email'  # username used for login
    REQUIRED_FIELDS = ['first_name', 'last_name']

    objects = UserManager()

class UserManager(BaseUserManager):

    def create_user(self, email, first_name, last_name, password=None, is_confirmed=False, is_active=False,
                    is_staff=False, is_superuser=False):
        if not email:
            raise ValueError('User must have an Email Address')
        if not password:
            raise ValueError('User must have a Password set')
        if not first_name:
            raise ValueError('User must have a First Name')
        if not last_name:
            raise ValueError('User must have a Last Name')
        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
        )
        user.set_password(password)
        user.is_confirmed = is_confirmed
        user.is_active = is_active
        user.is_staff = is_staff
        user.is_superuser = is_superuser
        user.save(using=self.db)

        return user

    def create_staffuser(self, email, first_name, last_name, password=None):

        user = self.create_user(email, first_name, last_name,  password=password, is_confirmed=True,
                                is_staff=True)
        return user

    def create_superuser(self, email, first_name, last_name, password=None):

        user = self.create_user(email, first_name, last_name, password=password, is_confirmed=True,
                                is_active=True, is_staff=True, is_superuser=True)
        return user

and the Form:

class UserBaseCreationForm(forms.ModelForm):
    """ A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(widget=PasswordWidget(attrs={'placeholder': 'Password*'}))
    password2 = forms.CharField(widget=PasswordWidget(attrs={'placeholder': 'Confirm Password*'}))

    class Meta:
        model = User
        fields = ['account', 'email', 'first_name', 'last_name']

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super().save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user
J Mo
  • 43
  • 6

2 Answers2

5

The password is not validated because you are not calling any validator in your UserBaseCreationForm. You can do this in different ways and the easiest one would be just inheriting UserCreationForm from django.contrib.auth.forms.

Check the django source code, it calls the password validators in _post_clean.

zeynel
  • 943
  • 1
  • 8
  • 13
0

According to Django Docs, Django uses a validate_password method from django.contrib.auth.password_validation.validate_password to validate the password.

Since you are using a Custom UserCreationForm, You need to call that validate_password method explicitly, by implementing a form validation through password field clean method.

Simply add following code snippet in your UserBaseCreationForm class.

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