feat(enterprise): implement organization "disable workspace sharing" option (#21376)

Adds a per-organization setting to disable workspace sharing. When enabled,
all existing workspace ACLs in the organization are cleared and the workspace
ACL mutation API endpoints return `403 Forbidden`.

This complements the existing site-wide `--disable-workspace-sharing` flag by
providing more granular control at the organization level.

Closes https://github.com/coder/internal/issues/1073 (part 2)

---------

Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
This commit is contained in:
George K
2026-01-14 09:47:50 -08:00
committed by GitHub
parent 7d5cd06f83
commit 0712faef4f
30 changed files with 1134 additions and 3 deletions
+29
View File
@@ -2344,6 +2344,10 @@ func (api *API) patchWorkspaceACL(rw http.ResponseWriter, r *http.Request) {
defer commitAudit()
aReq.Old = workspace.WorkspaceTable()
if !api.allowWorkspaceSharing(ctx, rw, workspace.OrganizationID) {
return
}
var req codersdk.UpdateWorkspaceACL
if !httpapi.Read(ctx, rw, r, &req) {
return
@@ -2440,6 +2444,10 @@ func (api *API) deleteWorkspaceACL(rw http.ResponseWriter, r *http.Request) {
defer commitAuditor()
aReq.Old = workspace.WorkspaceTable()
if !api.allowWorkspaceSharing(ctx, rw, workspace.OrganizationID) {
return
}
err := api.Database.InTx(func(tx database.Store) error {
err := tx.DeleteWorkspaceACLByID(ctx, workspace.ID)
if err != nil {
@@ -2463,6 +2471,27 @@ func (api *API) deleteWorkspaceACL(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusNoContent, nil)
}
// allowWorkspaceSharing enforces the workspace-sharing gate for an
// organization. It writes an HTTP error response and returns false if
// sharing is disabled or the org lookup fails; otherwise it returns
// true.
func (api *API) allowWorkspaceSharing(ctx context.Context, rw http.ResponseWriter, organizationID uuid.UUID) bool {
//nolint:gocritic // Use system context so this check doesnt
// depend on the caller having organization:read.
org, err := api.Database.GetOrganizationByID(dbauthz.AsSystemRestricted(ctx), organizationID)
if err != nil {
httpapi.InternalServerError(rw, err)
return false
}
if org.WorkspaceSharingDisabled {
httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{
Message: "Workspace sharing is disabled for this organization.",
})
return false
}
return true
}
// workspacesData only returns the data the caller can access. If the caller
// does not have the correct perms to read a given template, the template will
// not be returned.