Skip to content

Feat permissions audit - #2208

Draft
papistacoding wants to merge 4 commits into
mainfrom
feat-permissions-audit
Draft

Feat permissions audit#2208
papistacoding wants to merge 4 commits into
mainfrom
feat-permissions-audit

Conversation

@papistacoding

Copy link
Copy Markdown
Member

No description provided.

@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openlane-ui Ready Ready Preview Aug 28, 2026 10:27pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored Aug 28, 2026 10:27pm

Request Review

Audit of every <Button>/<button> in the console against the org-level
relations in core's fga/model/generated/crud.fga. Of 265 files invoking
mutations, these trigger surfaces reached the backend with no permission
check at all:

- trust center NDA approve/deny and Approve All, which only ever gated
  the revoke path
- subscriber bulk upload and row delete
- campaign launch, cancel, delete, reminders, recipients and the draft
  setup view
- the workflow wizard, both at its own route and where it renders
  embedded on an empty workflows list
- creating a questionnaire from a template, which was also gated on
  CanCreateTemplate rather than CanCreateAssessment
- inline owner/delegate/approver/stakeholder/assignee cells in the
  controls, policies, procedures, risks and tasks tables

The generic table toolbar fell back to org can_edit whenever a page did
not pass createPermission, which hid the create button from anyone
holding only a <entity>_creator grant. Nine table pages now pass their
own relation; every can_create_* includes can_edit, so nobody who could
already create loses the button.

Campaigns keeps its list readable: a route-level gate on
CanCreateCampaign would lock viewers out of the whole section even
though can_view_campaign is a distinct relation and the nav shows the
entry to every role. The gate sits on the create button and the detail
actions instead.

Two scope bugs found while wiring this up:

- PermissionGate keyed its denial on isSuccess, so a permission-service
  failure left it neither loading nor successful and rendered the
  protected children anyway. It now falls back to hasPermission, which
  fails closed and keeps the impersonation exception.
- can_edit_trust_center_nda_request is declared on the organization
  type, but the NDA page looked for it in a trust-center object-roles
  response, where it can never appear. Request management now ORs the
  org-scoped capability with generic trust-center edit.

Invite resend is separated from invite deletion: a MEMBER holds
can_invite_members and may reissue a member invite, but not manage
existing ones, and previously got an empty actions column.

Inline cells read their own permission via useCanEditRows rather than
threading a flag through the column factories, matching how the rest of
the app resolves org roles at the point of use.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Cross-checked every gate against core: internal/ent/schema/*.go for the
per-entity policy, iam@v0.31.1/entfga/checks.go for what each check
does, and fga/model for the relations themselves.

entfga.CheckEditAccess runs CheckAccessForEdit for delete operations
too, so of the 50 schemas only trustcenterndarequest and
workflowobjectref distinguish deletion at all. Four gates were derived
from the existence of a can_delete_* relation rather than from the check
core invokes, and hid the action from users the backend would allow:

- campaign delete now needs object can_edit (CheckEditAccess)
- subscriber delete now needs org can_edit (CheckOrgWriteAccess) —
  can_delete_subscriber resolves only through full_access, so gating on
  it locked out every admin
- trust center document delete now needs object can_edit
- trust center subprocessor delete now needs org can_edit

Organization delete keeps can_delete: HasOrgMutationAccess is one of the
two places that really does require it.

CanDeleteSubscriber and CanDeleteTrustCenterSubprocessor lose their last
consumers and go with them.

Creates were checked the same way and need no change: CheckCreateAccess
resolves to can_create_<entity>, which is what the createPermission
entries already pass.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Three of the route layouts gated on a relation core never consults for
the mutations behind them.

Branding and customer logos required org can_edit. Core admits any
trust-center editor: rule.AllowIfTrustCenterEditor checks can_edit on
the trust center object and short-circuits to privacy.Allow, and the
FGA model reaches editor either from the org through parent_editor or
from a direct tuple on the trust center. The org-only gate therefore
turned away legitimate editors before the page could run the
useAccountRoles check it already has. Both now use
TrustCenterEditorGate, which reads the object's roles.

The custom domain route required can_create_custom_domain, which is
`can_edit or can_edit_custom_domain`, while core guards CustomDomain
with CheckOrgWriteAccess — plain org can_edit. A can_edit_custom_domain
holder passed the route and then failed every mutation on it.

CanCreateCustomDomain loses its last consumer and goes with it.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Workflows and task creation are no longer gated, route-level gates are
replaced by per-affordance checks, and every trust-center surface resolves
one edit rule instead of eight. Org deletion returns to the owner check and
the email-template editor stops checking a create relation on update.

Close the gaps the route gates were hiding: SSO actions, domain
verification, FAQ reordering, subprocessor mode switching and trust-center
document management. Create gates run through hasPermission so
impersonation keeps working, and no longer stop at the button.

domain-list-editor is carried here too: the SSO and allowed-domain gates
need its prop shape, so the branch would not build without it.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant