From 38b12e99b74864da7c463c029f3b6acc46f83c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McKayla=20=E3=81=AF=E3=81=AA?= Date: Mon, 10 Aug 2026 19:07:00 +0000 Subject: [PATCH 1/3] fix(site/src/pages/WorkspacePage/WorkspaceActions): hide favorite button for non-owners Favorite is a column on the workspace row rather than a per-user relation, so the API rejects favorite and unfavorite requests from anyone other than the workspace owner. The button was rendered unconditionally, so viewing someone else's workspace offered a star that always looked un-favorited and failed with a 403 surfaced through the "Error building workspace" dialog. --- .../WorkspaceActions.stories.tsx | 27 +++++++++++++++++++ .../WorkspaceActions/WorkspaceActions.tsx | 14 ++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx index 0ec276605ca..2787789da31 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx @@ -211,6 +211,33 @@ export const CancelHiddenForUser: Story = { }, }; +export const FavoriteShownForOwner: Story = { + args: { + workspace: Mocks.MockWorkspace, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect( + canvas.getByRole("button", { name: "Favorite" }), + ).toBeVisible(); + }, +}; + +export const FavoriteHiddenForNonOwner: Story = { + args: { + workspace: Mocks.MockWorkspace, + }, + parameters: { + user: Mocks.MockUserMember, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect( + canvas.queryByRole("button", { name: "Favorite" }), + ).not.toBeInTheDocument(); + }, +}; + export const OpenDownloadLogs: Story = { args: { workspace: Mocks.MockWorkspace, diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx index 72ab7e6443b..cce8418905e 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx @@ -178,11 +178,15 @@ export const WorkspaceActions: FC = ({ {canCancel && } - + {/* Favorite status is stored on the workspace itself rather than per + user, so the API only lets the owner toggle it. */} + {user.id === workspace.owner_id && ( + + )} {permissions.shareWorkspace && ( Date: Mon, 10 Aug 2026 19:39:13 +0000 Subject: [PATCH 2/3] Update WorkspaceActions.stories.tsx --- .../WorkspaceActions.stories.tsx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx index 2787789da31..1ffca1a677d 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx @@ -211,18 +211,6 @@ export const CancelHiddenForUser: Story = { }, }; -export const FavoriteShownForOwner: Story = { - args: { - workspace: Mocks.MockWorkspace, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await expect( - canvas.getByRole("button", { name: "Favorite" }), - ).toBeVisible(); - }, -}; - export const FavoriteHiddenForNonOwner: Story = { args: { workspace: Mocks.MockWorkspace, @@ -230,12 +218,6 @@ export const FavoriteHiddenForNonOwner: Story = { parameters: { user: Mocks.MockUserMember, }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await expect( - canvas.queryByRole("button", { name: "Favorite" }), - ).not.toBeInTheDocument(); - }, }; export const OpenDownloadLogs: Story = { From 04a461580510f03a43f393b0ab97b7e680fd39a8 Mon Sep 17 00:00:00 2001 From: McKayla Washburn-Love Date: Mon, 10 Aug 2026 19:40:13 +0000 Subject: [PATCH 3/3] Update WorkspaceActions.tsx --- .../pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx index cce8418905e..d94ec53bbce 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx @@ -178,8 +178,7 @@ export const WorkspaceActions: FC = ({ {canCancel && } - {/* Favorite status is stored on the workspace itself rather than per - user, so the API only lets the owner toggle it. */} + {/* Only the owner can favorite a workspace. */} {user.id === workspace.owner_id && (