f2a12f9fed
v0.4 (Operator Tier — Cohort Dashboard + Auth + Postgres) milestone complete. Phases: ✓ P0 pre-execution (planning) → v0.1.6 ✓ P1 operator foundation (Postgres+auth+VC migration) → v0.1.7 ✓ P2 cohort dashboard + aggregation → v0.1.8 ✓ P3 final review + ship → v0.1.9 (= v0.4 milestone release) Requirements covered (8/8): REQ-MT-01 (Postgres store), REQ-MT-02 (aggregation pipeline), REQ-AUTH-01 (operator auth), REQ-DASH-01 (cohort dashboard), REQ-NFR-AUTH-01 (auth NFRs), REQ-NFR-MT-01 (Postgres-in-LXC), REQ-NFR-DASH-01 (k-anonymity ≥10), REQ-NFR-DASH-02 (freshness ≤24h) Grill MUSTs honored (6/6): G-008, G-011, G-027, G-031, G-038, G-041 Tests: 317 pytest pass, 36 skip (Postgres-requiring), 0 fail; 17/17 vitest pass Review: APPROVE_WITH_NOTES (6/6 personas, 0 P0, 8 P1+ carry-forward) Audit: HEALTHY (reconstruction PASS, 8/8 REQ, 6/6 grill) ---ci--- project: praxis phase: 3 milestone: v0.4 status: complete phase_role: final milestone_complete: true milestone_merged_to_main: true tag: v0.1.9 requirements: covered: [REQ-MT-01, REQ-MT-02, REQ-AUTH-01, REQ-DASH-01, REQ-NFR-AUTH-01, REQ-NFR-MT-01, REQ-NFR-DASH-01, REQ-NFR-DASH-02] partial: [] ---/ci---
68 lines
2.5 KiB
Python
68 lines
2.5 KiB
Python
"""Signed cookie configuration (TASK-03-02, D-041, D-056, R-AUTH-01, G-031).
|
|
|
|
Returns kwargs for Starlette SessionMiddleware (itsdangerous HMAC-SHA256
|
|
signed cookies — D-056, stateless, no sessions table). The cookie name is
|
|
`praxis_op` (distinct from any future learner cookie).
|
|
|
|
R-AUTH-01 / G-031 reframe: the PRIMARY mitigation for a sniffed operator
|
|
cookie is the k-anonymity defense-in-depth — the cohort dashboard reads
|
|
only k-anonymized aggregates, so a sniffed cookie leaks NO learner PII.
|
|
The `PRAXIS_COOKIE_SECURE` flag is the SECONDARY mitigation (operational
|
|
convenience for when TLS arrives). It defaults to true; the HTTP pilot
|
|
(LXC, no TLS — D-030) sets it to false with a logged WARNING.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import secrets
|
|
|
|
from loguru import logger
|
|
|
|
_COOKIE_MAX_AGE_S = 28800 # 8h (D-041)
|
|
|
|
|
|
def _env_bool(key: str, default: bool) -> bool:
|
|
raw = os.environ.get(key, "").strip().lower()
|
|
if raw in ("true", "1", "yes", "on"):
|
|
return True
|
|
if raw in ("false", "0", "no", "off"):
|
|
return False
|
|
return default
|
|
|
|
|
|
def get_session_middleware_kwargs() -> dict:
|
|
"""Return kwargs for Starlette SessionMiddleware.
|
|
|
|
If PRAXIS_COOKIE_SECRET is unset, generate an ephemeral random secret
|
|
and log a WARNING (dev only — sessions won't survive a restart and this
|
|
MUST NOT be used in pilot/production).
|
|
"""
|
|
secret = os.environ.get("PRAXIS_COOKIE_SECRET", "").strip()
|
|
if not secret:
|
|
secret = secrets.token_urlsafe(48)
|
|
logger.warning(
|
|
"PRAXIS_COOKIE_SECRET not set — generated an ephemeral random secret. "
|
|
"Sessions will NOT survive a server restart. This is dev-only; set "
|
|
"PRAXIS_COOKIE_SECRET (>=32 bytes) for pilot/production."
|
|
)
|
|
secure = _env_bool("PRAXIS_COOKIE_SECURE", True)
|
|
if not secure:
|
|
logger.warning(
|
|
"Cookie Secure flag disabled (PRAXIS_COOKIE_SECURE=false) — HTTP pilot "
|
|
"mode (R-AUTH-01). Do not use in production. NOTE (G-031): the primary "
|
|
"R-AUTH-01 mitigation is k-anon defense-in-depth (cohort dashboard reads "
|
|
"only k-anonymized aggregates → sniffed cookie leaks no PII); this flag "
|
|
"is the secondary mitigation."
|
|
)
|
|
return {
|
|
"secret_key": secret,
|
|
"session_cookie": "praxis_op",
|
|
"max_age": _COOKIE_MAX_AGE_S,
|
|
"https_only": secure,
|
|
"same_site": "strict",
|
|
"path": "/",
|
|
}
|
|
|
|
|
|
__all__ = ["get_session_middleware_kwargs"] |