Last active
February 17, 2023 07:37
-
-
Save wonderbeyond/72062d8075db502fc4241b567834daa3 to your computer and use it in GitHub Desktop.
password validation
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
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