Spaces:
Runtime error
Runtime error
| """Admin account management -- password hashing/verification lives here, | |
| kept separate from database.py so the bcrypt dependency and comparison | |
| logic are in one obvious place rather than scattered across callers. | |
| Passwords are never compared in SQL. verify_admin_email (the Supabase RPC) | |
| only returns the stored bcrypt hash for a given email; the actual | |
| credential check happens here, in Python, with bcrypt.checkpw -- a | |
| constant-time comparison against the hash, same security property | |
| hmac.compare_digest gave the old single ADMIN_PASSWORD check. | |
| """ | |
| import logging | |
| from typing import Optional | |
| import bcrypt | |
| import database | |
| logger = logging.getLogger(__name__) | |
| _BCRYPT_ROUNDS = 12 | |
| def hash_password(plaintext: str) -> str: | |
| return bcrypt.hashpw(plaintext.encode("utf-8"), bcrypt.gensalt(_BCRYPT_ROUNDS)).decode("utf-8") | |
| def verify_admin_credentials(email: str, password: str) -> Optional[dict]: | |
| """Look up an admin by email and check the password against the stored | |
| bcrypt hash. Returns {admin_id, admin_name} on success, None on any | |
| mismatch (wrong email OR wrong password -- deliberately not | |
| distinguished, so a login failure never reveals whether the email | |
| exists).""" | |
| row = database.fetch_admin_by_email(email) | |
| if not row: | |
| # Still run a bcrypt check against a dummy hash so a nonexistent | |
| # email doesn't return measurably faster than a wrong password for | |
| # a real one (timing side-channel on account enumeration). | |
| bcrypt.checkpw(b"", bcrypt.gensalt(_BCRYPT_ROUNDS)) | |
| return None | |
| try: | |
| ok = bcrypt.checkpw(password.encode("utf-8"), row["password_hash"].encode("utf-8")) | |
| except (ValueError, TypeError) as exc: | |
| logger.error("Admin password hash for %s is malformed: %s", email, exc) | |
| return None | |
| if not ok: | |
| return None | |
| return {"admin_id": row["admin_id"], "admin_name": row["admin_name"]} | |