fix: fix permissions for workspace creation (#17241)

This fixes the permissions check when creating a workspace by setting
the owner_id to the current user's id. This was originally setting
owner_id to *

```
		createWorkspace: {
			object: {
				resource_type: "workspace",
				organization_id: organizationId,
				owner_id: userId,
			},
			action: "create",
		},
```
This commit is contained in:
Jaayden Halko
2025-04-03 11:46:33 -04:00
committed by GitHub
parent b61f0ab958
commit ccfe1bda1a
12 changed files with 54 additions and 36 deletions
+4 -4
View File
@@ -306,6 +306,7 @@ export const organizationsPermissions = (
export const workspacePermissionsByOrganization = (
organizationIds: string[] | undefined,
userId: string,
) => {
if (!organizationIds) {
return { enabled: false };
@@ -315,10 +316,9 @@ export const workspacePermissionsByOrganization = (
queryKey: ["workspaces", organizationIds.sort(), "permissions"],
queryFn: async () => {
const prefixedChecks = organizationIds.flatMap((orgId) =>
Object.entries(workspacePermissionChecks(orgId)).map(([key, val]) => [
`${orgId}.${key}`,
val,
]),
Object.entries(workspacePermissionChecks(orgId, userId)).map(
([key, val]) => [`${orgId}.${key}`, val],
),
);
const response = await API.checkAuthorization({
+6 -3
View File
@@ -1,10 +1,13 @@
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",
},
@@ -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,7 +64,7 @@ const CreateWorkspacePage: FC = () => {
const permissionsQuery = useQuery(
templateQuery.data
? checkAuthorization({
checks: workspacePermissionChecks(templateQuery.data.organization_id),
checks: createWorkspaceChecks(templateQuery.data.organization_id),
})
: { enabled: false },
);
@@ -209,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}
@@ -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: (
@@ -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<typeof createWorkspaceChecks>,
boolean
>;
@@ -5,6 +5,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
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 { workspacePermissionChecks } from "modules/permissions/workspaces";
import {
type FC,
@@ -73,6 +74,7 @@ export const TemplateLayout: FC<PropsWithChildren> = ({
children = <Outlet />,
}) => {
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<PropsWithChildren> = ({
});
const workspacePermissionsQuery = useQuery(
checkAuthorization({
checks: workspacePermissionChecks(organizationName),
checks: workspacePermissionChecks(organizationName, me.id),
}),
);
@@ -14,7 +14,7 @@ const meta: Meta<typeof TemplatePageHeader> = {
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,
},
},
};
@@ -179,17 +179,16 @@ export const TemplatePageHeader: FC<TemplatePageHeaderProps> = ({
<PageHeader
actions={
<>
{!template.deprecated &&
workspacePermissions.createWorkspaceForUser && (
<Button
variant="contained"
startIcon={<AddIcon />}
component={RouterLink}
to={`${templateLink}/workspace`}
>
Create Workspace
</Button>
)}
{!template.deprecated && workspacePermissions.createWorkspace && (
<Button
variant="contained"
startIcon={<AddIcon />}
component={RouterLink}
to={`${templateLink}/workspace`}
>
Create Workspace
</Button>
)}
{permissions.canUpdateTemplate && (
<TemplateMenu
@@ -11,7 +11,7 @@ import { pageTitle } from "utils/page";
import { TemplatesPageView } from "./TemplatesPageView";
export const TemplatesPage: FC = () => {
const { permissions } = useAuthenticated();
const { permissions, user: me } = useAuthenticated();
const { showOrganizations } = useDashboard();
const searchParamsResult = useSearchParams();
@@ -30,6 +30,7 @@ export const TemplatesPage: FC = () => {
const workspacePermissionsQuery = useQuery(
workspacePermissionsByOrganization(
templatesQuery.data?.map((template) => template.organization_id),
me.id,
),
);
@@ -76,7 +76,7 @@ export const WithTemplates: Story = {
examples: [],
workspacePermissions: {
[MockTemplate.organization_id]: {
createWorkspaceForUser: true,
createWorkspace: true,
},
},
},
@@ -94,7 +94,7 @@ export const CannotCreateWorkspaces: Story = {
...WithTemplates.args,
workspacePermissions: {
[MockTemplate.organization_id]: {
createWorkspaceForUser: false,
createWorkspace: false,
},
},
},
@@ -160,7 +160,7 @@ const TemplateRow: FC<TemplateRowProps> = ({
{template.deprecated ? (
<DeprecatedBadge />
) : workspacePermissions?.[template.organization_id]
?.createWorkspaceForUser ? (
?.createWorkspace ? (
<MuiButton
size="small"
css={styles.actionButton}
@@ -40,7 +40,7 @@ const WorkspacesPage: FC = () => {
// each hook.
const searchParamsResult = useSafeSearchParams();
const pagination = usePagination({ searchParamsResult });
const { permissions } = useAuthenticated();
const { permissions, user: me } = useAuthenticated();
const { entitlements } = useDashboard();
const templatesQuery = useQuery(templates());
@@ -48,6 +48,7 @@ const WorkspacesPage: FC = () => {
const orgPermissionsQuery = useQuery(
workspacePermissionsByOrganization(
templatesQuery.data?.map((template) => template.organization_id),
me.id,
),
);
@@ -59,7 +60,7 @@ const WorkspacesPage: FC = () => {
return templatesQuery.data.filter((template) => {
const orgPermission = orgPermissionsQuery.data[template.organization_id];
return orgPermission?.createWorkspaceForUser;
return orgPermission?.createWorkspace;
});
}, [templatesQuery.data, orgPermissionsQuery.data]);