mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
fix: disable task sharing (#21867)
This commit is contained in:
@@ -2353,6 +2353,17 @@ func (api *API) patchWorkspaceACL(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't allow adding new groups or users to a workspace associated with a
|
||||
// task. Sharing a task workspace without sharing the task itself is a broken
|
||||
// half measure that we don't want to support right now. To be fixed!
|
||||
if workspace.TaskID.Valid {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Task workspaces cannot be shared.",
|
||||
Detail: "This workspace is managed by a task. Task sharing has not yet been implemented.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
apiKey := httpmw.APIKey(r)
|
||||
if _, ok := req.UserRoles[apiKey.UserID.String()]; ok {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "components/DropdownMenu/DropdownMenu";
|
||||
import { CoderIcon } from "components/Icons/CoderIcon";
|
||||
@@ -22,13 +21,7 @@ import {
|
||||
} from "components/Tooltip/Tooltip";
|
||||
import { useAuthenticated } from "hooks";
|
||||
import { useSearchParamsKey } from "hooks/useSearchParamsKey";
|
||||
import {
|
||||
EditIcon,
|
||||
EllipsisIcon,
|
||||
PanelLeftIcon,
|
||||
Share2Icon,
|
||||
TrashIcon,
|
||||
} from "lucide-react";
|
||||
import { EditIcon, EllipsisIcon, PanelLeftIcon, TrashIcon } from "lucide-react";
|
||||
import { type FC, useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { Link as RouterLink, useNavigate, useParams } from "react-router";
|
||||
@@ -234,15 +227,6 @@ const TaskSidebarMenuItem: FC<TaskSidebarMenuItemProps> = ({ task }) => {
|
||||
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem asChild>
|
||||
<RouterLink
|
||||
to={`/@${task.owner_name}/${task.workspace_name}/settings/sharing`}
|
||||
>
|
||||
<Share2Icon />
|
||||
Share
|
||||
</RouterLink>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -141,6 +141,7 @@ export const RoleSelectField: FC<RoleSelectFieldProps> = ({
|
||||
interface WorkspaceSharingFormProps {
|
||||
workspaceACL: WorkspaceACL | undefined;
|
||||
canUpdatePermissions: boolean;
|
||||
isTaskWorkspace: boolean;
|
||||
error: unknown;
|
||||
onUpdateUser: (user: WorkspaceUser, role: WorkspaceRole) => void;
|
||||
updatingUserId: WorkspaceUser["id"] | undefined;
|
||||
@@ -156,6 +157,7 @@ interface WorkspaceSharingFormProps {
|
||||
export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
|
||||
workspaceACL,
|
||||
canUpdatePermissions,
|
||||
isTaskWorkspace,
|
||||
error,
|
||||
updatingUserId,
|
||||
onUpdateUser,
|
||||
@@ -185,14 +187,24 @@ export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
|
||||
|
||||
const tableBody = (
|
||||
<TableBody>
|
||||
{!workspaceACL ? (
|
||||
{isTaskWorkspace ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={999}>
|
||||
<EmptyState
|
||||
message="Task workspaces cannot be shared"
|
||||
description="This workspace is managed by a task. Task sharing has not yet been implemented."
|
||||
isCompact={isCompact}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : !workspaceACL ? (
|
||||
<TableLoader />
|
||||
) : isEmpty ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={999}>
|
||||
<EmptyState
|
||||
message="No shared members or groups yet"
|
||||
description="Add a member or group using the controls above"
|
||||
description="Add a member or group using the controls above."
|
||||
isCompact={isCompact}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { workspaceBuildParameters } from "api/queries/workspaceBuilds";
|
||||
import {
|
||||
startWorkspace,
|
||||
workspaceByOwnerAndName,
|
||||
workspacePermissions,
|
||||
} from "api/queries/workspaces";
|
||||
import type {
|
||||
Workspace,
|
||||
@@ -79,7 +78,6 @@ const TaskPage = () => {
|
||||
return state.error ? false : 5_000;
|
||||
},
|
||||
});
|
||||
const { data: permissions } = useQuery(workspacePermissions(workspace));
|
||||
const refetch = taskQuery.error ? taskQuery.refetch : workspaceQuery.refetch;
|
||||
const error = taskQuery.error ?? workspaceQuery.error;
|
||||
const waitingStatuses: WorkspaceStatus[] = ["starting", "pending"];
|
||||
@@ -200,11 +198,7 @@ const TaskPage = () => {
|
||||
<TaskPageLayout>
|
||||
<title>{pageTitle(task.display_name)}</title>
|
||||
|
||||
<TaskTopbar
|
||||
task={task}
|
||||
workspace={workspace}
|
||||
canUpdatePermissions={permissions?.updateWorkspace ?? false}
|
||||
/>
|
||||
<TaskTopbar task={task} workspace={workspace} />
|
||||
{content}
|
||||
|
||||
<ModifyPromptDialog
|
||||
|
||||
@@ -16,21 +16,12 @@ import {
|
||||
} from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { Link as RouterLink } from "react-router";
|
||||
import { ShareButton } from "../WorkspacePage/WorkspaceActions/ShareButton";
|
||||
import { TaskStartupWarningButton } from "./TaskStartupWarningButton";
|
||||
import { TaskStatusLink } from "./TaskStatusLink";
|
||||
|
||||
type TaskTopbarProps = {
|
||||
task: Task;
|
||||
workspace: Workspace;
|
||||
canUpdatePermissions: boolean;
|
||||
};
|
||||
type TaskTopbarProps = { task: Task; workspace: Workspace };
|
||||
|
||||
export const TaskTopbar: FC<TaskTopbarProps> = ({
|
||||
task,
|
||||
workspace,
|
||||
canUpdatePermissions,
|
||||
}) => {
|
||||
export const TaskTopbar: FC<TaskTopbarProps> = ({ task, workspace }) => {
|
||||
return (
|
||||
<header className="flex flex-shrink-0 items-center gap-2 p-3 border-solid border-border border-0 border-b">
|
||||
<TooltipProvider>
|
||||
@@ -79,11 +70,6 @@ export const TaskTopbar: FC<TaskTopbarProps> = ({
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<ShareButton
|
||||
workspace={workspace}
|
||||
canUpdatePermissions={canUpdatePermissions}
|
||||
/>
|
||||
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<RouterLink to={`/@${workspace.owner_name}/${workspace.name}`}>
|
||||
<LayoutPanelTopIcon />
|
||||
|
||||
@@ -248,7 +248,7 @@ export const OpenKebabMenu: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const kebabButtons = await canvas.findAllByRole("button", {
|
||||
name: /open task actions/i,
|
||||
name: /show task actions/i,
|
||||
});
|
||||
await userEvent.click(kebabButtons[0]);
|
||||
},
|
||||
@@ -262,7 +262,7 @@ export const OpenDeleteDialog: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const kebabButtons = await canvas.findAllByRole("button", {
|
||||
name: /open task actions/i,
|
||||
name: /show task actions/i,
|
||||
});
|
||||
await userEvent.click(kebabButtons[0]);
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "components/DropdownMenu/DropdownMenu";
|
||||
import { Skeleton } from "components/Skeleton/Skeleton";
|
||||
@@ -26,12 +25,7 @@ import {
|
||||
TableRowSkeleton,
|
||||
} from "components/TableLoader/TableLoader";
|
||||
import { useClickableTableRow } from "hooks";
|
||||
import {
|
||||
EllipsisVertical,
|
||||
RotateCcwIcon,
|
||||
Share2Icon,
|
||||
TrashIcon,
|
||||
} from "lucide-react";
|
||||
import { EllipsisVertical, RotateCcwIcon, TrashIcon } from "lucide-react";
|
||||
import { TaskDeleteDialog } from "modules/tasks/TaskDeleteDialog/TaskDeleteDialog";
|
||||
import { TaskStatus } from "modules/tasks/TaskStatus/TaskStatus";
|
||||
import { type FC, type ReactNode, useState } from "react";
|
||||
@@ -266,22 +260,10 @@ const TaskRow: FC<TaskRowProps> = ({
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<EllipsisVertical aria-hidden="true" />
|
||||
<span className="sr-only">Open task actions</span>
|
||||
<span className="sr-only">Show task actions</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/@${task.owner_name}/${task.workspace_name}/settings/sharing`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Share2Icon />
|
||||
Share
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -35,6 +35,7 @@ export const ShareButton: FC<ShareButtonProps> = ({
|
||||
<WorkspaceSharingForm
|
||||
workspaceACL={sharing.workspaceACL}
|
||||
canUpdatePermissions={canUpdatePermissions}
|
||||
isTaskWorkspace={Boolean(workspace.task_id)}
|
||||
error={sharing.error ?? sharing.mutationError}
|
||||
updatingUserId={sharing.updatingUserId}
|
||||
onUpdateUser={sharing.updateUser}
|
||||
|
||||
@@ -54,6 +54,7 @@ export const WorkspaceSharingPageView: FC<WorkspaceSharingPageViewProps> = ({
|
||||
<WorkspaceSharingForm
|
||||
workspaceACL={workspaceACL}
|
||||
canUpdatePermissions={canUpdatePermissions}
|
||||
isTaskWorkspace={Boolean(workspace.task_id)}
|
||||
error={error}
|
||||
updatingUserId={updatingUserId}
|
||||
onUpdateUser={onUpdateUser}
|
||||
|
||||
Reference in New Issue
Block a user