Created
May 9, 2025 16:02
-
-
Save DArmstrong87/ab13d90d1fd1f939a9a8752004b07af1 to your computer and use it in GitHub Desktop.
Password Validators
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
# Password validation | |
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators | |
# Your password can’t be too similar to your other personal information. | |
# Your password must contain at least 10 characters. | |
# Your password can’t be a commonly used password. | |
# Your password can’t be entirely numeric. | |
# Your password must contain at least 1 symbol: ()[]{}|\`~!@#$%^&*_-+=;:'",<>./? | |
# Your password must contain at least 1 number, 0-9. | |
# Your password cannot be expired | |
# Your new password cannot be a previous password. | |
Set this in your settings.py: | |
AUTH_PASSWORD_VALIDATORS = [ | |
{ | |
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", | |
}, | |
{ | |
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", | |
"OPTIONS": { | |
"min_length": 10, | |
}, | |
}, | |
{ | |
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", | |
}, | |
{ | |
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", | |
}, | |
{ | |
"NAME": "core.validators.SymbolValidator", | |
}, | |
{ | |
"NAME": "core.validators.NumberValidator", | |
}, | |
{ | |
"NAME": "core.validators.PreviousPasswordsValidator", | |
}, | |
] | |
""" | |
import re | |
from django.core.exceptions import ValidationError | |
from django.contrib.auth.hashers import check_password | |
from django.utils.translation import gettext_lazy as _ | |
from authentication.models import UserPasswordLog | |
class SymbolValidator: | |
""" Validates presence of symbol in password """ | |
def __init__(self) -> None: | |
return | |
def validate(self, password, user=None): | |
if not re.findall('[()[\]{}|\\`~!@#$%^&*_\-+=;:\'",<>./?]', password): | |
raise ValidationError( | |
_("The password must contain at least 1 symbol: " + | |
"()[]{}|\`~!@#$%^&*_-+=;:'\",<>./?"), | |
code='password_no_symbol', | |
) | |
def get_help_text(self): | |
return _( | |
"Your password must contain at least 1 symbol: " + | |
"()[]{}|\`~!@#$%^&*_-+=;:'\",<>./?" | |
) | |
class NumberValidator: | |
""" Validates presence of number in password """ | |
def validate(self, password, user=None): | |
if not re.findall('\d', password): | |
raise ValidationError( | |
_("Your password must contain at least 1 number, 0-9."), | |
code='password_no_number', | |
) | |
def get_help_text(self): | |
return _( | |
"Your password must contain at least 1 number, 0-9." | |
) | |
class PreviousPasswordsValidator: | |
""" Validates password is not a previously used one """ | |
def validate(self, password, user): | |
limit = 4 | |
if limit is not None and limit != 0: | |
previous_passwords = [] | |
last_used = UserPasswordLog.objects.filter(user=user).order_by('-created')[:limit] | |
# Delete old password logs | |
UserPasswordLog.objects.filter(user=user).exclude(pk__in=last_used).delete() | |
for password_log in last_used: | |
previous_passwords.append(password_log.password) | |
for previous_password in previous_passwords: | |
match = check_password(password, previous_password) | |
if match: | |
raise ValidationError( | |
_(f"You cannot reuse one of your previous {limit} passwords."), | |
code='password_previously_used', | |
) | |
def get_help_text(self): | |
return _( | |
"You cannot reuse a previously used password." | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment