Skip to content

Add compliance portal manager roles - #1608

Open
gearnode wants to merge 7 commits into
mainfrom
cursor/compliance-manager-role-f28c
Open

Add compliance portal manager roles#1608
gearnode wants to merge 7 commits into
mainfrom
cursor/compliance-manager-role-f28c

Conversation

@gearnode

@gearnode gearnode commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Adds two membership roles for compliance portal delegation without full admin access.

COMPLIANCE_PORTAL_MANAGER

  • Full compliance portal management
  • Supporting permissions to toggle document/audit/third-party portal visibility, set aliases, and manage Slack connectors used by the portal
  • Lands on compliance-portals

COMPLIANCE_PORTAL_ACCESS_MANAGER

  • Manage visitor access and document access requests only (list/create/update/delete portal access)
  • Organization-scoped read of documents, audits, reports, and files that access requests may reference (callers still only load IDs attached to requests they can list)
  • Lands on Access for a portal (forbidden tabs are hidden)

Also

  • GraphQL / MCP / CLI / n8n / console UI surfaces for both roles
  • Portal config tabs are gated by permission fields; access managers are redirected from overview to Access
  • PageError shows dedicated forbidden copy for FORBIDDEN errors
  • SAML role mapping allowlists the portal roles without newly admitting AUDITOR
  • Fixed commitment UI permission checks to use the compliance-portal: action namespace
  • Migrations add the final COMPLIANCE_PORTAL_* enum values directly (no rename migration)
Open in Web Open in Cursor 

Summary by cubic

Adds two portal-scoped membership roles, COMPLIANCE_PORTAL_MANAGER and COMPLIANCE_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 -6

Add the new roles to authz_role; extend MembershipRole and validation; create migrations that add the portal-scoped names directly (no rename step).

Service +114 -8

Add 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 -0

Expose the new roles in Connect and Console membership role enums.

MCP +2 -0

Include both roles in the MCP spec enum.

prb (CLI) +2 -2

Allow filtering by the new roles in prb user list.

Package: helpers +33 -2

Add role constants/labels and include them in assignable roles for owners/admins.

Package: n8n-node +6 -0

Add the roles to createUser, listUsers, and updateMembership options.

App: console +139 -59

Permission-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 -6

Add roles to test utilities and update IAM policy set tests.

Agents +4 -10

Update 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.

Review in cubic

@cursor cursor Bot changed the title Add compliance manager membership role Add compliance portal manager roles Jul 31, 2026
@cursor
cursor Bot force-pushed the cursor/compliance-manager-role-f28c branch from f097121 to c9c08a0 Compare July 31, 2026 10:32
gearnode and others added 4 commits August 5, 2026 07:01
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>
@cursor
cursor Bot force-pushed the cursor/compliance-manager-role-f28c branch from 8602f4c to 71292d3 Compare August 5, 2026 07:05
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>
@gearnode
gearnode marked this pull request as ready for review August 5, 2026 07:48
-- 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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe merge the migration?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — folded the rename into the original ADD migrations so they introduce COMPLIANCE_PORTAL_MANAGER / COMPLIANCE_PORTAL_ACCESS_MANAGER directly, and dropped 20260805T074339Z.sql.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Comment thread pkg/probo/policies.go
"title": "Page not found",
"description": "The page you are looking for does not exist."
},
"forbidden": {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — PageError now uses pageError.forbidden.*, and the Dutch locale got the matching keys.

Comment thread pkg/iam/saml/attributes.go Outdated
Comment thread pkg/resourcealias/policies.go
Comment thread apps/console/src/_locales/nl-NL.json Outdated
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants