Skip to content

fix(workspaces): explain why org admins can't be removed from a workspace - #6838

Merged
icecrasher321 merged 2 commits into
stagingfrom
douala
Aug 19, 2026
Merged

fix(workspaces): explain why org admins can't be removed from a workspace#6838
icecrasher321 merged 2 commits into
stagingfrom
douala

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

  • Removing an org admin from a workspace failed with User not found in workspace — they hold workspace admin through their org role, not a permissions row, so there was never a row to find
  • DELETE /api/workspaces/members/[id] now returns a 400 naming the actual reason: organization admins are automatically workspace admins, change their organization role to remove them
  • The guard fires whether or not they also hold an explicit row. That case was quietly worse than the 404: it deleted a grant the derived one immediately replaced, and because the seat reconciliation counts permissions rows only, removing an org admin from their last org workspace could drop their org membership and seat while they stayed a workspace admin
  • Moved the admin/self authorization check above the target lookups so the new standing-specific reply can't be used to probe org membership for an arbitrary userId — same oracle concern the permissions PATCH route documents
  • Teammates rows no longer offer a Remove the server will refuse: new workspaceMemberRemovalLockReason mirrors the two server guards and renders the item disabled with the reason as a tooltip
  • Added isOrgAdmin to the workspace member payload. roleSource ranks owner above org-admin, so it can't answer for a workspace owner who is also an org admin — the one case the client previously couldn't see

Type of Change

  • Bug fix

Testing

Tested manually. tsc --noEmit clean, bun run lint, check:audits (29 audits) and check:block-registry pass, lib/workspaces/permissions/utils.test.ts 64/64 — updated the exact-shape assertion and added a case pinning that an owner who is also an org admin keeps roleSource: 'owner' and still reports isOrgAdmin: true.

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)

…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.
@vercel

vercel Bot commented Aug 19, 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 Aug 19, 2026 3:15am

Request Review

@cursor

cursor Bot commented Aug 19, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches member removal authorization order and seat-reconciliation-adjacent paths for org admins; changes are guarded and mirrored client/server, but incorrect pairing could still allow removal or block legitimate leaves.

Overview
Fixes incorrect 404 "User not found in workspace" when removing organization admins, who get workspace admin through org role rather than a permissions row.

API: DELETE /api/workspaces/members/[id] now returns 400 with a clear message that org admins must change organization role to leave or be removed. The guard runs even when an explicit permission row exists, avoiding a no-op delete that could still affect seat reconciliation. Admin/self authorization is checked before target lookups to avoid probing org membership.

Data: Member payloads add isOrgAdmin (separate from roleSource, which ranks owner above org-admin). Workspace listing propagates viaOrgAdminisOrgAdmin so the sidebar can distinguish derived admin from explicit workspace admin.

UI: workspaceMemberRemovalLockReason mirrors server guards; Teammates shows Remove disabled with tooltip (and RowActionsMenu folds tooltip into aria-label for assistive tech). Org admins no longer see Leave in the workspace switcher context menu; failed self-leave surfaces a toast with the server error.

Reviewed by Cursor Bugbot for commit 19bda02. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR prevents removal of organization-derived workspace administrators and aligns workspace membership payloads and UI controls with that server behavior.

  • Moves removal authorization ahead of target-specific responses.
  • Adds organization-admin membership metadata and removal lock reasons.
  • Improves leave-workspace error feedback and updates permission utilities and tests.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

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

Comment thread apps/sim/app/api/workspaces/members/[id]/route.ts
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.
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

@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 19bda02. Configure here.

@icecrasher321
icecrasher321 merged commit d909889 into staging Aug 19, 2026
30 checks passed
@waleedlatif1
waleedlatif1 deleted the douala branch August 19, 2026 05:04
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