Add compliance portal manager roles - #1608
Conversation
f097121 to
c9c08a0
Compare
Owners and admins can assign COMPLIANCE_MANAGER so people can fully manage the compliance portal without broader admin access. Grant portal full access plus the related document, audit, and third-party visibility permissions. Signed-off-by: Bryan Frimin <bryan@probo.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr> Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Delegate visitor access approval without granting full compliance portal configuration. The role can list and update portal access requests and only sees the Access tab in the console. Signed-off-by: Bryan Frimin <bryan@probo.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
The switch closing brace was over-indented and failed @stylistic/indent in CI. Signed-off-by: Bryan Frimin <bryan@probo.com> Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Hiding tabs a role cannot open left the section looking broken and made it unclear why an entry was missing. Keep every tab visible and let each child route report the denial: a route error boundary catches the ForbiddenError raised by the page query and renders a missing-access message in place of the outlet, so the header and tabs stay usable. Signed-off-by: Bryan Frimin <bryan@probo.com> Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr> Signed-off-by: Cursor Agent <cursoragent@cursor.com>
8602f4c to
71292d3
Compare
Access managers were shown every config tab and a no-access page. Gate tabs on permission fields instead, narrow their IAM policy, and grant portal:list so they land on Access directly. Signed-off-by: Bryan Frimin <bryan@probo.com>
These roles only cover the compliance portal, so rename them to COMPLIANCE_PORTAL_* across the API, UI, and database. Drop the old ContentErrorBoundary now that portal tabs use an in-page no-access state. Signed-off-by: Bryan Frimin <bryan@probo.com>
| -- SOFTWARE. | ||
|
|
||
| ALTER TYPE authz_role RENAME VALUE 'COMPLIANCE_MANAGER' TO 'COMPLIANCE_PORTAL_MANAGER'; | ||
| ALTER TYPE authz_role RENAME VALUE 'COMPLIANCE_ACCESS_MANAGER' TO 'COMPLIANCE_PORTAL_ACCESS_MANAGER'; |
There was a problem hiding this comment.
Maybe merge the migration?
There was a problem hiding this comment.
Done — folded the rename into the original ADD migrations so they introduce COMPLIANCE_PORTAL_MANAGER / COMPLIANCE_PORTAL_ACCESS_MANAGER directly, and dropped 20260805T074339Z.sql.
There was a problem hiding this comment.
2 issues found across 32 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/console/src/pages/organizations/compliance-portals/configuration/CompliancePortalConfigLayout.tsx">
<violation number="1" location="apps/console/src/pages/organizations/compliance-portals/configuration/CompliancePortalConfigLayout.tsx:133">
P1: Access managers lose most compliance portal tabs instead of seeing them with the child-route forbidden state. The new permission wrappers hide the routes entirely, contradicting the required “all tabs remain visible” UX; keep these `TabLink`s rendered unconditionally and let the child routes handle `FORBIDDEN`.</violation>
</file>
<file name="apps/console/src/_locales/en-US.json">
<violation number="1" location="apps/console/src/_locales/en-US.json:408">
P2: The new pageError.forbidden keys are never referenced: PageError.tsx still renders the "Page not found" text for FORBIDDEN errors, so the user-friendly "you do not have access" state described in the PR won't actually be shown to denied users. Wire the FORBIDDEN branch of PageError.tsx to the new keys (or confirm the other changed file does) so the intended messaging appears.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| <IconBell2 className="size-4" /> | ||
| {t("configLayout.tabs.mailingList")} | ||
| </TabLink> | ||
| {compliancePortal.canListFrameworks && ( |
There was a problem hiding this comment.
P1: Access managers lose most compliance portal tabs instead of seeing them with the child-route forbidden state. The new permission wrappers hide the routes entirely, contradicting the required “all tabs remain visible” UX; keep these TabLinks rendered unconditionally and let the child routes handle FORBIDDEN.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/console/src/pages/organizations/compliance-portals/configuration/CompliancePortalConfigLayout.tsx, line 133:
<comment>Access managers lose most compliance portal tabs instead of seeing them with the child-route forbidden state. The new permission wrappers hide the routes entirely, contradicting the required “all tabs remain visible” UX; keep these `TabLink`s rendered unconditionally and let the child routes handle `FORBIDDEN`.</comment>
<file context>
@@ -107,46 +130,66 @@ export default function CompliancePortalConfigLayout({ queryRef }: CompliancePor
- <IconBell2 className="size-4" />
- {t("configLayout.tabs.mailingList")}
- </TabLink>
+ {compliancePortal.canListFrameworks && (
+ <TabLink to={portalBase} end>
+ <IconSettingsGear2 className="size-4" />
</file context>
There was a problem hiding this comment.
Leaving this as-is. Hiding forbidden tabs was an intentional follow-up in efda6bc07 (access managers land on Access directly instead of seeing every tab with a no-access page).
| "title": "Page not found", | ||
| "description": "The page you are looking for does not exist." | ||
| }, | ||
| "forbidden": { |
There was a problem hiding this comment.
P2: The new pageError.forbidden keys are never referenced: PageError.tsx still renders the "Page not found" text for FORBIDDEN errors, so the user-friendly "you do not have access" state described in the PR won't actually be shown to denied users. Wire the FORBIDDEN branch of PageError.tsx to the new keys (or confirm the other changed file does) so the intended messaging appears.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/console/src/_locales/en-US.json, line 408:
<comment>The new pageError.forbidden keys are never referenced: PageError.tsx still renders the "Page not found" text for FORBIDDEN errors, so the user-friendly "you do not have access" state described in the PR won't actually be shown to denied users. Wire the FORBIDDEN branch of PageError.tsx to the new keys (or confirm the other changed file does) so the intended messaging appears.</comment>
<file context>
@@ -405,6 +405,10 @@
"title": "Page not found",
"description": "The page you are looking for does not exist."
},
+ "forbidden": {
+ "title": "You do not have access to this page",
+ "description": "Your role does not allow you to view this page. Ask an organization admin if you need access."
</file context>
There was a problem hiding this comment.
Fixed — PageError now uses pageError.forbidden.*, and the Dutch locale got the matching keys.
Merge the role rename into the original ADD migrations so the branch never introduces the temporary names. Restore an explicit SAML allowlist that adds the portal roles without newly admitting AUDITOR, wire PageError to the forbidden copy, translate the Dutch role labels, and document the intentional org-scoped reads for access managers. Signed-off-by: Bryan Frimin <bryan@probo.com> Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Adds two membership roles for compliance portal delegation without full admin access.
COMPLIANCE_PORTAL_MANAGERcompliance-portalsCOMPLIANCE_PORTAL_ACCESS_MANAGERAlso
PageErrorshows dedicated forbidden copy forFORBIDDENerrorsAUDITORcompliance-portal:action namespaceCOMPLIANCE_PORTAL_*enum values directly (no rename migration)Summary by cubic
Adds two portal-scoped membership roles,
COMPLIANCE_PORTAL_MANAGERandCOMPLIANCE_PORTAL_ACCESS_MANAGER, to delegate compliance portal management and access approvals without broader admin rights. The console gates tabs by permission, redirects access managers to Access, and shows a clear forbidden state when a page is denied.Coredata
+54-6Add the new roles to
authz_role; extendMembershipRoleand validation; create migrations that add the portal-scoped names directly (no rename step).Service
+114-8Add Compliance Portal Manager/Access Manager policies and map them across policy sets; minimal IAM for access managers (org read + membership-profile get); extend Probo and resource-alias policies; SAML role mapping uses an explicit allowlist that includes the portal roles but not AUDITOR.
GraphQL API
+16-0Expose the new roles in Connect and Console membership role enums.
MCP
+2-0Include both roles in the MCP spec enum.
prb (CLI)
+2-2Allow filtering by the new roles in
prb user list.Package: helpers
+33-2Add role constants/labels and include them in assignable roles for owners/admins.
Package: n8n-node
+6-0Add the roles to
createUser,listUsers, andupdateMembershipoptions.App: console
+139-59Permission-gate portal tabs and create actions; redirect access managers from portal root to Access; fix commitment permission namespaces; route both roles to
compliance-portals; wire PageError to the forbidden copy; add i18n labels/descriptions.Tests
+8-6Add roles to test utilities and update IAM policy set tests.
Agents
+4-10Update authorization and routing docs with the new role names and explain org-scoped reads for access managers.
Written for commit c74e8bd. Summary will update on new commits.