Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

custom password validation in django 1.7

I am trying to add custom password validation to the create user and change password admin forms. I did not see anything in the django docs about how to do this. I found this post on SO: Enforcing password strength requirements with django.contrib.auth.views.password_change, which gives 2 solutions. I tried both, but neither worked for me.

Here's what I have now in my apps admin.py:

def validate_password_strength(value):
    """Validates that a password is as least 10 characters long and has at least
    2 digits and 1 Upper case letter.
    """
    min_length = 10

    if len(value) < min_length:
        raise ValidationError(_('Password must be at least {0} characters '
                                'long.').format(min_length))

    # check for 2 digits
    if sum(c.isdigit() for c in value) < 2:
        raise ValidationError(_('Password must container at least 2 digits.'))

    # check for uppercase letter
    if not any(c.isupper() for c in value):
        raise ValidationError(_('Password must container at least 1 uppercase letter.'))

class MySetPasswordForm(SetPasswordForm):
    def __init__(self, *args, **kwargs):
        super(MySetPasswordForm, self).__init__(*args, **kwargs)
        self.fields['password1'].validators.append(validate_password_strength)

But what I can't figure out is how do I get MySetPasswordForm to be used.

I've tried a few different things. First I did this:

class UserAdmin(UserAdmin):
    form = MySetPasswordForm

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

And I got this error:

<class 'elex_apis.energy.webservice.admin.UserAdmin'>: (admin.E016) The value of 'form' must inherit from 'BaseModelForm'.

So then I changed MySetPasswordForm to inherit from BaseModelForm and then I got this error:

__init__() missing 1 required positional argument: 'user'

So then I added the user param so now MySetPasswordForm looked like this:

class MySetPasswordForm(BaseModelForm):
    def __init__(self, user, *args, **kwargs):
        super(MySetPasswordForm, self).__init__(user, *args, **kwargs)
        self.fields['password1'].validators.append(validate_password_strength)

But I still got the same error as before.

I can't believe it's this hard to add a password validator. It must be a very common need, so clearly I must be missing something simple. Can anyone please provide some assistance here.

like image 437
Larry Martell Avatar asked Apr 10 '15 14:04

Larry Martell


People also ask

How do I create a custom password validator in Django?

How To Create Your Own Django Password Validator. If you have more specific needs, you can create your own validators. To do so, simply create your own classes based on object and raise a ValidationError if the entered password fails. class NumberValidator(object): def validate(self, password, user=None): if not re.

How does Django validate password?

validate(self, password, user=None) : validate a password. Return None if the password is valid, or raise a ValidationError with an error message if the password is not valid. You must be able to deal with user being None - if that means your validator can't run, return None for no error.

Is there a password field in Django?

The Django's Forms The above form has two inputs - a text field named username (the name attribute in the html input field is what determines the name of input field) and a password field named password - and a submit button. The form uses POST method to submit form data to server.


1 Answers

The easiest way is to inherit the original UserAdmin and just override the change_password_form.

Example:

from django.contrib.auth import models as auth_models
from django.contrib.auth import admin as auth_admin
from django.contrib.auth import forms as auth_forms
from django.core.exceptions import ValidationError


def validate_password_strength(value):
    """Validates that a password is as least 10 characters long and has at least
    2 digits and 1 Upper case letter.
    """
    min_length = 10

    if len(value) < min_length:
        raise ValidationError(_('Password must be at least {0} characters '
                                'long.').format(min_length))

    # check for 2 digits
    if sum(c.isdigit() for c in value) < 2:
        raise ValidationError(_('Password must container at least 2 digits.'))

    # check for uppercase letter
    if not any(c.isupper() for c in value):
        raise ValidationError(_('Password must container at least 1 uppercase letter.'))

    return value


class AdminPasswordChangeForm(auth_forms.AdminPasswordChangeForm):
    def clean_password1(self):
        return validate_password_strength(self.cleaned_data['password1'])


class UserCreationForm(auth_forms.UserCreationForm):
    def clean_password1(self):
        return validate_password_strength(self.cleaned_data['password1'])


class UserAdmin(auth_admin.UserAdmin):
    change_password_form = AdminPasswordChangeForm
    add_form = UserCreationForm


# Re-register UserAdmin
admin.site.unregister(auth_models.User)
admin.site.register(auth_models.User, UserAdmin)
like image 59
Wolph Avatar answered Oct 17 '22 15:10

Wolph