Access permissions, seed permissions, security, session, api/session improved handling + speed across the site/UX improvements
This commit is contained in:
@@ -48,3 +48,41 @@ def verify_token(token: str) -> dict[str, Any]:
|
||||
if int(payload.get("exp", 0)) < int(time.time()):
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication token has expired")
|
||||
return payload
|
||||
|
||||
|
||||
# --- Password hashing ------------------------------------------------------
|
||||
# PBKDF2-SHA256 with a per-password 16-byte salt and 200k iterations. Stored
|
||||
# as `pbkdf2_sha256$iterations$salt_hex$hash_hex`. No external dep needed.
|
||||
|
||||
import os
|
||||
import secrets
|
||||
|
||||
_PBKDF2_ITERATIONS = 200_000
|
||||
_PBKDF2_ALGO = "pbkdf2_sha256"
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
if not password:
|
||||
raise ValueError("Password cannot be empty")
|
||||
salt = secrets.token_bytes(16)
|
||||
digest = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, _PBKDF2_ITERATIONS)
|
||||
return f"{_PBKDF2_ALGO}${_PBKDF2_ITERATIONS}${salt.hex()}${digest.hex()}"
|
||||
|
||||
|
||||
def verify_password(password: str, encoded: str | None) -> bool:
|
||||
if not encoded or not password:
|
||||
return False
|
||||
try:
|
||||
algo, iterations_str, salt_hex, digest_hex = encoded.split("$", 3)
|
||||
except ValueError:
|
||||
return False
|
||||
if algo != _PBKDF2_ALGO:
|
||||
return False
|
||||
try:
|
||||
iterations = int(iterations_str)
|
||||
salt = bytes.fromhex(salt_hex)
|
||||
expected = bytes.fromhex(digest_hex)
|
||||
except ValueError:
|
||||
return False
|
||||
candidate = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, iterations)
|
||||
return hmac.compare_digest(candidate, expected)
|
||||
|
||||
Reference in New Issue
Block a user