fix(workspaces): explain why org admins can't be removed from a workspace - #6838
Conversation
…pace Organization admins hold workspace admin through their org role, not a permissions row, so removal had nothing to revoke. It failed with "User not found in workspace" for someone listed as an Admin on the same screen, and when they also held an explicit row it deleted a grant the derived one immediately replaced — which could drop their org membership and seat, since the seat reconciliation counts rows only.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview API: Data: Member payloads add UI: Reviewed by Cursor Bugbot for commit 19bda02. Configure here. |
Greptile SummaryThe PR prevents removal of organization-derived workspace administrators and aligns workspace membership payloads and UI controls with that server behavior.
Confidence Score: 5/5The PR appears safe to merge because no blocking failure remains. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/app/api/workspaces/members/[id]/route.ts | Authorizes the caller before target-specific checks and rejects removal of organization-derived administrators. |
| apps/sim/lib/workspaces/permissions/utils.ts | Extends workspace member permission data with organization-admin standing. |
| apps/sim/components/permissions/role-lock.tsx | Adds a client-side removal lock reason matching server-side membership restrictions. |
| apps/sim/app/workspace/[workspaceId]/settings/components/teammates/teammates.tsx | Disables removal actions for organization administrators and billing accounts. |
| apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx | Hides unavailable leave actions for derived organization administrators and surfaces server errors. |
Reviews (2): Last reviewed commit: "fix(workspaces): stop offering leave to ..." | Re-trigger Greptile
Sidebar Leave was still offered to non-owner organization admins, whose access is derived and cannot be given up, and the confirm modal swallowed the refusal — so it sat open with no reason shown. The workspaces list now reports whether the viewer's admin access came from their org role, which `permissions: 'admin'` alone could not distinguish from an explicit grant. Also folds a disabled row action's tooltip into its accessible name, since Radix skips disabled items in a menu's roving focus.
|
@cursor review |
There was a problem hiding this comment.
✅ 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 19bda02. Configure here.
Summary
User not found in workspace— they hold workspace admin through their org role, not apermissionsrow, so there was never a row to findDELETE /api/workspaces/members/[id]now returns a 400 naming the actual reason: organization admins are automatically workspace admins, change their organization role to remove thempermissionsrows only, removing an org admin from their last org workspace could drop their org membership and seat while they stayed a workspace adminRemovethe server will refuse: newworkspaceMemberRemovalLockReasonmirrors the two server guards and renders the item disabled with the reason as a tooltipisOrgAdminto the workspace member payload.roleSourceranksowneraboveorg-admin, so it can't answer for a workspace owner who is also an org admin — the one case the client previously couldn't seeType of Change
Testing
Tested manually.
tsc --noEmitclean,bun run lint,check:audits(29 audits) andcheck:block-registrypass,lib/workspaces/permissions/utils.test.ts64/64 — updated the exact-shape assertion and added a case pinning that an owner who is also an org admin keepsroleSource: 'owner'and still reportsisOrgAdmin: true.Checklist