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:
@@ -45,6 +45,21 @@ def cookie_kwargs() -> dict:
|
||||
return kwargs
|
||||
|
||||
|
||||
def single_user_owner() -> Owner:
|
||||
"""The one Owner every request shares under `COOKIE_SECURE=single_user`.
|
||||
|
||||
No cookie is read, so the owner has to be findable from the DB alone: the
|
||||
oldest row wins. That way a deployment that used to run cookie-scoped keeps
|
||||
the printers it already had instead of waking up empty, and repeat requests
|
||||
never mint a second row. It is marked permanent so nothing treats it as a
|
||||
throwaway first-visit session.
|
||||
"""
|
||||
owner = Owner.select().order_by(Owner.id).first()
|
||||
if owner is None:
|
||||
owner = Owner.create(key=generate_key(), is_permanent=True)
|
||||
return owner
|
||||
|
||||
|
||||
def _request_hosts(request: Request) -> set[str]:
|
||||
"""Every host spelling that legitimately identifies this deployment."""
|
||||
hosts = {request.url.netloc}
|
||||
@@ -98,6 +113,16 @@ class OwnerSessionMiddleware(BaseHTTPMiddleware):
|
||||
if path in _UNSCOPED_PATHS or path.startswith(_UNSCOPED_PREFIXES):
|
||||
return await call_next(request)
|
||||
|
||||
if cfg.SINGLE_USER:
|
||||
# No cookie is read and none is set: identity comes from the DB, so
|
||||
# onboarding ("keep your printers?") and the ephemeral-cookie
|
||||
# warning are both meaningless here.
|
||||
request.state.owner = single_user_owner()
|
||||
request.state.is_new_owner = False
|
||||
request.state.ephemeral_session = False
|
||||
request.state.single_user = True
|
||||
return await call_next(request)
|
||||
|
||||
key = request.cookies.get(COOKIE_NAME)
|
||||
owner = Owner.get_or_none(Owner.key == key) if key else None
|
||||
is_new = owner is None
|
||||
@@ -106,6 +131,7 @@ class OwnerSessionMiddleware(BaseHTTPMiddleware):
|
||||
|
||||
request.state.owner = owner
|
||||
request.state.is_new_owner = is_new
|
||||
request.state.single_user = False
|
||||
# Templates warn about it; see cookie_kwargs().
|
||||
request.state.ephemeral_session = not cfg.COOKIE_SECURE
|
||||
|
||||
|
||||
Reference in New Issue
Block a user