Imports to top of file (ruff rule now). Refactor a couple of import cycles by implementing clear hierarchy of modules.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import hmac
|
||||
import re
|
||||
from typing import Protocol
|
||||
from unicodedata import normalize
|
||||
|
||||
import argon2
|
||||
|
||||
_argon = argon2.PasswordHasher()
|
||||
_droppyhash = re.compile(r"^([a-f0-9]{64})\$([a-f0-9]{8})$")
|
||||
|
||||
|
||||
class SupportsHash(Protocol):
|
||||
hash: str
|
||||
|
||||
|
||||
def normalize_secret(value: str) -> bytes:
|
||||
return normalize("NFC", value).strip().encode()
|
||||
|
||||
|
||||
def verify_hash(user_hash: str, *, username: str, password: str) -> bool:
|
||||
"""Verify password hash and return whether the stored hash should be upgraded."""
|
||||
if not user_hash:
|
||||
raise ValueError("Account disabled")
|
||||
|
||||
normalized_username = normalize_secret(username)
|
||||
normalized_password = normalize_secret(password)
|
||||
|
||||
if (match := _droppyhash.match(user_hash)) is not None:
|
||||
expected_hash, salt = match.groups()
|
||||
computed_hash = hmac.digest(
|
||||
normalized_password + salt.encode() + normalized_username,
|
||||
b"",
|
||||
"sha256",
|
||||
).hex()
|
||||
if not hmac.compare_digest(expected_hash, computed_hash):
|
||||
raise ValueError("Invalid password")
|
||||
return True
|
||||
|
||||
try:
|
||||
_argon.verify(user_hash, normalized_password)
|
||||
except Exception:
|
||||
raise ValueError("Invalid password") from None
|
||||
return _argon.check_needs_rehash(user_hash)
|
||||
|
||||
|
||||
def set_password(user: SupportsHash, password: str) -> None:
|
||||
user.hash = _argon.hash(normalize_secret(password))
|
||||
Reference in New Issue
Block a user