Skip to content

Instantly share code, notes, and snippets.

@wonderbeyond
Last active February 17, 2023 07:37
Show Gist options
  • Save wonderbeyond/72062d8075db502fc4241b567834daa3 to your computer and use it in GitHub Desktop.
Save wonderbeyond/72062d8075db502fc4241b567834daa3 to your computer and use it in GitHub Desktop.
password validation
import re
PASSWORD_MIN_LENGTH = 8
PASSWORD_MAX_LENGTH = 128
PASSWORD_MIN_UNIQUE_CHARS = 5
PASSWORD_MAX_REPEATED_CHARS = 3
MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES = 3
class PasswordValidationError(Exception):
pass
def validate_password(raw_password: str) -> bool:
# sourcery skip: low-code-quality
if len(raw_password) < PASSWORD_MIN_LENGTH:
raise PasswordValidationError(f'Password must be at least {PASSWORD_MIN_LENGTH} characters long.')
if len(raw_password) > PASSWORD_MAX_LENGTH:
raise PasswordValidationError(f'Password must be at most {PASSWORD_MAX_LENGTH} characters long.')
uniq_chars = set(raw_password)
if len(uniq_chars) < PASSWORD_MIN_UNIQUE_CHARS:
raise PasswordValidationError(f'Password must contain at least {PASSWORD_MIN_UNIQUE_CHARS} different characters.')
# If there are too many repeated chars
for c in uniq_chars:
repetition = c * PASSWORD_MAX_REPEATED_CHARS
if repetition in raw_password:
raise PasswordValidationError(
f'Password cannot contain more than {PASSWORD_MAX_REPEATED_CHARS} consecutive repeated characters.'
f' (notice [red]{repetition}[/red])'
)
# If there are continuous increasing or decreasing sequences
incr_decr_seqs = set()
for c in uniq_chars:
n = ord(c)
if (
c.isdigit() and n + MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES - 1 <= ord('9')
or c.isupper() and n + MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES - 1 <= ord('Z')
or c.islower() and n + MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES - 1 <= ord('z')
):
if (seq := ''.join(chr(n + i) for i in range(MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES))) in raw_password:
incr_decr_seqs.add(seq)
if (
c.isdigit() and n - MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES + 1 >= ord('0')
or c.isupper() and n - MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES + 1 >= ord('A')
or c.islower() and n - MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES + 1 >= ord('a')
):
if (seq := ''.join(chr(n - i) for i in range(MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES))) in raw_password:
incr_decr_seqs.add(seq)
if incr_decr_seqs:
raise PasswordValidationError(
f'Password cannot contain more than {MAX_CONTINUOUS_INCREASING_OR_DECREASING_SEQUENCES} '
f'consecutive increasing or decreasing characters. (notice [red]{", ".join(incr_decr_seqs)}[/red])'
)
contains_lower_letters = re.search(r'[a-z]', raw_password)
contains_upper_letters = re.search(r'[A-Z]', raw_password)
contains_digits = re.search(r'[0-9]', raw_password)
contains_special_chars = re.search(r'[^a-zA-Z0-9]', raw_password)
if len([fct for fct in [
contains_lower_letters,
contains_upper_letters,
contains_digits,
contains_special_chars
] if fct]) < 3:
raise PasswordValidationError(
'Password must contain at least 3 of the following: '
'1. lowercase letters, 2. uppercase letters, 3. digits, '
'4. special characters.'
)
return True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment