fix: make chat model sharing work for sharers without directory access - #28542
Conversation
Docs previewCheck off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 475be16b86
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
970041e to
87f752b
Compare
87f752b to
7da6bf8
Compare
7da6bf8 to
73e9d6b
Compare
73e9d6b to
19782f1
Compare
## Summary Adds the missing user-facing half of MCP server config ACLs: admins can now share an MCP server with individual users and groups from the UI, backed by a new permission-safe candidate-discovery endpoint. ## Problem The MCP server config ACL backend already existed (share RBAC action, hydrated `GET .../acl`, sparse `PATCH .../acl`), but there was no frontend for it. There was also no way to populate a sharing autocomplete without the generic organization member/group APIs, which require `organization_member:read` and `group:read` and would wrongly couple MCP sharing to the workspace-sharing mode (the same bug fixed for chat models in #28542). ## Changes Backend: - New v2-only `GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl/available` returning `codersdk.ACLAvailable`; because this API is new, it is not mounted under the experimental compatibility prefix. Gated on `ActionShare` for the specific server config; performs bounded org-scoped member/group lookups via `dbauthz.AsSystemRestricted`; excludes system users; supports `q`, `limit`, `offset`, and `after_id` with template/chat-model autocomplete semantics. - Tests cover authorization (404 without share), search, pagination, system-user exclusion, org scoping, and a share-only custom role under all three workspace-sharing modes (`none`, `service_accounts`, `everyone`). Frontend: - `shareMCPServerConfig` permission plumbing; the MCP servers list, details page, and AI settings sidebar now admit share-only users (share does not require update; share-only users cannot edit the server form). - Share-only access follows the established permission contract: a bare-share role manages sharing through the list and ACL endpoints (the detail route still requires read, update, or delete, matching the existing enterprise permission-matrix tests), and the disabled-config gate now also admits the share permission so a sharer with read access can open a disabled server. Top-level navigation (Admin settings menu and the /ai/settings index redirect) discovers organization-level MCP sharers and surfaces permission lookup failures instead of silently falling back. - "Share server" action on the MCP server edit page opening a sharing dialog: hydrated ACL grants render from the `GET .../acl` response, adds/removals are saved as sparse `PATCH` deltas (`"read"` / `""`). - Principal autocomplete backed exclusively by the new `/acl/available` endpoint; the sharing flow never calls the generic org member/group APIs (Storybook enforces this by rejecting those API spies). A discovery error surfaces under the autocomplete without hiding existing grants. - Storybook interaction coverage for the dialog (hydrated rendering, add/remove, sparse deltas, error/cancel/reopen paths, autocomplete exclusion and failure) and share-only access to the page/form. Dogfood UAT ran remotely against a dev instance and passed, including verifying via the network log that candidate discovery only hits `/acl/available`. The share-only custom role UI flow could not be exercised there (no premium license on the dev instance); it is covered by the backend permission-matrix tests. > 🤖 This PR was authored by Xum (an AI coding agent) acting on Mike's behalf.
|
@codex review\n\nPlease review current head .\n\n> Xum acted on Mike'''s behalf.\n |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 19782f10f5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Pushed @codex review Please review current head
|
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Merged The merge also carries the @codex review Please review current head
|
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
## Summary Adds the missing user-facing half of MCP server config ACLs: admins can now share an MCP server with individual users and groups from the UI, backed by a new permission-safe candidate-discovery endpoint. ## Problem The MCP server config ACL backend already existed (share RBAC action, hydrated `GET .../acl`, sparse `PATCH .../acl`), but there was no frontend for it. There was also no way to populate a sharing autocomplete without the generic organization member/group APIs, which require `organization_member:read` and `group:read` and would wrongly couple MCP sharing to the workspace-sharing mode (the same bug fixed for chat models in #28542). ## Changes Backend: - New v2-only `GET /api/v2/organizations/{organization}/mcp-servers/{mcpserverconfig}/acl/available` returning `codersdk.ACLAvailable`; because this API is new, it is not mounted under the experimental compatibility prefix. Gated on `ActionShare` for the specific server config; performs bounded org-scoped member/group lookups via `dbauthz.AsSystemRestricted`; excludes system users; supports `q`, `limit`, `offset`, and `after_id` with template/chat-model autocomplete semantics. - Tests cover authorization (404 without share), search, pagination, system-user exclusion, org scoping, and a share-only custom role under all three workspace-sharing modes (`none`, `service_accounts`, `everyone`). Frontend: - `shareMCPServerConfig` permission plumbing; the MCP servers list, details page, and AI settings sidebar now admit share-only users (share does not require update; share-only users cannot edit the server form). - Share-only access follows the established permission contract: a bare-share role manages sharing through the list and ACL endpoints (the detail route still requires read, update, or delete, matching the existing enterprise permission-matrix tests), and the disabled-config gate now also admits the share permission so a sharer with read access can open a disabled server. Top-level navigation (Admin settings menu and the /ai/settings index redirect) discovers organization-level MCP sharers and surfaces permission lookup failures instead of silently falling back. - "Share server" action on the MCP server edit page opening a sharing dialog: hydrated ACL grants render from the `GET .../acl` response, adds/removals are saved as sparse `PATCH` deltas (`"read"` / `""`). - Principal autocomplete backed exclusively by the new `/acl/available` endpoint; the sharing flow never calls the generic org member/group APIs (Storybook enforces this by rejecting those API spies). A discovery error surfaces under the autocomplete without hiding existing grants. - Storybook interaction coverage for the dialog (hydrated rendering, add/remove, sparse deltas, error/cancel/reopen paths, autocomplete exclusion and failure) and share-only access to the page/form. Dogfood UAT ran remotely against a dev instance and passed, including verifying via the network log that candidate discovery only hits `/acl/available`. The share-only custom role UI flow could not be exercised there (no premium license on the dev instance); it is covered by the backend permission-matrix tests. > 🤖 This PR was authored by Xum (an AI coding agent) acting on Mike's behalf. (cherry picked from commit 35cbca0)
01189c3 to
4aec269
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
The chat model sharing dialog previously required org member and group
directory reads that a delegated sharer role does not have, blanking
the dialog under restrictive workspace sharing modes and rendering
group grants as raw UUIDs.
Hydrate GET .../chats/models/{model}/acl with resolved users and
groups, and add GET .../chats/models/{model}/acl/available returning
assignable principals, both authorized solely via chat model
config share permission, mirroring templateAvailablePermissions.
The interface is only referenced inside debugPanelUtils.ts, so exporting it causes knip to report an unused export. Match the adjacent file-local view-model types.
4aec269 to
92597c2
Compare
Problem
This is a bug fix. Chat model sharing UAT (main @ 3c32408) found two bugs when a delegated "Model Sharer" role holds only
chat_model_config:read+chat_model_config:share:This state is reachable entirely through the UI in a normal least-privilege setup:
noneorservice_accounts.chat_model_config:readandchat_model_config:share.none, the sharing dialog errored out entirely ("Resource not found or you do not have access to this resource"). The sharer couldn't view, add, or even remove grants.service_accounts, existing group grants rendered as raw UUIDs, and groups didn't appear in the add-principal search.Both have the same cause. The ACL GET returned bare UUID→role maps, so to display names (and to power its add-principal search) the dialog called the generic org directory APIs: list organization members and list groups. Those APIs require
organization_member:readandgroup:read, which the sharer role doesn't have. Ordinary members only get them as a side effect of a different feature:OrgMemberPermissionsgrants member read when the org's workspace sharing mode isn'tnone(so users can pick coworkers to share a workspace with), and group read only when it'severyone. The chat dialog was silently borrowing those workspace-sharing permissions, so it only worked when that unrelated org setting happened to grant them:none: no member read → dbauthz rejects the member listing → the dialog's query chain 404s and everything blanks (UAT-05/08).service_accounts: member read but no group read → the group listing silently filters to empty → group grants can't be resolved past their UUIDs (UAT-04).In short: an org setting about who can share workspaces was deciding whether a delegated sharer could manage a chat model ACL.
Fix
Stop borrowing directory permissions entirely; authorize everything through the model's own share permission, using the two patterns the repo already has for ACL editors:
workspaceACLand the per-chat ACL already do. Fixes the raw UUIDs.GET .../acl/availablereturning assignable org members and groups for the autocomplete, modeled directly ontemplateAvailablePermissions(same query semantics, same share-gate-then-system-context lookup that every sibling ACL endpoint uses). Fixes candidate discovery. The new endpoint exists only because the dialog has no permission-safe way to enumerate principals today; it is plumbing for the fix, not a new feature.This is exactly how template sharing already solves the same problem:
templateACLreturns hydrated principals, andtemplateAvailablePermissionsgates on the template then does the lookups under a system context because, per its own comment, "the caller might not have permission to read all users". We differ from the template version only where newer conventions exist:ActionUpdateon the template (predatesshare)ActionShareon the model, matching the workspace/chat/MCP ACL endpointsGetUsersThe dialog now runs off the hydrated ACL plus the new endpoint, so all three sharing modes take the same code path, and a discovery failure no longer blanks existing grants.
OrgMemberPermissions, the directory APIs, and the PATCH (still a sparse UUID→role delta) are untouched.Diff breakdown
+1274/−191, but only about a third is product code:
exp_chats_model_acl.go, ~240), the dialog rework plus a new colocated autocomplete component (~200), and SDK/site API plumbing (~60).typesGenerated.ts, API reference docs.Depends on #28498 (stacked on the
/api/v2chat API promotion; the new endpoint is registered under both prefixes like its siblings).