hub v0.54.0: change operator login password from the Configuration UI

Adds a "Login password" card on /configuration. The password was previously
settable only via the hub-config ConfigMap (auth.password_hash) + redeploy.

- store: hub_settings key operator_password_hash + Get/SetOperatorPasswordHash
- server: passwordHash field -> configPasswordHash (seed); new
  effectivePasswordHash() (DB override wins, else seed) is now the single
  source for the CSRF gate, RequireAuth, and handleLogin
- POST /configuration/password (handleChangePassword): requires current
  password, 8-72 byte new + confirm, bcrypt cost 10, persists DB override;
  existing sessions kept valid; ConfigMap stays the break-glass reset path
- UI: current/new/confirm form + inline mismatch pre-check + 6 flashes
- tests + red-proofs: override precedence, happy-path via handleLogin,
  wrong-current rejection, mismatch/too-short/no-op, template render
- docs: CHANGELOG, README (auth+config), REUSE, REPORT

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbMm4T7Ayzs1unB9pN6Uqd
@
This commit is contained in:
2026-07-13 22:46:49 +02:00
parent ed271fadb2
commit a1d045079f
8 changed files with 440 additions and 32 deletions
+27
View File
@@ -1,5 +1,32 @@
# Felhom Hub — Changelog
## v0.54.0 — operator login password changeable from the UI (2026-07-13)
The hub login password was previously settable ONLY by editing the `auth.password_hash` field in
the `hub-config` ConfigMap and redeploying — there was no in-app way to change it. Added a
**"Login password"** card on the Configuration page.
- **DB-override precedence** (same pattern as the controller-version floor). New `hub_settings` key
`operator_password_hash` (store: `Get/SetOperatorPasswordHash`). The web server no longer reads a
static field for auth: `Server.passwordHash` is renamed `configPasswordHash` (the hub.yaml SEED)
and every auth check — the CSRF gate, `RequireAuth` session/basic-auth paths, and `handleLogin`
now goes through `effectivePasswordHash()` = **DB override wins, else config seed**. The ConfigMap
value stays the **break-glass fallback**: blank the DB row (or edit the manifest + redeploy) to
reset a lost password.
- **`POST /configuration/password`** (`handleChangePassword`): requires the **current** password
(verified against the effective hash), a new password of 872 bytes, and a matching confirmation;
rejects a no-op change. On success it bcrypts the new password (cost 10, matching the seed) and
persists the override. Existing sessions are intentionally kept valid — only the next sign-in and
Basic-Auth use the new hash. CSRF-enforced (existing `ServeHTTP` gate); no secret is ever logged.
- **UI**: change-password card on `configuration.html` with current/new/confirm fields, inline
client-side mismatch pre-check, and six flash outcomes (`pw_changed`, `pw_current_wrong`,
`pw_too_short`, `pw_too_long`, `pw_mismatch`, `pw_unchanged`).
- **Tests + red-proofs** (`change_password_test.go`): override-wins precedence, happy-path
end-to-end through `handleLogin` (new works, old dead), wrong-current rejection (security anchor),
mismatch/too-short/no-op rejections, and template render. Red-proofs verified — dropping the
current-password check writes the override anyway (WrongCurrentRejected fails); breaking the
override precedence kills both the precedence and happy-path login assertions.
## (unreleased) hostInstallVersion 1.16.0 (2026-07-13)
Display-const bump only, keeping `scripts/hostinstall_gates.py` green with the installer's