- operator_users router now depends on _require_auth_enabled, which raises
404 when OPERATOR_AUTH_ENABLED is false — prevents world-open pre-seeding
of a superadmin while the flag is off (the default). Flag is read as a
live module attribute (operator_auth.OPERATOR_AUTH_ENABLED) so monkeypatching
in tests and a runtime flip both take effect.
- operator_gate passes OPTIONS requests through immediately before the exempt-
path check, so CORS preflight reaches CORSMiddleware rather than being
303/401'd by the gate.
- Two new tests: test_admin_surface_404s_when_flag_off (test_operator_users)
and test_options_preflight_passes_through_gate (test_operator_gate).
Full suite: 90 passed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds operator_gate Starlette HTTP middleware that gates every route
except an explicit allow-list. Flag defaults OFF so all existing
behaviour and tests are unchanged. wire_operator_auth helper in
conftest lets tests monkeypatch the module-global SessionLocal and
flag, keeping the gate's own DB session pointed at the test engine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add OperatorUser SQLAlchemy model (operator_users table, auto-created by
create_all) with email uniqueness, default active/must_change_password/
failed_login_count, and sessions_valid_from truncated to whole seconds.
Add backend/operator_auth.py with feature flag, cookie constants, _ROLE_RANK
map, role_at_least(), and _norm_email() helpers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>