improvement(permissions): confine workspace role changes to existing members - #6180
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview The handler is hardened for concurrency: ordered APIs and UI stay aligned: roster and workspace member payloads add Reviewed by Cursor Bugbot for commit 21820e0. Configure here. |
Greptile SummaryThe PR confines workspace role changes to existing members and hardens concurrent permission updates.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/app/api/workspaces/[id]/permissions/route.ts | Restricts role changes to existing permission rows and revalidates all state-dependent authorization constraints under ordered locks. |
| apps/sim/lib/db/transaction.ts | Adds bounded transaction retries for serialization failures, deadlocks, and lock timeouts. |
| apps/sim/lib/api/contracts/workspaces.ts | Updates the permission mutation contract to validate bounded, unique member-role changes and the acknowledgement response. |
| apps/sim/app/api/organizations/[id]/roster/route.ts | Adds role-source and billing-account metadata needed to lock immutable role controls. |
| apps/sim/components/permissions/role-lock.tsx | Centralizes UI role-lock behavior for owners, organization administrators, and billing accounts. |
| apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts | Correctly distinguishes concurrent membership creation from conflict-driven role updates in responses and audit events. |
Sequence Diagram
sequenceDiagram
participant Admin
participant Route as Permissions API
participant DB as PostgreSQL
Admin->>Route: PATCH existing member roles
Route->>Route: Authenticate and validate request
Route->>DB: Read membership and workspace context
Route->>DB: Begin transaction and set lock timeout
Route->>DB: Lock org member, workspace, and permission rows
Route->>Route: Revalidate authority and protected roles
alt Membership and authority remain valid
Route->>DB: Update existing permission rows
DB-->>Route: Commit
Route-->>Admin: 200 acknowledgement
else State changed concurrently
DB-->>Route: Roll back
Route-->>Admin: 409 conflict
else Lock contention
Route->>DB: Retry in a fresh transaction
Route-->>Admin: 409 if retries are exhausted
end
Reviews (2): Last reviewed commit: "improvement(permissions): retry lock tim..." | Re-trigger Greptile
… truthfully Bounding the row-lock wait with `lock_timeout` made a competing writer raise 55P03, which the retry set excluded — so the bounded wait failed the request where the unbounded one would have waited. 55P03 is now retried like the other contention aborts, the timeout is shortened since each attempt releases its pooled connection, and contention that outlives every attempt answers with a busy conflict instead of a generic server error. The admin member upsert recorded MEMBER_ADDED even when the conflict branch amended an existing membership, putting a join that never happened in the workspace audit trail. It now records a role change, matching the action the response already reported. Adds the missing test coverage for withTransactionRetry. Co-Authored-By: Claude <noreply@anthropic.com>
ab7f6b0 to
21820e0
Compare
|
@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 21820e0. Configure here.
Summary
Fixes a workspace-access bypass. PATCH /api/workspaces/[id]/permissions accepted arbitrary userIds and inserted permission rows for them, letting any workspace admin attach an account — and, through the credential sync this route performs, hand it the workspace’s secrets — without the invitation flow’s plan check, seat provisioning, or invitee consent. The write is now a conditional UPDATE of an existing row so introducing a member is structurally unreachable, and every state-dependent guard (owner, organization admins, billing account, the caller’s own authority) is re-evaluated under lock, since an admin could otherwise drive an ownership transfer against their own in-flight batch and strand a user as workspace owner with read. Reviewer-visible changes: permissions.createdAt now means join time rather than last-role-change (documented on the admin members endpoint), the PATCH response is an acknowledgement only, and the owner/billing-account role controls are disabled in the members lists — all three were offering or reporting something the server never honored.
Type of Change
Testing
Tests added
Checklist