Skip to content

improvement(perms): followup to org scoping of permission groups#5039

Merged
icecrasher321 merged 1 commit into
stagingfrom
fix/org-help
Jun 14, 2026
Merged

improvement(perms): followup to org scoping of permission groups#5039
icecrasher321 merged 1 commit into
stagingfrom
fix/org-help

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

Followup to #5035

Type of Change

  • Bug fix

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 14, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 14, 2026 1:01am

Request Review

@cursor

cursor Bot commented Jun 14, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes who can manage access control and which org APIs are called; incorrect scoping could wrongly grant or deny admin UI, but logic is centralized on the server with targeted query skipping on the client.

Overview
Fixes org scoping for permission groups so management and entitlement use the workspace’s owning organization, not the caller’s active org (e.g. external members viewing another org’s workspace).

The GET /api/permission-groups/user response now includes organizationId and isOrgAdmin, computed server-side via isOrganizationAdminOrOwner against that owning org. Workspaces with no org return a clear unentitled payload. The Access Control UI reads those fields from useUserPermissionConfig instead of session/active org + client getUserRole, and only loads permission groups and roster when the user is an org admin—avoiding expected 403s for non-admins.

Admin DELETE access-control adds an explicit organizationId guard (replacing a non-null assertion).

Reviewed by Cursor Bugbot for commit 3c4234c. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes permission-group admin gating to use the workspace's owning organization rather than the caller's active organization, so external members (users who belong to a workspace via a different org) are no longer incorrectly treated as admins of that workspace's org.

  • The /api/permission-groups/user route now resolves organizationId and isOrgAdmin server-side against the workspace's owning org, and the userPermissionConfigSchema contract is extended to carry both fields back to the client.
  • AccessControl drops the client-side useSession / useOrganizations / getUserRole chain and reads organizationId and isOrgAdmin directly from the already-fetched userPermissionConfig, cascading queries correctly so permission groups and the org roster are only fetched for confirmed admins.
  • The admin DELETE route in /api/v1/admin/access-control gains an explicit !organizationId guard before the DB query, removing a non-null assertion that was technically unsafe if the Zod contract's refine were ever relaxed.

Confidence Score: 5/5

Safe to merge — the fix correctly moves org-admin resolution to the server, all query cascades are properly gated, and every response path in the API route returns the new fields.

The core logic change is straightforward: the API route now derives both organizationId and isOrgAdmin from the workspace record rather than the session's active org. All four return sites in the route consistently include the new fields, the Zod contract is updated to match, and the component drops the now-redundant client-side role computation without leaving orphaned state. The isLoading chain correctly waits for entitlement before cascading to groups, and the admin DELETE route gains a proper null guard instead of a non-null assertion.

No files require special attention; the two-round-trip latency for org admins (userPermissionConfig must resolve before permission groups can be fetched) is intentional and well-handled by the isLoading guard.

Important Files Changed

Filename Overview
apps/sim/app/api/permission-groups/user/route.ts Splits the old combined guard into two passes — early-exit for no-org workspaces, then resolves isOrgAdmin before the enterprise-plan check — and populates organizationId/isOrgAdmin on every response path. Logic is clean and consistent across all four return sites.
apps/sim/ee/access-control/components/access-control.tsx Replaces the client-side active-org chain with server-derived organizationId/isOrgAdmin; cascades group and roster fetches correctly behind the admin flag. The isLoading condition now chains on entitlementLoading first, which is correct but introduces a subtle two-round-trip latency for admins.
apps/sim/app/api/v1/admin/access-control/route.ts Adds an explicit organizationId null-guard in DELETE to replace the non-null assertion; import of badRequestResponse added accordingly. Small, safe defensive improvement.
apps/sim/lib/api/contracts/permission-groups.ts Extends userPermissionConfigSchema with organizationId (nullable string) and isOrgAdmin (boolean); JSDoc comments explain the semantics clearly.

Sequence Diagram

sequenceDiagram
    participant C as AccessControl (client)
    participant UPC as /api/permission-groups/user
    participant PG as /api/organizations/[id]/permission-groups
    participant Roster as org roster
    participant DB as Database

    C->>UPC: "GET ?workspaceId=X"
    UPC->>DB: checkWorkspaceAccess(workspaceId, userId)
    DB-->>UPC: workspace (incl. organizationId)
    alt no organizationId
        UPC-->>C: "{isOrgAdmin: false, organizationId: null, entitled: false}"
    else has organizationId
        UPC->>DB: isOrganizationAdminOrOwner(userId, orgId)
        DB-->>UPC: isOrgAdmin
        UPC->>DB: isOrganizationOnEnterprisePlan(orgId)
        DB-->>UPC: entitled
        UPC-->>C: "{isOrgAdmin, organizationId, entitled, ...}"
    end

    C->>C: "organizationId = userPermissionConfig.organizationId"
    C->>C: "currentUserIsOrgAdmin = userPermissionConfig.isOrgAdmin"

    alt "currentUserIsOrgAdmin == true"
        C->>PG: GET (fetch permission groups)
        PG-->>C: permissionGroups[]
        C->>Roster: GET (fetch org roster)
        Roster-->>C: roster[]
    end
Loading

Reviews (1): Last reviewed commit: "followup" | Re-trigger Greptile

@icecrasher321 icecrasher321 merged commit 1fdb43f into staging Jun 14, 2026
15 checks passed
@icecrasher321 icecrasher321 deleted the fix/org-help branch June 14, 2026 01:14
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