T0-A1 Active-Role Session / Concurrency Decision¶
| Item | Value |
|---|---|
| Mode | Server development |
| Workspace | /home/scola/odoo |
| Status | CLOSED for V1 architecture — DEC-002 approved: session-scoped active role |
| Related | STAB-007, AUTH-001, DEC-002 |
Decision (approved)¶
B — Session-scoped acting role is the Scola V1 authorization architecture.
Implemented in T0-B1 via scola_core/services/session_active_role.py (scola_active_role on Odoo HTTP session). res.users.scola_role is preferred/default for new sessions only.
Current behavior¶
- Acting role is stored on
res.users.scola_role(user-global field). action_switch_active_scola_rolevalidates assignment/authority groups, thenwrite({'scola_role': role_code}).compute_permissions(post T0-A) derives capabilities from active role only, not group-union.- HTTP session cookie does not own a separate acting-role slot; every request re-reads the user record.
Scenario analysis¶
| Scenario | Observed / reasoned behavior |
|---|---|
| Two tabs, same browser session | Share cookie → same DB user. Last successful switch wins for both tabs. Tab A can suddenly lose capabilities mid-flight after Tab B switches. |
| Two independent browser sessions (same login) | Still same res.users row. Switch in session 1 updates global acting role for session 2. |
| Desktop + mobile | Same as independent sessions — last write wins globally. |
| Role switch during in-progress mutation | Next request after write uses new role. In-flight JSON call already past gate may complete under old snapshot; subsequent calls use new role. No request-scoped freeze. |
| Stale frontend permissions | FE holds permission list until refresh/switch payload. Backend is authoritative; stale FE UX can show buttons that API will 403. |
| Logout / login | Logout clears session; login rebuilds payload from current scola_role. Global field persists across sessions until switched again. |
| Concurrent switch requests | Last committed write wins; no optimistic concurrency / etag on scola_role. |
Options¶
A. User-global acting role (current)¶
- Security: Simple single source of truth; hard to “forget” a privileged persona on one device while another retains it — but also means privilege changes are instantly cross-device.
- UX: Surprising across tabs/devices (last-write-wins).
- Audit: Acting role at request time equals user field; attribution clear if sampled per request.
- Complexity: Lowest.
- Revocation: Changing role revokes previous persona everywhere immediately.
- Testability: Easy unit tests; concurrency tests must assert global last-write.
B. Session-scoped acting role¶
- Store acting role on server session (or signed session claim), not (only) on
res.users. - Security: Better isolation between devices; must define which field remains for defaults/provisioning.
- UX: Predictable per tab/session family; multi-device can diverge intentionally.
- Audit: Must log session id + acting role; user field alone insufficient.
- Complexity: Medium–high (session store, migration, FE refresh contract, revoke-all-sessions).
- Revocation: Need explicit session kill / role-invalidate broadcast.
- Testability: Requires multi-session fixtures in T0-B.
C. Other server-owned context¶
- e.g. short-lived acting-role token / request header bound to session id with server validation.
- Highest flexibility; highest implementation and abuse surface unless tightly designed.
Recommendation¶
Recommend B (session-scoped acting role) as the long-term security/UX target for multi-device schools.
For T0-A.1 / immediate T0-B entry: do not silently change semantics. Keep A (user-global) as the documented interim contract until Product + Security approve DEC-002 / this record.
Rationale for interim A:
- Matches shipped code and T0-A unit evidence.
- Changing to B mid-certification would invalidate prior tests and require session redesign out of T0-A.1 scope.
Decision status¶
| Field | Value |
|---|---|
| Chosen interim for code (T0-A.1) | A — user-global acting role (historical) |
| Approved / implemented (T0-B1) | B — session-scoped acting role |
| Product/Security sign-off | DEC-002 APPROVED |
| Silent semantic change in T0-A.1 | No (correct) |
| Semantic change in T0-B1 | Yes — intentional per DEC-002 |
Migration impact (if B approved later)¶
- Introduce session key
scola_active_role(name TBD) set on login + switch. compute_permissions/ gates read session role with fallback to user default.- Decide whether
res.users.scola_roleremains “last preferred” only. - FE: treat permissions as session-bound; refresh on focus if conflict detected.
- Audit: persist session id + acting role on sensitive denials/mutations.
- Revocation: logout clears session role; admin kill-sessions clears all.
Required T0-B tests (entry gate)¶
Until DEC is closed, T0-B must explicitly test interim A and mark B as future:
- Two tabs same session: switch in tab B → tab A API uses new role (document expected).
- Two independent sessions: switch in one → other observes new role (A) or isolation (B once implemented).
- Switch mid mutation: completed call vs follow-up call authority.
- Stale FE: UI capability cache vs API 403 after switch.
- Logout/login restores documented default/active role.
- Concurrent switch: last-write-wins + audit both attempts.
- Forged role / unauthorized switch still denied (STAB-007).
T0-B must not assume session isolation until B is implemented and this decision is CLOSED.