Reputation: 67
To avoid using an if-else loop, I did the following for adding password validation in Pydantic.
@field_validator("password")
def check_password(cls, value):
# Convert the password to a string if it is not already
value = str(value)
# Check that the password meets the criteria
if len(value) < 8:
raise ValueError("Password must have at least 8 characters")
if not any(c.isupper() and c.islower() and c.isdigit() and c in string.punctuation for c in value):
raise ValueError("Password must have at least one uppercase letter, one lowercase letter, and one digit")
return value
But unfortunately that if not any
condition is not working correctly. How can I fix it?
Upvotes: 1
Views: 1380
Reputation: 321
Just an extension.I hope it helps.
#Password policy
SPECIAL_CHARS: set[str] = {
"$",
"@",
"#",
"%",
"!",
"^",
"&",
"*",
"(",
")",
"-",
"_",
"+",
"=",
"{",
"}",
"[",
"]",
}
MIN_LENGTH: int = 5
MAX_LENGTH: int = 20
INCLUDES_SPECIAL_CHARS: bool = True
INCLUDES_NUMBERS: bool = True
INCLUDES_LOWERCASE: bool = True
INCLUDES_UPPERCASE: bool = True
def validate_password(v: SecretStr) -> SecretStr:
min_length = MIN_LENGTH
max_length = MAX_LENGTH
includes_special_chars = INCLUDES_SPECIAL_CHARS
includes_numbers = INCLUDES_NUMBERS
includes_lowercase = INCLUDES_LOWERCASE
includes_uppercase = INCLUDES_UPPERCASE
special_chars = SPECIAL_CHARS
if not isinstance(v.get_secret_value(), str):
raise TypeError("string required")
if len(v.get_secret_value()) < min_length or len(v.get_secret_value()) > max_length:
raise ValueError(f"length should be at least {min_length} but not more than 20")
if includes_numbers and not any(char.isdigit() for char in v.get_secret_value()):
raise ValueError("Password should have at least one numeral")
if includes_uppercase and not any(char.isupper() for char in v.get_secret_value()):
raise ValueError("Password should have at least one uppercase letter")
if includes_lowercase and not any(char.islower() for char in v.get_secret_value()):
raise ValueError("Password should have at least one lowercase letter")
if includes_special_chars and not any(
char in special_chars for char in v.get_secret_value()
):
raise ValueError(
f"Password should have at least one of the symbols {special_chars}"
)
return v
ValidatePassword = Annotated[SecretStr, AfterValidator(validate_password)]
class CreateUserRequest(BaseModel):
password: ValidatePassword
Upvotes: 0
Reputation: 11297
There is no character that is simultaneously upper, lower, a digit, and a punctuation.
You need:
if not any(c.isupper() for c in value) or \
not any(c.islower() for c in value) or \
not any(c.isdigit() for c in value) or \
not any(c in punctionation for c in value):
... handle bad password ...
Upvotes: 1