Design: Role Permission Matrix editor (G4) #788
ChakshuGautam
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Role Permission Matrix editor
Architecture discussion for phase G4 of the CRS Configurator. Implementation tracked in (draft) PR #786. Stacked on the escalation foundation in PR #770.
G4: Role Permission Matrix editor
Why
Every CRS tenant runs the same six PGR/CRS workflow roles against the same
small set of complaint-lifecycle functions — view, edit, assign, close, see
the dashboard, change configuration. Today the mapping is split across
three places:
action).
menu entry).
/escalation/_triggeradmin endpoint shipped in PR feat(pgr+configurator): escalation feature — OTEL-instrumented scheduler, /escalation/_trigger admin endpoint, configurator editor, embedded workflow designer, integration tests #770(ADMIN-only by hardcoded role check).
This phase generalises (1)–(3) into a single editable surface — a
per-tenant matrix of
roles × functionswith constrained cell values,plus a sibling audit log that captures every save. Once this lands, the
matrix becomes the single source of truth that subsequent phases (G5–G8)
gate against.
Reference: BRD §5.2 "Permission Matrix" (six roles × five functions with
explicit Yes / No / Read-only cells); CRS Configurator roadmap
docs/crs-configurator-roadmap.md§"Phase G4 — Role Permission Matrix".Scope
In this phase:
{ yes, no, read-only, operational, executive }— mirroring the BRD,including the dashboard column's
Operational/Executivevariants.CRS.RolePermissionMatrix(single record per tenant).CRS.ConfigAuditLog(introduced here, reused by everylater G-phase that mutates configurator state).
pgr-servicesthat reads the matrixonce per request (Redis-cached, invalidated on save) and enforces it
for
_close,_assign, and the_triggerendpoint from PR feat(pgr+configurator): escalation feature — OTEL-instrumented scheduler, /escalation/_trigger admin endpoint, configurator editor, embedded workflow designer, integration tests #770.BRD §5.2 verbatim values.
Explicitly OUT (deferred):
state machine; needs its own phase).
customisation is in scope).
may revisit this).
only. A richer diff view is a follow-up.
the matrix at render time — G4 only enforces server-side. The sidebar
migration is a sibling track.
MDMS schemas
Two new schema codes are reserved by this PR. Both are committed as
empty-shell stubs in
utilities/default-data-handler/src/main/resources/schema/CRS.G4.jsonwith
isActive: falseso MDMS won't accept records against them untilthe implementation PR fills the shape in.
CRS.RolePermissionMatrixSingle record per tenant. Holds the full 6×5 grid.
{ "tenantId": "ke.bomet", "version": 1, "cells": [ { "role": "RECEPTION_TECHNICIAN", "function": "VIEW_CASES", "value": "yes" }, { "role": "SUPERVISOR", "function": "CLOSE", "value": "yes" }, { "role": "LEADERSHIP", "function": "VIEW_DASHBOARD", "value": "executive" } ], "updatedBy": "user-uuid", "updatedAt": 1717891200000 }Schema-level constraints (
required,x-unique,additionalProperties: false) match the rest of theCRS.*family. Per-cell enum validationfor
role/function/valueis JSON-Schema-native (noapplication-side workaround needed — unlike
slaHoursByState, whichhad to defer numeric/array cell-shape validation to the client).
CRS.ConfigAuditLogAppend-only. One row per save across all CRS.* configurator screens
from G4 onwards.
{ "id": "uuid", "tenantId": "ke.bomet", "schemaCode": "CRS.RolePermissionMatrix", "recordCode": "ke.bomet", "action": "UPDATE", "beforeJson": "...", "afterJson": "...", "actorUserId": "user-uuid", "actorRoles": ["ADMINISTRATOR"], "at": 1717891200000 }Configurator routes + UI sketch
New routes (under
/manage/crs/...)/manage/crs-permissions— the 6×5 grid editor./manage/crs-audit— generic config audit-log viewer with filters byschemaCode, actor, and date range.Sidebar nav
Both routes land in the Governance sidebar group (existing group;
currently holds escalation-config + designation-tree). New group only
if Governance ends up with more than ~6 entries by end of G-phases.
Page anatomy —
/manage/crs-permissionstimestamp + "Reset to BRD defaults" button (with confirm modal).
button (disabled when no diff vs. last saved state).
Each
[...]cell is a dropdown constrained to the five allowed values.Cells with non-default values render with a subtle background so the
operator can see at a glance what's been customised.
Page anatomy —
/manage/crs-auditStandard configurator list page: filter panel (
schemaCodemulti-select, actor combobox, date range), virtualised table with
columns
at,actor,schemaCode,recordCode,action, and a"view diff" expander reusing the JSON-diff component from the
EscalationConfig editor (#770).
API endpoints touched
MDMS v2 (the default path)
POST /mdms-v2/v2/_create/CRS.RolePermissionMatrix— write.POST /mdms-v2/v2/_searchwithschemaCode=CRS.RolePermissionMatrix— read.
POST /mdms-v2/v2/_create/CRS.ConfigAuditLog— write (calledserver-side by the configurator-bff on every successful save).
POST /mdms-v2/v2/_searchwithschemaCode=CRS.ConfigAuditLog—read for the audit viewer.
Backend services that need new endpoints
None new. But one new middleware in
pgr-services(~50 LOC):crs.permission.matrix.<tenant>(mirrors thevalidationRulespattern from the CCRS mobile-validation work).
_create/_updatecall (publish to a dedicated Redis pub/sub topiccrs.permission.matrix.invalidate; pgr-services subscribes).pgr-services/v2/_assign,pgr-services/v2/_close,pgr-services/escalation/_trigger.Dependencies on prior phases
Hard dependencies (must ship first):
/escalation/_triggerendpointis the headline thing G4 enforces against.
Soft dependencies (nice to have first, not blocking):
CRS.ConfigAuditLogif they ship afterG4; if they land first they get retrofitted.
What this phase blocks:
CRS.ConfigAuditLogfor savehistory.
accessLevelfield (Operational / Executive cells gate whichindicators a role sees).
Acceptance criteria
Operator-runnable checks that confirm G4 is fully shipped:
§5.2 verbatim defaults; all 30 cells match exactly.
Supervisor / Closefromyestonocauses a Supervisor'sPOST /pgr-services/v2/_closeto returnHTTP 403 within one request (no service restart), with a structured
reasonfield naming the denying tuple.CRS.ConfigAuditLogrow with non-emptybeforeJson/afterJson,the correct
actorUserId, and anatwithin ±2s of the save.redis-cli GET crs.permission.matrix.<tenant>is deleted within 500ms of a save.
POST /escalation/_trigger403s when(ADMINISTRATOR, EDIT_CASES)flips tono.audit-log row whose
afterJsonmatches the BRD seed byte-for-byte./manage/crs-auditfiltered byschemaCode=CRS.RolePermissionMatrixshows the last N saves inreverse-chronological order with a working diff expander.
Estimated effort
M (~3–4 days) — small grid UI, one MDMS schema with strict enums,
one shared backend filter, one ConfigAuditLog schema that pays for
itself across G5–G8. Bulk of the work is the Redis-cache invalidation
plumbing (mirror the validationRules pattern) and the audit-viewer
diff component (reuse from EscalationConfig editor).
Open questions
configurator sidebar consult the matrix at render time, or stay on
the existing hardcoded role gates and let server-side 403s be the
only enforcement? Server-side is the authoritative answer, but a
sidebar that shows a menu the user can't actually use is a poor UX.
CRS.ConfigAuditLogget a TTL, orgrow unbounded? MDMS v2 has no native TTL; we'd need a periodic
purge job. If we go unbounded, the audit-viewer needs server-side
pagination from day one.
Operational/Executiveonly for the dashboard column. Do we constrain thevalue picker per-column (dashboard column gets a different option
set than the other four), or accept any of the five values in any
cell and treat mismatched values as "no" at enforcement time?
EMPLOYEE,CITIZEN,ULB_ADMINthat don't map 1:1 to the BRD'ssix. Do we auto-create the six BRD roles in keycloak/eg_userrole
on tenant bootstrap, or document a manual setup step?
individually (more audit rows, finer-grained undo), or does the
whole grid save atomically as one record (one audit row per save,
simpler invalidation)? Roadmap currently assumes atomic.
Cross-references
cross-linked from the draft PR)
§"Phase G4 — Role Permission Matrix"
(the
/escalation/_triggerendpoint that G4 gates)OTEL spans, and the SLA scheduler that G4 enforces against)
(sibling doc from PR #B; G4 is orthogonal but operators reading
the G-phase docs in order will hit them together)
Beta Was this translation helpful? Give feedback.
All reactions