PR #265 · pramaan-io/pramaan-functions · merged 2026-06-07
When support needs to enter a firm — without seeing the firm.
One session. One firm. Zero confidential rows.
Wide key. Same door. Confidential leak.
The fix — On duty, re-verified every request, allowlisted to settings only — the rest is invisible at the database.
sequenceDiagram
autonumber
participant staff as Platform Staff
participant router as Front Desk (Router + DB dep)
participant db as Config Tables (allowlist)
participant vault as Matters / Clients (no policy)
staff->>router: POST /go-on-duty {firm_id}
router->>router: verify active grant exists
router->>db: on_duty_firm_id =
router->>router: load 'support' capability set (settings only)
staff->>router: request with staff token
router->>router: re-verify grant EVERY request
router->>db: SET app.support_firm_id = X
router-->>vault: no policy → row invisible
| Method | in → out | what it does |
|---|---|---|
_resolve_on_duty_firm_id | (session, staff_user_id) → UUID | looks up which firm the staff is on duty for; NULL means off duty |
get_db_with_app_support_session | (request, current_user) → AsyncIterator[AsyncSession] | re-verifies the active grant on every request and opens a support-scoped session |
_create_support_firm_read_only_policy | (table) → None | installs the allowlist door lock on a config table |
_create_support_firm_id_policy | (table) → None | installs the per-firm policy that scopes the support key |
Migration 0076 that creates per-firm RLS policies on allowlisted config tables and leaves confidential tables with no policy.
| new method | signature |
|---|---|
_create_support_firm_policy | (table) → None |
_create_support_firm_read_only_policy | (table) → None |
_create_support_firm_id_policy | (table) → None |
_drop_support_policy | (table) → None |
upgrade | () → None |
downgrade | () → None |
revision : strdown_revision : str | Sequence[str] | Nonebranch_labels : str | Sequence[str] | Nonedepends_on : str | Sequence[str] | NoneNew support session dependency that resolves the on-duty firm and re-verifies the active grant on every single request.
| new method | signature |
|---|---|
_resolve_on_duty_firm_id | (session, staff_user_id) → UUID |
get_db_with_app_support_session | (request, current_user) → AsyncIterator[AsyncSession] |
Business logic for the on/off duty toggle, including the active-grant precondition before a staff is allowed on duty.
| new method | signature |
|---|---|
go_on_duty | (session, user_id, firm_id) → StaffOnDutyResponse |
go_off_duty | (session, user_id) → StaffOnDutyResponse |
Admin HTTP endpoints that expose go on duty and go off duty to platform staff.
| new method | signature |
|---|---|
go_on_duty | (req, db, current_user) → StaffOnDutyResponse |
go_off_duty | (db, current_user) → StaffOnDutyResponse |
Request and response shapes for the on/off duty endpoints, including the on_duty_firm_id field.
| new class | signature |
|---|---|
StaffOnDutyRequest | (class) |
StaffOnDutyResponse | (class) |
firm_id : UUIDuser_id : UUIDon_duty_firm_id : UUID | NoneAdds the on_duty_firm_id field on the platform_staff entity, the NULLable FK that drives the duty state.
on_duty_firm_id : UUID | NoneAdds the _is_support_session helper so firm routes can detect a support-staff session and scope writes correctly.
| new method | signature |
|---|---|
_is_support_session | (session) → bool |