Password validation with pydantic

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

Answers (2)

elyte5star
elyte5star

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

Frank Yellin
Frank Yellin

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

Related Questions