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.
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)