From 489dbba534c299911983d489224db0a35bd72f2d Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 13 Jun 2025 16:22:23 +0000 Subject: [PATCH 1/5] fix: add workspace owner id as query param to websocket --- site/src/api/api.ts | 3 ++- .../WorkspaceParametersPageExperimental.tsx | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 28807bd547c2a..cb865691063e5 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1017,6 +1017,7 @@ class ApiMethods { templateVersionDynamicParameters = ( versionId: string, + userId: string, { onMessage, onError, @@ -1028,7 +1029,7 @@ class ApiMethods { }, ): WebSocket => { const socket = createWebSocket( - `/api/v2/templateversions/${versionId}/dynamic-parameters`, + `/api/v2/templateversions/${versionId}/dynamic-parameters?user_id=${userId}`, ); socket.addEventListener("message", (event) => diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPageExperimental.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPageExperimental.tsx index 755291ec28629..68340ddad5e05 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPageExperimental.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPageExperimental.tsx @@ -111,6 +111,7 @@ const WorkspaceParametersPageExperimental: FC = () => { const socket = API.templateVersionDynamicParameters( templateVersionId ?? workspace.latest_build.template_version_id, + workspace.owner_id, { onMessage, onError: (error) => { @@ -140,6 +141,7 @@ const WorkspaceParametersPageExperimental: FC = () => { templateVersionId, workspace.latest_build.template_version_id, onMessage, + workspace.owner_id, ]); const updateParameters = useMutation({ From a159e1401e78879b51a718f5212d0e30da0ca3d1 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 13 Jun 2025 11:37:03 -0500 Subject: [PATCH 2/5] add unit test for user_id query param --- coderd/parameters.go | 16 +++++++++++++++- coderd/parameters_test.go | 4 ++-- codersdk/parameters.go | 13 +++++++++++-- enterprise/coderd/parameters_test.go | 20 +++++++++++++++++++- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/coderd/parameters.go b/coderd/parameters.go index d8551b2031f7a..f4bcd21743dab 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -58,11 +58,25 @@ func (api *API) templateVersionDynamicParametersEvaluate(rw http.ResponseWriter, // @Router /templateversions/{templateversion}/dynamic-parameters [get] func (api *API) templateVersionDynamicParametersWebsocket(rw http.ResponseWriter, r *http.Request) { apikey := httpmw.APIKey(r) + userID := apikey.UserID + + qUserID := r.URL.Query().Get("user_id") + if qUserID != "" { + uid, err := uuid.Parse(qUserID) + if err != nil { + httpapi.Write(r.Context(), rw, http.StatusBadRequest, codersdk.Response{ + Message: "Invalid user_id query parameter", + Detail: err.Error(), + }) + return + } + userID = uid + } api.templateVersionDynamicParameters(true, codersdk.DynamicParametersRequest{ ID: -1, Inputs: map[string]string{}, - OwnerID: apikey.UserID, + OwnerID: userID, })(rw, r) } diff --git a/coderd/parameters_test.go b/coderd/parameters_test.go index 640dc3ad22e55..3c792c2ce9a7a 100644 --- a/coderd/parameters_test.go +++ b/coderd/parameters_test.go @@ -56,7 +56,7 @@ func TestDynamicParametersOwnerSSHPublicKey(t *testing.T) { _ = coderdtest.CreateTemplate(t, templateAdmin, owner.OrganizationID, version.ID) ctx := testutil.Context(t, testutil.WaitShort) - stream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, version.ID) + stream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, codersdk.Me, version.ID) require.NoError(t, err) defer stream.Close(websocket.StatusGoingAway) @@ -387,7 +387,7 @@ func setupDynamicParamsTest(t *testing.T, args setupDynamicParamsTestParams) dyn require.NoError(t, err) ctx := testutil.Context(t, testutil.WaitShort) - stream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, version.ID) + stream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, codersdk.Me, version.ID) if args.expectWebsocketError { require.Errorf(t, err, "expected error forming websocket") } else { diff --git a/codersdk/parameters.go b/codersdk/parameters.go index 035537d34259e..dbbe3cdbd2fc7 100644 --- a/codersdk/parameters.go +++ b/codersdk/parameters.go @@ -125,8 +125,17 @@ type DynamicParametersResponse struct { // TODO: Workspace tags } -func (c *Client) TemplateVersionDynamicParameters(ctx context.Context, version uuid.UUID) (*wsjson.Stream[DynamicParametersResponse, DynamicParametersRequest], error) { - conn, err := c.Dial(ctx, fmt.Sprintf("/api/v2/templateversions/%s/dynamic-parameters", version), nil) +func (c *Client) TemplateVersionDynamicParameters(ctx context.Context, userID string, version uuid.UUID) (*wsjson.Stream[DynamicParametersResponse, DynamicParametersRequest], error) { + endpoint := fmt.Sprintf("/api/v2/templateversions/%s/dynamic-parameters", version) + if userID != Me { + uid, err := uuid.Parse(userID) + if err != nil { + return nil, fmt.Errorf("invalid user ID: %w", err) + } + endpoint += fmt.Sprintf("?user_id=%s", uid.String()) + } + + conn, err := c.Dial(ctx, endpoint, nil) if err != nil { return nil, err } diff --git a/enterprise/coderd/parameters_test.go b/enterprise/coderd/parameters_test.go index 5fc0eaa4aa369..93f5057206527 100644 --- a/enterprise/coderd/parameters_test.go +++ b/enterprise/coderd/parameters_test.go @@ -32,6 +32,7 @@ func TestDynamicParametersOwnerGroups(t *testing.T) { }, ) templateAdmin, templateAdminUser := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin()) + _, noGroupUser := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) // Create the group to be asserted group := coderdtest.CreateGroup(t, ownerClient, owner.OrganizationID, "bloob", templateAdminUser) @@ -57,7 +58,24 @@ func TestDynamicParametersOwnerGroups(t *testing.T) { _ = coderdtest.CreateTemplate(t, templateAdmin, owner.OrganizationID, version.ID) ctx := testutil.Context(t, testutil.WaitShort) - stream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, version.ID) + + // First check with a no group admin user, that they do not see the extra group + // Use the admin client, as the user might not have access to the template. + // Also checking that the admin can see the form for the other user. + noGroupStream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, noGroupUser.ID.String(), version.ID) + require.NoError(t, err) + defer noGroupStream.Close(websocket.StatusGoingAway) + noGroupPreviews := noGroupStream.Chan() + noGroupPreview := testutil.RequireReceive(ctx, t, noGroupPreviews) + require.Equal(t, -1, noGroupPreview.ID) + require.Empty(t, noGroupPreview.Diagnostics) + require.Equal(t, "group", noGroupPreview.Parameters[0].Name) + require.Equal(t, database.EveryoneGroup, noGroupPreview.Parameters[0].Value.Value) + require.Equal(t, 1, len(noGroupPreview.Parameters[0].Options)) // Only 1 group + noGroupStream.Close(websocket.StatusGoingAway) + + // Now try with a user with more than 1 group + stream, err := templateAdmin.TemplateVersionDynamicParameters(ctx, codersdk.Me, version.ID) require.NoError(t, err) defer stream.Close(websocket.StatusGoingAway) From 6f1d50adba9354b5fd536100009ea8b3efd455d2 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 13 Jun 2025 11:56:53 -0500 Subject: [PATCH 3/5] fix create workspace page --- coderd/parameters.go | 2 +- .../CreateWorkspacePage/CreateWorkspacePageExperimental.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/parameters.go b/coderd/parameters.go index f4bcd21743dab..48cccc27e6727 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -61,7 +61,7 @@ func (api *API) templateVersionDynamicParametersWebsocket(rw http.ResponseWriter userID := apikey.UserID qUserID := r.URL.Query().Get("user_id") - if qUserID != "" { + if qUserID != "" && qUserID != codersdk.Me { uid, err := uuid.Parse(qUserID) if err != nil { httpapi.Write(r.Context(), rw, http.StatusBadRequest, codersdk.Response{ diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx index cf0e80d592cd6..5c13dc9921b25 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx @@ -148,7 +148,7 @@ const CreateWorkspacePageExperimental: FC = () => { useEffect(() => { if (!realizedVersionId) return; - const socket = API.templateVersionDynamicParameters(realizedVersionId, { + const socket = API.templateVersionDynamicParameters(realizedVersionId, "me", { onMessage, onError: (error) => { if (ws.current === socket) { From ed17eaf7038a9c2da2c2826aec6cd3f05d3ccda1 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 13 Jun 2025 12:04:41 -0500 Subject: [PATCH 4/5] fmt --- codersdk/parameters.go | 3 +- site/src/api/api.ts | 3 +- .../CreateWorkspacePageExperimental.tsx | 38 ++++++++++--------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/codersdk/parameters.go b/codersdk/parameters.go index dbbe3cdbd2fc7..bdf48f2c6e8fa 100644 --- a/codersdk/parameters.go +++ b/codersdk/parameters.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/uuid" + "golang.org/x/xerrors" "github.com/coder/coder/v2/codersdk/wsjson" "github.com/coder/websocket" @@ -130,7 +131,7 @@ func (c *Client) TemplateVersionDynamicParameters(ctx context.Context, userID st if userID != Me { uid, err := uuid.Parse(userID) if err != nil { - return nil, fmt.Errorf("invalid user ID: %w", err) + return nil, xerrors.Errorf("invalid user ID: %w", err) } endpoint += fmt.Sprintf("?user_id=%s", uid.String()) } diff --git a/site/src/api/api.ts b/site/src/api/api.ts index cb865691063e5..5b7cde65fb2ce 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1029,7 +1029,8 @@ class ApiMethods { }, ): WebSocket => { const socket = createWebSocket( - `/api/v2/templateversions/${versionId}/dynamic-parameters?user_id=${userId}`, + `/api/v2/templateversions/${versionId}/dynamic-parameters`, + new URLSearchParams({ user_id: userId }), ); socket.addEventListener("message", (event) => diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx index 5c13dc9921b25..da9c638e55e25 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx @@ -148,24 +148,28 @@ const CreateWorkspacePageExperimental: FC = () => { useEffect(() => { if (!realizedVersionId) return; - const socket = API.templateVersionDynamicParameters(realizedVersionId, "me", { - onMessage, - onError: (error) => { - if (ws.current === socket) { - setWsError(error); - } - }, - onClose: () => { - if (ws.current === socket) { - setWsError( - new DetailedError( - "Websocket connection for dynamic parameters unexpectedly closed.", - "Refresh the page to reset the form.", - ), - ); - } + const socket = API.templateVersionDynamicParameters( + realizedVersionId, + "me", + { + onMessage, + onError: (error) => { + if (ws.current === socket) { + setWsError(error); + } + }, + onClose: () => { + if (ws.current === socket) { + setWsError( + new DetailedError( + "Websocket connection for dynamic parameters unexpectedly closed.", + "Refresh the page to reset the form.", + ), + ); + } + }, }, - }); + ); ws.current = socket; From b7054e51f8b13d11a8925ed597bfedd9823c0c0b Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 13 Jun 2025 17:22:02 +0000 Subject: [PATCH 5/5] chore: use defaultowner id --- .../CreateWorkspacePage/CreateWorkspacePageExperimental.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx index da9c638e55e25..070576a5e9a99 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx @@ -150,7 +150,7 @@ const CreateWorkspacePageExperimental: FC = () => { const socket = API.templateVersionDynamicParameters( realizedVersionId, - "me", + defaultOwner.id, { onMessage, onError: (error) => { @@ -176,7 +176,7 @@ const CreateWorkspacePageExperimental: FC = () => { return () => { socket.close(); }; - }, [realizedVersionId, onMessage]); + }, [realizedVersionId, onMessage, defaultOwner.id]); const organizationId = templateQuery.data?.organization_id;