Skip to content

Instantly share code, notes, and snippets.

@DArmstrong87
Created May 9, 2025 16:02
Show Gist options
  • Save DArmstrong87/ab13d90d1fd1f939a9a8752004b07af1 to your computer and use it in GitHub Desktop.
Save DArmstrong87/ab13d90d1fd1f939a9a8752004b07af1 to your computer and use it in GitHub Desktop.
Password Validators
"""
# 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