Skip to content

improvement(governance): derived access#5134

Merged
icecrasher321 merged 11 commits into
stagingfrom
improvement/gov-model-guarantees
Jun 19, 2026
Merged

improvement(governance): derived access#5134
icecrasher321 merged 11 commits into
stagingfrom
improvement/gov-model-guarantees

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

Org Admins are auto Workspace Admins. And workspace admins are auto credential admins.

Type of Change

  • Other: UX improvement

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 19, 2026 7:33pm

Request Review

@cursor

cursor Bot commented Jun 19, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Broad changes to authorization and credential/workspace access across sim, realtime, and many API routes; incorrect derived-role logic could grant or deny access incorrectly.

Overview
Replaces @sim/workflow-authz with @sim/platform-authz (workflow, workspace, and predicate subpaths) and wires derived governance through APIs and UI.

Organization admins are treated as workspace admins on every org workspace (roster, workspace lists, permission updates block demotion). Workspace admins are credential admins for shared credentials (OAuth, service accounts, workspace env vars)—listing, OAuth credential pickers, member APIs, and env secret read/edit rules follow that model; personal env vars stay private.

Credential member APIs now merge workspace-admin rows with roleSource, block demoting/removing org/workspace admins, and use getCredentialActorContext / deriveCredentialAdmin instead of raw membership-only checks. Many routes swap permissions joins for checkWorkspaceAccess, listAccessibleWorkspaceRowsForUser, or permissionSatisfies so org-derived access applies (logs, copilot chats, webhooks, MCP discover, v1 logs, etc.).

Settings UI adds RoleLockTooltip on locked workspace/credential roles; docs expand credential access and env-variable permissions. Org member usage-limit GET also returns billingInterval (month/year).

Reviewed by Cursor Bugbot for commit e1241a0. Configure here.

Comment thread apps/sim/app/api/credentials/route.ts
@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR implements a governance inheritance model where org owners/admins are automatically workspace admins, and workspace admins are automatically credential admins on all shared (non-personal) credentials. The implementation is centralized in a new @sim/platform-authz package so both apps/sim and apps/realtime share the same resolution logic.

  • Derived workspace access: resolveEffectiveWorkspacePermission (shared package) adds an org-admin lookup on top of the explicit permissions row, replacing the old hand-written ownerId === userId owner shortcut with a claim that every owner always holds an explicit admin permission row.
  • Derived credential access: deriveCredentialAdmin grants credential admin to any user who is a workspace admin on the containing workspace (except for personal env credentials); the credential-member API's GET/POST/DELETE endpoints are updated to enforce and surface these derived roles.
  • revokeWorkspaceCredentialMembershipsTx simplification: the previous per-credential owner-promotion logic is removed since workspace admins can never orphan a shared credential.

Confidence Score: 5/5

Safe to merge; the governance inheritance is correctly centralized in a shared package and consistently applied across workspace and credential access checks.

The core derivation logic in resolveEffectiveWorkspacePermission and deriveCredentialAdmin is correct, and the credential route's last-admin guards are properly scoped to personal env credentials. The only new finding is a display-only isExternal non-determinism in getUsersWithPermissions from an unconstrained leftJoin on the member table, which does not affect access control.

apps/sim/lib/workspaces/permissions/utils.ts — the leftJoin(member, ...) without org-id scoping in getUsersWithPermissions.

Important Files Changed

Filename Overview
packages/platform-authz/src/workspace.ts New shared resolver for effective workspace permission: fetches explicit permission row and optionally derives admin from org owner/admin role; logic is correct for the defined governance model.
packages/platform-authz/src/predicates.ts New shared permission predicates (permissionSatisfies, isOrgAdminRole, PERMISSION_RANK) extracted so both apps/sim and apps/realtime can share them; correct and dependency-free.
apps/sim/lib/workspaces/permissions/utils.ts Refactored checkWorkspaceAccess and getUserEntityPermissions to use org-admin inheritance; introduces getUsersWithPermissions rewrite with a leftJoin on member that is not scoped to the workspace org, producing non-deterministic isExternal for multi-org users.
apps/sim/lib/workspaces/utils.ts Adds getOrgAdminWorkspaceRows and listAccessibleWorkspaceRowsForUser to surface derived workspace access; the .limit(1) query without a role filter in getOrgAdminWorkspaceRows may miss admin roles for users in multiple orgs (noted in previous review threads).
apps/sim/lib/credentials/access.ts Adds deriveCredentialAdmin, isSharedCredentialType, and SHARED_CREDENTIAL_TYPES; removes the complex ownership-transfer logic in revokeWorkspaceCredentialMembershipsTx since workspace admins are now always derived credential admins.
apps/sim/app/api/credentials/[id]/members/route.ts GET endpoint now merges derived workspace-admin entries into the credential member list; POST/DELETE guard workspace admins from demotion/removal; last-admin guard is correctly scoped to personal env credentials only.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User] -->|member.role = owner/admin?| B{Org Admin?}
    B -->|Yes| C[Derived Workspace Admin]
    B -->|No| D[Check permissions table]
    D -->|explicit row| E{permissionType}
    E -->|admin| F[Workspace Admin]
    E -->|write/read/null| G[Workspace write/read/none]
    C --> F

    F -->|isSharedCredentialType?| H{Shared Credential?}
    H -->|Yes env_workspace, oauth, service_account| I[Derived Credential Admin]
    H -->|No env_personal| J[Check credentialMember table]
    J -->|role=admin, status=active| K[Explicit Credential Admin]
    J -->|other/none| L[Credential member/no access]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User] -->|member.role = owner/admin?| B{Org Admin?}
    B -->|Yes| C[Derived Workspace Admin]
    B -->|No| D[Check permissions table]
    D -->|explicit row| E{permissionType}
    E -->|admin| F[Workspace Admin]
    E -->|write/read/null| G[Workspace write/read/none]
    C --> F

    F -->|isSharedCredentialType?| H{Shared Credential?}
    H -->|Yes env_workspace, oauth, service_account| I[Derived Credential Admin]
    H -->|No env_personal| J[Check credentialMember table]
    J -->|role=admin, status=active| K[Explicit Credential Admin]
    J -->|other/none| L[Credential member/no access]
Loading

Reviews (4): Last reviewed commit: "chore(db): regenerate kb→workspace casca..." | Re-trigger Greptile

Comment thread apps/sim/lib/workspaces/utils.ts
@icecrasher321 icecrasher321 requested a review from a team as a code owner June 19, 2026 04:14
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread apps/sim/app/api/credentials/[id]/members/route.ts
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread apps/sim/app/api/logs/execution/[executionId]/route.ts
Our 0242 collides with staging's 0242. Remove it (and its snapshot +
journal entry) so the KB-cascade migration can be regenerated with the
correct number on top of the merged staging migrations.
Regenerated via drizzle-kit generate on top of the merged staging
migrations (staging took 0242). Re-applied the safety edits: NOT VALID
+ separate VALIDATE on the FK re-add, and the -- migration-safe note on
the DROP. check:migrations passes.
@icecrasher321 icecrasher321 changed the title improvement(governance): org-ws-credential roles clarity improvement(governance): derived access Jun 19, 2026
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e1241a0. Configure here.

@icecrasher321 icecrasher321 merged commit 91f9dfd into staging Jun 19, 2026
15 checks passed
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.

1 participant