feat: session mode parsing, memory-only cookies, single-user mode
- COOKIE_SECURE env now accepts: 'true' (secure), 'false' (memory-only), 'single_user' (no cookie) - config.parse_cookie_mode() returns (COOKIE_SECURE, SINGLE_USER) tuple for routing - single_user_owner() returns oldest Owner for test/local deployments - session cookie respects mode: Max-Age only in secure mode, dropped for memory-only - base.html renders ephemeral-session and single-user banners per mode - Tests: comprehensive coverage for all three modes with monkeypatch configs - CLAUDE.md + README docs updated Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -115,7 +115,7 @@ on narrow screens or in the add-printer sidebar (`.form-aside`).
|
||||
|-----|---------|---------|
|
||||
| `DATA_DIR` | `/data` | Storage root (DB + drivers + icons) |
|
||||
| `PORT` | `8000` | Server port |
|
||||
| `COOKIE_SECURE` | `true` | Owner-session cookie `Secure` flag. Set `false` for plain-HTTP serving or the browser drops the cookie and a new Owner is created on every request. `false` also makes the cookie **memory-only** (no `Max-Age`) — see below. |
|
||||
| `COOKIE_SECURE` | `true` | Three-way session mode, parsed by `config.parse_cookie_mode()` into `(COOKIE_SECURE, SINGLE_USER)`: `true` = Secure + 10-year cookie; `false` = plain-HTTP serving (browser drops a Secure cookie → new Owner per request), cookie becomes **memory-only** (no `Max-Age`); `single_user` (or `single-user`/`single`) = no cookie at all, one shared Owner — see below. |
|
||||
|
||||
`COOKIE_SECURE=false` degrades the session instead of weakening the credential:
|
||||
`services/session.cookie_kwargs()` drops `max_age`, so the browser holds the
|
||||
@@ -125,4 +125,18 @@ cookie-setting call sites (the middleware and `POST /session/restore`) must go
|
||||
through `cookie_kwargs()`. `request.state.ephemeral_session` mirrors the flag,
|
||||
and `base.html` renders the `#ephemeral-session-warning` banner plus an extra
|
||||
paragraph in the onboarding modal off it. Changing this touches
|
||||
`tests/test_session.py::test_insecure_mode_*` / `test_secure_mode_*`.
|
||||
`tests/test_session.py::test_insecure_mode_*` / `test_secure_mode_*`.
|
||||
|
||||
`COOKIE_SECURE=single_user` (`cfg.SINGLE_USER`) removes sessions for test boxes
|
||||
and single-person local prod: the middleware never reads or sets a cookie and
|
||||
returns `services/session.single_user_owner()` — the **oldest** `Owner` row,
|
||||
created on demand — so a deployment switched over from cookie mode keeps the
|
||||
printers it already had and no second row is ever minted. `request.state.owner`
|
||||
is still what every route filters on, so per-owner query code is unchanged.
|
||||
`request.state.single_user` gates the sidebar "This session" menu and the
|
||||
`#single-user-notice` banner in `base.html`; `is_new_owner`/`ephemeral_session`
|
||||
are forced `False` (no onboarding modal, no memory-only warning). All three
|
||||
`/session/*` routes 404 via `api/session._require_cookie_sessions()` — a key
|
||||
can't re-point a cookie that isn't read, and downloading one would leak the
|
||||
shared owner's bearer credential for a later switch back to cookie mode. Tests:
|
||||
`test_session.py::test_single_user_*` + `test_parse_cookie_mode_*`.
|
||||
Reference in New Issue
Block a user