improvement(access-controls): default workspace experience includes all members#5153
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryHigh Risk Overview Only the organization default group may be org-wide. Non-default “all workspaces” scope is removed from APIs/contracts/UI, and a data migration backfills legacy non-default all-workspaces groups (member-bearing groups get every org workspace linked; empty groups stay inert). Governing-group resolution changes to: explicit member on a workspace group → all-members workspace group → org default → unrestricted. Conflict checks now block duplicate all-members groups per workspace and overlapping explicit memberships; removing the last member is transactional and can 409 if another all-members group already exists. Access Control UI moves member management into the permissions modal, requires workspaces on create, and updates list copy. Docs and tests follow the new rules. Reviewed by Cursor Bugbot for commit 2bc256d. Configure here. |
|
bugbot run |
|
@greptile |
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 2bc256d. Configure here.
|
@greptile |
Greptile SummaryThis PR changes the default experience for permission groups: a non-default group now governs all members of its assigned workspaces unless explicit members are added, replacing the old model where groups started from the member list and an empty group governed nobody. A data migration handles existing groups (pinning member-bearing all-workspaces groups to their current workspaces, clearing workspace associations for zero-member groups so they stay inert).
Confidence Score: 5/5Safe to merge. All write paths acquire the org advisory lock before the all-members conflict check, the migration correctly idles zero-member groups before the code goes live, and the schema contract rejects the forbidden all-workspaces state for non-default groups before it reaches any route. The model change is a significant shift (empty group = all-members vs. governs nobody), but every write path that can produce the new state has a corresponding conflict check, the migration handles pre-existing data in all three cases, and the resolver's fallback order is clean and deterministic. No unguarded path was found that would silently widen or narrow access. No files require special attention. The migration, route handlers, resolver, and UI changes are internally consistent. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[resolveWorkspaceGroup] --> B[Query all non-default groups targeting workspaceId]
B --> C{isMember = true?}
C -- Yes --> D[Winner = explicit-member group]
C -- No --> E{hasMembers = false?}
E -- Yes --> F[Winner = all-members group]
E -- No --> G[resolveDefaultGroup]
D --> H[Return group config]
F --> H
G --> H
%%{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[resolveWorkspaceGroup] --> B[Query all non-default groups targeting workspaceId]
B --> C{isMember = true?}
C -- Yes --> D[Winner = explicit-member group]
C -- No --> E{hasMembers = false?}
E -- Yes --> F[Winner = all-members group]
E -- No --> G[resolveDefaultGroup]
D --> H[Return group config]
F --> H
G --> H
Reviews (4): Last reviewed commit: "address zero-member edge case" | Re-trigger Greptile |
Greptile SummaryThis PR redesigns the permission-group model so that a non-default group governs all members of its targeted workspaces by default (rather than only the members explicitly added to it), and narrows to specific people only once members are added. Only the organization's default group remains org-wide.
Confidence Score: 4/5The access-control logic and migration are correct; the one cosmetic issue (workspace name in both row slots) does not affect permissions enforcement. The model change is broad but well-tested, with unit tests covering the new isMember/hasMembers resolution paths and the new conflict checks. The migration is data-only, idempotent, and handles the empty-group edge case carefully. The only concrete defect found is cosmetic: workspaces in the detail view are rendered via MemberRow with email set to the workspace name, causing the name to appear in both label and subtitle positions. apps/sim/ee/access-control/components/access-control.tsx — the MemberRow email field for workspace rows; apps/sim/ee/access-control/utils/permission-check.ts — the new resolveWorkspaceGroup query shape is the heart of the model change and worth a close read. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[resolveWorkspaceGroup called
userId + workspaceId] --> B[Query: all non-default groups
targeting workspaceId
with isMember + hasMembers EXISTS]
B --> C{rows.find row.isMember?}
C -- yes --> D[Return: explicit-member group]
C -- no --> E{rows.find !row.hasMembers?}
E -- yes --> F[Return: all-members group
no explicit members]
E -- no --> G[resolveDefaultGroup]
G --> H{default group exists?}
H -- yes --> I[Return: org default group]
H -- no --> J[Return: null
unrestricted]
subgraph Conflict Guards
K[Create group / Update workspaces
with 0 members] --> L[findAllMembersWorkspaceConflict
check per workspace]
M[Remove last member
from group] --> L
N[Add member to group] --> O[findScopeConflicts
explicit-member overlap check]
end
%%{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[resolveWorkspaceGroup called
userId + workspaceId] --> B[Query: all non-default groups
targeting workspaceId
with isMember + hasMembers EXISTS]
B --> C{rows.find row.isMember?}
C -- yes --> D[Return: explicit-member group]
C -- no --> E{rows.find !row.hasMembers?}
E -- yes --> F[Return: all-members group
no explicit members]
E -- no --> G[resolveDefaultGroup]
G --> H{default group exists?}
H -- yes --> I[Return: org default group]
H -- no --> J[Return: null
unrestricted]
subgraph Conflict Guards
K[Create group / Update workspaces
with 0 members] --> L[findAllMembersWorkspaceConflict
check per workspace]
M[Remove last member
from group] --> L
N[Add member to group] --> O[findScopeConflicts
explicit-member overlap check]
end
Reviews (2): Last reviewed commit: "improve copy" | Re-trigger Greptile |
Greptile SummaryThis PR changes the default experience for workspace-scoped permission groups from member-keyed (governs only explicitly added members) to all-members (governs everyone in the targeted workspaces by default, narrowed to specific people once members are added). The permission resolver, conflict checks, contract validation, migration, and UI are all updated together to implement and enforce this new semantic.
Confidence Score: 3/5The resolver rewrite and new conflict guards are internally consistent, but the migration leaves a gap that could cause unexpected permission restrictions for existing customers. Existing non-default workspace-specific groups with zero members were previously inert under the member-keyed resolver. Under the new resolver they match !row.hasMembers and immediately govern everyone in their workspaces. Any org with such a group will see unexpected restrictions applied to all workspace members post-deploy without admin intent. packages/db/migrations/0246_convert_grandfathered_all_ws_permission_groups.sql needs a step to clear workspace associations for zero-member non-default workspace-specific groups. apps/sim/ee/access-control/components/access-control.tsx has a minor display issue with email={ws.name} in the workspace list. Important Files Changed
Reviews (3): Last reviewed commit: "improve copy" | Re-trigger Greptile |
|
@greptile |
Summary
Selecting workspaces includes all members as the default experience. If members already selected -- then you can still configure them in the Configure modal in the members tab.
Type of Change
Testing
Tested manually
Checklist