From 55ed6535847c136718a4f1dc4e7958f3701e7130 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Thu, 3 Apr 2025 13:22:05 +0000 Subject: [PATCH 1/6] fix: fix permissions for workspace creation --- site/src/api/queries/organizations.ts | 3 +- site/src/components/Checkbox/Checkbox.tsx | 30 +++++++++++++++++++ site/src/modules/permissions/workspaces.ts | 6 ++-- .../CreateWorkspacePage.tsx | 2 +- .../CreateWorkspacePageView.stories.tsx | 2 +- .../CreateWorkspacePageView.tsx | 4 +-- .../src/pages/TemplatePage/TemplateLayout.tsx | 4 ++- .../TemplatePageHeader.stories.tsx | 4 +-- .../pages/TemplatePage/TemplatePageHeader.tsx | 2 +- .../src/pages/TemplatesPage/TemplatesPage.tsx | 3 +- .../TemplatesPageView.stories.tsx | 4 +-- .../pages/TemplatesPage/TemplatesPageView.tsx | 2 +- .../pages/WorkspacesPage/WorkspacesPage.tsx | 5 ++-- 13 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 site/src/components/Checkbox/Checkbox.tsx diff --git a/site/src/api/queries/organizations.ts b/site/src/api/queries/organizations.ts index b0e25a985bd0f..4e58bc412c8b5 100644 --- a/site/src/api/queries/organizations.ts +++ b/site/src/api/queries/organizations.ts @@ -306,6 +306,7 @@ export const organizationsPermissions = ( export const workspacePermissionsByOrganization = ( organizationIds: string[] | undefined, + userId: string, ) => { if (!organizationIds) { return { enabled: false }; @@ -315,7 +316,7 @@ export const workspacePermissionsByOrganization = ( queryKey: ["workspaces", organizationIds.sort(), "permissions"], queryFn: async () => { const prefixedChecks = organizationIds.flatMap((orgId) => - Object.entries(workspacePermissionChecks(orgId)).map(([key, val]) => [ + Object.entries(workspacePermissionChecks(orgId, userId)).map(([key, val]) => [ `${orgId}.${key}`, val, ]), diff --git a/site/src/components/Checkbox/Checkbox.tsx b/site/src/components/Checkbox/Checkbox.tsx new file mode 100644 index 0000000000000..64cf894a86c49 --- /dev/null +++ b/site/src/components/Checkbox/Checkbox.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "utils/cn" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } diff --git a/site/src/modules/permissions/workspaces.ts b/site/src/modules/permissions/workspaces.ts index 9ebb75d4790de..6567ee6d5e893 100644 --- a/site/src/modules/permissions/workspaces.ts +++ b/site/src/modules/permissions/workspaces.ts @@ -1,10 +1,10 @@ -export const workspacePermissionChecks = (organizationId: string) => +export const workspacePermissionChecks = (organizationId: string, userId: string) => ({ - createWorkspaceForUser: { + createWorkspace: { object: { resource_type: "workspace", organization_id: organizationId, - owner_id: "*", + owner_id: userId, }, action: "create", }, diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 26f1808b83152..d15d651fd4b5d 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -67,7 +67,7 @@ const CreateWorkspacePage: FC = () => { const permissionsQuery = useQuery( templateQuery.data ? checkAuthorization({ - checks: workspacePermissionChecks(templateQuery.data.organization_id), + checks: workspacePermissionChecks(templateQuery.data.organization_id, me.id), }) : { enabled: false }, ); diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 47d1198765452..12c8b9e2c6671 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -27,7 +27,7 @@ const meta: Meta = { hasAllRequiredExternalAuth: true, mode: "form", permissions: { - createWorkspaceForUser: true, + createWorkspace: true, }, onCancel: action("onCancel"), }, diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 660580b5b80b8..8abbdc5f1a24a 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -256,7 +256,7 @@ export const CreateWorkspacePageView: FC = ({ = ({ - {permissions.createWorkspaceForUser && ( + {permissions.createWorkspace && ( { diff --git a/site/src/pages/TemplatePage/TemplateLayout.tsx b/site/src/pages/TemplatePage/TemplateLayout.tsx index 93d25d6f591db..68f70937b2c6c 100644 --- a/site/src/pages/TemplatePage/TemplateLayout.tsx +++ b/site/src/pages/TemplatePage/TemplateLayout.tsx @@ -6,6 +6,7 @@ import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs"; import { workspacePermissionChecks } from "modules/permissions/workspaces"; +import { useAuthenticated } from "contexts/auth/RequireAuth"; import { type FC, type PropsWithChildren, @@ -73,6 +74,7 @@ export const TemplateLayout: FC = ({ children = , }) => { const navigate = useNavigate(); + const { user: me } = useAuthenticated(); const { organization: organizationName = "default", template: templateName } = useParams() as { organization?: string; template: string }; const { data, error, isLoading } = useQuery({ @@ -81,7 +83,7 @@ export const TemplateLayout: FC = ({ }); const workspacePermissionsQuery = useQuery( checkAuthorization({ - checks: workspacePermissionChecks(organizationName), + checks: workspacePermissionChecks(organizationName, me.id), }), ); diff --git a/site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx index 4acd28446631f..b70dd4204b1ba 100644 --- a/site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx @@ -14,7 +14,7 @@ const meta: Meta = { canUpdateTemplate: true, }, workspacePermissions: { - createWorkspaceForUser: true, + createWorkspace: true, }, }, }; @@ -35,7 +35,7 @@ export const CanNotUpdate: Story = { export const CannotCreateWorkspace: Story = { args: { workspacePermissions: { - createWorkspaceForUser: false, + createWorkspace: false, }, }, }; diff --git a/site/src/pages/TemplatePage/TemplatePageHeader.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.tsx index 1d70379e75f43..9dd072b8275b0 100644 --- a/site/src/pages/TemplatePage/TemplatePageHeader.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.tsx @@ -180,7 +180,7 @@ export const TemplatePageHeader: FC = ({ actions={ <> {!template.deprecated && - workspacePermissions.createWorkspaceForUser && ( + workspacePermissions.createWorkspace && ( - )} + {!template.deprecated && workspacePermissions.createWorkspace && ( + + )} {permissions.canUpdateTemplate && ( Date: Thu, 3 Apr 2025 13:33:18 +0000 Subject: [PATCH 3/6] fix: remove checkbox component --- site/src/components/Checkbox/Checkbox.tsx | 30 ----------------------- 1 file changed, 30 deletions(-) delete mode 100644 site/src/components/Checkbox/Checkbox.tsx diff --git a/site/src/components/Checkbox/Checkbox.tsx b/site/src/components/Checkbox/Checkbox.tsx deleted file mode 100644 index 64cf894a86c49..0000000000000 --- a/site/src/components/Checkbox/Checkbox.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client" - -import * as React from "react" -import * as CheckboxPrimitive from "@radix-ui/react-checkbox" -import { Check } from "lucide-react" - -import { cn } from "utils/cn" - -const Checkbox = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - - -)) -Checkbox.displayName = CheckboxPrimitive.Root.displayName - -export { Checkbox } From 49951a6e075df64dca1bd528bd12a56af1bac5fc Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Thu, 3 Apr 2025 14:10:49 +0000 Subject: [PATCH 4/6] fix: revert create createWorkspaceForUser permissions --- .../CreateWorkspacePage/CreateWorkspacePage.tsx | 12 +++--------- .../CreateWorkspacePageView.stories.tsx | 2 +- .../CreateWorkspacePageView.tsx | 9 ++++----- .../src/pages/CreateWorkspacePage/permissions.ts | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 site/src/pages/CreateWorkspacePage/permissions.ts diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index a53670afa2076..150a79bd69487 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -17,10 +17,6 @@ import { Loader } from "components/Loader/Loader"; import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useEffectEvent } from "hooks/hookPolyfills"; import { useDashboard } from "modules/dashboard/useDashboard"; -import { - type WorkspacePermissions, - workspacePermissionChecks, -} from "modules/permissions/workspaces"; import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"; import { type FC, useCallback, useEffect, useRef, useState } from "react"; import { Helmet } from "react-helmet-async"; @@ -30,6 +26,7 @@ import { pageTitle } from "utils/page"; import type { AutofillBuildParameter } from "utils/richParameters"; import { paramsUsedToCreateWorkspace } from "utils/workspace"; import { CreateWorkspacePageView } from "./CreateWorkspacePageView"; +import { type CreateWSPermissions, createWorkspaceChecks } from "./permissions"; export const createWorkspaceModes = ["form", "auto", "duplicate"] as const; export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number]; @@ -67,10 +64,7 @@ const CreateWorkspacePage: FC = () => { const permissionsQuery = useQuery( templateQuery.data ? checkAuthorization({ - checks: workspacePermissionChecks( - templateQuery.data.organization_id, - me.id, - ), + checks: createWorkspaceChecks(templateQuery.data.organization_id), }) : { enabled: false }, ); @@ -212,7 +206,7 @@ const CreateWorkspacePage: FC = () => { externalAuthPollingState={externalAuthPollingState} startPollingExternalAuth={startPollingExternalAuth} hasAllRequiredExternalAuth={hasAllRequiredExternalAuth} - permissions={permissionsQuery.data as WorkspacePermissions} + permissions={permissionsQuery.data as CreateWSPermissions} parameters={realizedParameters as TemplateVersionParameter[]} presets={templateVersionPresetsQuery.data ?? []} creatingWorkspace={createWorkspaceMutation.isLoading} diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 12c8b9e2c6671..47d1198765452 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -27,7 +27,7 @@ const meta: Meta = { hasAllRequiredExternalAuth: true, mode: "form", permissions: { - createWorkspace: true, + createWorkspaceForUser: true, }, onCancel: action("onCancel"), }, diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 8abbdc5f1a24a..656e18563eb60 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -28,7 +28,6 @@ import { Stack } from "components/Stack/Stack"; import { Switch } from "components/Switch/Switch"; import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; import { type FormikContextType, useFormik } from "formik"; -import type { WorkspacePermissions } from "modules/permissions/workspaces"; import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"; import { type FC, useCallback, useEffect, useMemo, useState } from "react"; import { @@ -47,7 +46,7 @@ import type { ExternalAuthPollingState, } from "./CreateWorkspacePage"; import { ExternalAuthButton } from "./ExternalAuthButton"; - +import type { CreateWSPermissions } from "./permissions"; export const Language = { duplicationWarning: "Duplicating a workspace only copies its parameters. No state from the old workspace is copied over.", @@ -69,7 +68,7 @@ export interface CreateWorkspacePageViewProps { parameters: TypesGen.TemplateVersionParameter[]; autofillParameters: AutofillBuildParameter[]; presets: TypesGen.Preset[]; - permissions: WorkspacePermissions; + permissions: CreateWSPermissions; creatingWorkspace: boolean; onCancel: () => void; onSubmit: ( @@ -256,7 +255,7 @@ export const CreateWorkspacePageView: FC = ({ = ({ - {permissions.createWorkspace && ( + {permissions.createWorkspaceForUser && ( { diff --git a/site/src/pages/CreateWorkspacePage/permissions.ts b/site/src/pages/CreateWorkspacePage/permissions.ts new file mode 100644 index 0000000000000..07bad5031ddc2 --- /dev/null +++ b/site/src/pages/CreateWorkspacePage/permissions.ts @@ -0,0 +1,16 @@ +export const createWorkspaceChecks = (organizationId: string) => + ({ + createWorkspaceForUser: { + object: { + resource_type: "workspace", + organization_id: organizationId, + owner_id: "*", + }, + action: "create", + }, + }) as const; + +export type CreateWSPermissions = Record< + keyof ReturnType, + boolean +>; From 0b13269669355c242a2289323aef2b73b5163d19 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Thu, 3 Apr 2025 14:44:59 +0000 Subject: [PATCH 5/6] fix: get org id for permission check on template layout --- site/src/pages/TemplatePage/TemplateLayout.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/site/src/pages/TemplatePage/TemplateLayout.tsx b/site/src/pages/TemplatePage/TemplateLayout.tsx index 78b8822b6fa42..b8360506fdeeb 100644 --- a/site/src/pages/TemplatePage/TemplateLayout.tsx +++ b/site/src/pages/TemplatePage/TemplateLayout.tsx @@ -6,6 +6,7 @@ import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs"; import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { workspacePermissionChecks } from "modules/permissions/workspaces"; import { type FC, @@ -77,15 +78,18 @@ export const TemplateLayout: FC = ({ const { user: me } = useAuthenticated(); const { organization: organizationName = "default", template: templateName } = useParams() as { organization?: string; template: string }; + const { organizations } = useDashboard(); + const organization = organizations.find((o) => o.name === organizationName); const { data, error, isLoading } = useQuery({ queryKey: ["template", templateName], queryFn: () => fetchTemplate(organizationName, templateName), }); - const workspacePermissionsQuery = useQuery( - checkAuthorization({ - checks: workspacePermissionChecks(organizationName, me.id), + const workspacePermissionsQuery = useQuery({ + ...checkAuthorization({ + checks: workspacePermissionChecks(organization?.id ?? "", me.id), }), - ); + enabled: organization !== undefined, + }); const location = useLocation(); const paths = location.pathname.split("/"); From d1f7eb0f53c2c6b0a8351b13d3627a38343ed700 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Thu, 3 Apr 2025 15:32:51 +0000 Subject: [PATCH 6/6] Revert "fix: get org id for permission check on template layout" This reverts commit 0b13269669355c242a2289323aef2b73b5163d19. --- site/src/pages/TemplatePage/TemplateLayout.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/site/src/pages/TemplatePage/TemplateLayout.tsx b/site/src/pages/TemplatePage/TemplateLayout.tsx index b8360506fdeeb..78b8822b6fa42 100644 --- a/site/src/pages/TemplatePage/TemplateLayout.tsx +++ b/site/src/pages/TemplatePage/TemplateLayout.tsx @@ -6,7 +6,6 @@ import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs"; import { useAuthenticated } from "contexts/auth/RequireAuth"; -import { useDashboard } from "modules/dashboard/useDashboard"; import { workspacePermissionChecks } from "modules/permissions/workspaces"; import { type FC, @@ -78,18 +77,15 @@ export const TemplateLayout: FC = ({ const { user: me } = useAuthenticated(); const { organization: organizationName = "default", template: templateName } = useParams() as { organization?: string; template: string }; - const { organizations } = useDashboard(); - const organization = organizations.find((o) => o.name === organizationName); const { data, error, isLoading } = useQuery({ queryKey: ["template", templateName], queryFn: () => fetchTemplate(organizationName, templateName), }); - const workspacePermissionsQuery = useQuery({ - ...checkAuthorization({ - checks: workspacePermissionChecks(organization?.id ?? "", me.id), + const workspacePermissionsQuery = useQuery( + checkAuthorization({ + checks: workspacePermissionChecks(organizationName, me.id), }), - enabled: organization !== undefined, - }); + ); const location = useLocation(); const paths = location.pathname.split("/"); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy