fix: allow users with workspace:create for any owner to list users (#21947)

## Summary

Custom roles that can create workspaces on behalf of other users need to
be able to list users to populate the owner dropdown in the workspace
creation UI. Previously, this required a separate `user:read`
permission, causing the dropdown to fail for custom roles.

## Changes

- Modified `GetUsers` in `dbauthz` to check if the user can create
workspaces for any owner (`workspace:create` with `owner_id: *`)
- If the user has this permission, they can list all users without
needing explicit `user:read` permission
- Added tests to verify the new behavior

## Testing

- Updated mock tests to assert the new authorization check
- Added integration tests for both positive and negative cases

Fixes #18203
This commit is contained in:
Garrett Delfosse
2026-02-19 13:04:53 -05:00
committed by GitHub
parent 911d734df9
commit e8d6016807
13 changed files with 388 additions and 16 deletions
+45
View File
@@ -2952,3 +2952,48 @@ func convertToWorkspaceRole(actions []policy.Action) codersdk.WorkspaceRole {
return codersdk.WorkspaceRoleDeleted
}
// @Summary Get users available for workspace creation
// @ID get-users-available-for-workspace-creation
// @Security CoderSessionToken
// @Produce json
// @Tags Workspaces
// @Param organization path string true "Organization ID" format(uuid)
// @Param user path string true "User ID, name, or me"
// @Param q query string false "Search query"
// @Param limit query int false "Limit results"
// @Param offset query int false "Offset for pagination"
// @Success 200 {array} codersdk.MinimalUser
// @Router /organizations/{organization}/members/{user}/workspaces/available-users [get]
func (api *API) workspaceAvailableUsers(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
organization := httpmw.OrganizationParam(r)
// This endpoint requires the user to be able to create workspaces for other
// users in this organization. We check if they can create a workspace with
// a wildcard owner.
if !api.Authorize(r, policy.ActionCreate, rbac.ResourceWorkspace.InOrg(organization.ID).WithOwner(policy.WildcardSymbol)) {
httpapi.Forbidden(rw)
return
}
// Use system context to list all users. The authorization check above
// ensures only users who can create workspaces for others can access this.
//nolint:gocritic // System context needed to list users for workspace owner selection.
users, _, ok := api.GetUsers(rw, r.WithContext(dbauthz.AsSystemRestricted(ctx)))
if !ok {
return
}
minimalUsers := make([]codersdk.MinimalUser, 0, len(users))
for _, user := range users {
minimalUsers = append(minimalUsers, codersdk.MinimalUser{
ID: user.ID,
Username: user.Username,
Name: user.Name,
AvatarURL: user.AvatarURL,
})
}
httpapi.Write(ctx, rw, http.StatusOK, minimalUsers)
}