Access permissions, seed permissions, security, session, api/session improved handling + speed across the site/UX improvements

This commit is contained in:
2026-05-08 00:00:56 +12:00
parent ebee72d4df
commit 1533b5aa9b
29 changed files with 1851 additions and 520 deletions
+38
View File
@@ -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)