Coverage for src / mysingle / auth / security / password.py: 85%
20 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-01 23:57 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-01 23:57 +0900
1import secrets
2import string
4from pwdlib import PasswordHash
5from pwdlib.hashers.argon2 import Argon2Hasher
6from pwdlib.hashers.bcrypt import BcryptHasher
9class PasswordHelper:
10 def __init__(self, password_hash: PasswordHash | None = None) -> None:
11 if password_hash is None:
12 self.password_hash = PasswordHash(
13 (
14 Argon2Hasher(),
15 BcryptHasher(),
16 )
17 )
18 else:
19 self.password_hash = password_hash # pragma: no cover
21 def verify_and_update(
22 self, plain_password: str, hashed_password: str
23 ) -> tuple[bool, str | None]:
24 return self.password_hash.verify_and_update(plain_password, hashed_password)
26 def hash(self, password: str) -> str:
27 return self.password_hash.hash(password)
29 def generate(self) -> str:
30 return secrets.token_urlsafe()
32 def generate_secure_password(self, length: int = 12) -> str:
33 characters = string.ascii_letters + string.digits + string.punctuation
34 password = "".join(secrets.choice(characters) for _ in range(length))
35 return password
38password_helper = PasswordHelper()