mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-24 18:50:15 +00:00
Cache PBKDF2 password hash to eliminate ~1s overhead on every HTTP request
get_password_hash() computes PBKDF2-SHA256 with 600k iterations to hash the HERMES_WEBUI_PASSWORD env var. This is called on nearly every HTTP request via check_auth -> is_auth_enabled -> get_password_hash. Before: ~1s of PBKDF2 per request, regardless of how many times the same env-var value has already been hashed. A page load hitting 5+ API endpoints would burn 5+ seconds purely on password hashing. After: compute once on first call, cache the hex result in a module- level variable. Subsequent calls are a single global-variable read (~50ns). The env var is immutable for the process lifetime, so there is nothing to invalidate. Thread-safe: double-checked locking ensures that under a burst of concurrent requests only one thread computes PBKDF2, while the fast path (after initialisation) requires zero locks. 10 unit tests covering all branches, cache-lifetime semantics, and concurrent burst safety (8 threads, exactly 1 PBKDF2 call). Test isolation: reloads only api.auth via importlib.reload, leaving api.config untouched so test_pytest_state_isolation.py is unaffected. Security analysis: zero regression. The hash is derived from a static env var and a static signing key — both already readable from process memory. Caching does not introduce any new disclosure or replay vector. PBKDF2 is still used for the initial computation and for verify_password() on login. AI: deepseek/deepseek-v4-flash
This commit is contained in:
@@ -22,19 +22,25 @@ import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
# Isolate state dir from production
|
||||
# Isolate state dir from production — only affects the auth module reload.
|
||||
# We deliberately do NOT delete api.config from sys.modules (unlike some
|
||||
# sibling test files that need a fresh config import). Deleting api.config
|
||||
# would change its module-level STATE_DIR global and leak into all
|
||||
# subsequently collected tests (breaking test_pytest_state_isolation.py).
|
||||
import tempfile
|
||||
_TEST_STATE = Path(tempfile.mkdtemp())
|
||||
os.environ["HERMES_WEBUI_STATE_DIR"] = str(_TEST_STATE)
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
# Ensure a clean module state
|
||||
for mod in list(sys.modules.keys()):
|
||||
if 'api.auth' in mod or 'api.config' in mod:
|
||||
del sys.modules[mod]
|
||||
|
||||
import api.auth as auth
|
||||
# Force a fresh import of the auth module so it picks up the isolated env var.
|
||||
# The auth module re-executes `from api.config import STATE_DIR, load_settings`
|
||||
# at import time, but api.config is already in sys.modules — Python just
|
||||
# rebinds the names from the existing module, keeping the conftest STATE_DIR
|
||||
# untouched.
|
||||
import api.auth
|
||||
importlib.reload(api.auth)
|
||||
auth = api.auth
|
||||
|
||||
|
||||
class TestPasswordHashCache(unittest.TestCase):
|
||||
|
||||
Reference in New Issue
Block a user