fix: fix deployment settings navigation issues (#16780)

This commit is contained in:
ケイラ
2025-03-03 15:48:17 -07:00
committed by GitHub
parent 24f3445e00
commit 17ad2849e4
18 changed files with 405 additions and 276 deletions
+157
View File
@@ -0,0 +1,157 @@
import { type Page, expect, test } from "@playwright/test";
import {
createOrganization,
createOrganizationMember,
setupApiCalls,
} from "../api";
import { license, users } from "../constants";
import { login, requiresLicense } from "../helpers";
import { beforeCoderTest } from "../hooks";
test.beforeEach(async ({ page }) => {
beforeCoderTest(page);
});
type AdminSetting = (typeof adminSettings)[number];
const adminSettings = [
"Deployment",
"Organizations",
"Healthcheck",
"Audit Logs",
] as const;
async function hasAccessToAdminSettings(page: Page, settings: AdminSetting[]) {
// Organizations and Audit Logs both require a license to be visible
const visibleSettings = license
? settings
: settings.filter((it) => it !== "Organizations" && it !== "Audit Logs");
const adminSettingsButton = page.getByRole("button", {
name: "Admin settings",
});
if (visibleSettings.length < 1) {
await expect(adminSettingsButton).not.toBeVisible();
return;
}
await adminSettingsButton.click();
for (const name of visibleSettings) {
await expect(page.getByText(name, { exact: true })).toBeVisible();
}
const hiddenSettings = adminSettings.filter(
(it) => !visibleSettings.includes(it),
);
for (const name of hiddenSettings) {
await expect(page.getByText(name, { exact: true })).not.toBeVisible();
}
}
test.describe("roles admin settings access", () => {
test("member cannot see admin settings", async ({ page }) => {
await login(page, users.member);
await page.goto("/", { waitUntil: "domcontentloaded" });
// None, "Admin settings" button should not be visible
await hasAccessToAdminSettings(page, []);
});
test("template admin can see admin settings", async ({ page }) => {
await login(page, users.templateAdmin);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, ["Deployment", "Organizations"]);
});
test("user admin can see admin settings", async ({ page }) => {
await login(page, users.userAdmin);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, ["Deployment", "Organizations"]);
});
test("auditor can see admin settings", async ({ page }) => {
await login(page, users.auditor);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, [
"Deployment",
"Organizations",
"Audit Logs",
]);
});
test("admin can see admin settings", async ({ page }) => {
await login(page, users.admin);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, [
"Deployment",
"Organizations",
"Healthcheck",
"Audit Logs",
]);
});
});
test.describe("org-scoped roles admin settings access", () => {
requiresLicense();
test.beforeEach(async ({ page }) => {
await login(page);
await setupApiCalls(page);
});
test("org template admin can see admin settings", async ({ page }) => {
const org = await createOrganization();
const orgTemplateAdmin = await createOrganizationMember({
[org.id]: ["organization-template-admin"],
});
await login(page, orgTemplateAdmin);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, ["Organizations"]);
});
test("org user admin can see admin settings", async ({ page }) => {
const org = await createOrganization();
const orgUserAdmin = await createOrganizationMember({
[org.id]: ["organization-user-admin"],
});
await login(page, orgUserAdmin);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, ["Deployment", "Organizations"]);
});
test("org auditor can see admin settings", async ({ page }) => {
const org = await createOrganization();
const orgAuditor = await createOrganizationMember({
[org.id]: ["organization-auditor"],
});
await login(page, orgAuditor);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, ["Organizations", "Audit Logs"]);
});
test("org admin can see admin settings", async ({ page }) => {
const org = await createOrganization();
const orgAdmin = await createOrganizationMember({
[org.id]: ["organization-admin"],
});
await login(page, orgAdmin);
await page.goto("/", { waitUntil: "domcontentloaded" });
await hasAccessToAdminSettings(page, [
"Deployment",
"Organizations",
"Audit Logs",
]);
});
});
-17
View File
@@ -6,10 +6,8 @@ import type {
UpdateOrganizationRequest,
} from "api/typesGenerated";
import {
type AnyOrganizationPermissions,
type OrganizationPermissionName,
type OrganizationPermissions,
anyOrganizationPermissionChecks,
organizationPermissionChecks,
} from "modules/management/organizationPermissions";
import type { QueryClient } from "react-query";
@@ -266,21 +264,6 @@ export const organizationsPermissions = (
};
};
export const anyOrganizationPermissionsKey = [
"authorization",
"anyOrganization",
];
export const anyOrganizationPermissions = () => {
return {
queryKey: anyOrganizationPermissionsKey,
queryFn: () =>
API.checkAuthorization({
checks: anyOrganizationPermissionChecks,
}) as Promise<AnyOrganizationPermissions>,
};
};
export const getOrganizationIdpSyncClaimFieldValuesKey = (
organization: string,
field: string,
+3 -3
View File
@@ -18,7 +18,7 @@ import {
useContext,
} from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { type Permissions, permissionsToCheck } from "./permissions";
import { type Permissions, permissionChecks } from "./permissions";
export type AuthContextValue = {
isLoading: boolean;
@@ -50,13 +50,13 @@ export const AuthProvider: FC<PropsWithChildren> = ({ children }) => {
const hasFirstUserQuery = useQuery(hasFirstUser(userMetadataState));
const permissionsQuery = useQuery({
...checkAuthorization({ checks: permissionsToCheck }),
...checkAuthorization({ checks: permissionChecks }),
enabled: userQuery.data !== undefined,
});
const queryClient = useQueryClient();
const loginMutation = useMutation(
login({ checks: permissionsToCheck }, queryClient),
login({ checks: permissionChecks }, queryClient),
);
const logoutMutation = useMutation(logout(queryClient));
+159 -110
View File
@@ -1,156 +1,205 @@
import type { AuthorizationCheck } from "api/typesGenerated";
export const checks = {
viewAllUsers: "viewAllUsers",
updateUsers: "updateUsers",
createUser: "createUser",
createTemplates: "createTemplates",
updateTemplates: "updateTemplates",
deleteTemplates: "deleteTemplates",
viewAnyAuditLog: "viewAnyAuditLog",
viewDeploymentValues: "viewDeploymentValues",
editDeploymentValues: "editDeploymentValues",
viewUpdateCheck: "viewUpdateCheck",
viewExternalAuthConfig: "viewExternalAuthConfig",
viewDeploymentStats: "viewDeploymentStats",
readWorkspaceProxies: "readWorkspaceProxies",
editWorkspaceProxies: "editWorkspaceProxies",
createOrganization: "createOrganization",
viewAnyGroup: "viewAnyGroup",
createGroup: "createGroup",
viewAllLicenses: "viewAllLicenses",
viewNotificationTemplate: "viewNotificationTemplate",
viewOrganizationIDPSyncSettings: "viewOrganizationIDPSyncSettings",
} as const satisfies Record<string, string>;
export type Permissions = {
[k in PermissionName]: boolean;
};
// Type expression seems a little redundant (`keyof typeof checks` has the same
// result), just because each key-value pair is currently symmetrical; this may
// change down the line
type PermissionValue = (typeof checks)[keyof typeof checks];
export type PermissionName = keyof typeof permissionChecks;
export const permissionsToCheck = {
[checks.viewAllUsers]: {
export const permissionChecks = {
viewAllUsers: {
object: {
resource_type: "user",
},
action: "read",
},
[checks.updateUsers]: {
updateUsers: {
object: {
resource_type: "user",
},
action: "update",
},
[checks.createUser]: {
createUser: {
object: {
resource_type: "user",
},
action: "create",
},
[checks.createTemplates]: {
createTemplates: {
object: {
resource_type: "template",
any_org: true,
},
action: "update",
},
[checks.updateTemplates]: {
updateTemplates: {
object: {
resource_type: "template",
},
action: "update",
},
[checks.deleteTemplates]: {
deleteTemplates: {
object: {
resource_type: "template",
},
action: "delete",
},
[checks.viewAnyAuditLog]: {
viewDeploymentValues: {
object: {
resource_type: "deployment_config",
},
action: "read",
},
editDeploymentValues: {
object: {
resource_type: "deployment_config",
},
action: "update",
},
viewUpdateCheck: {
object: {
resource_type: "deployment_config",
},
action: "read",
},
viewExternalAuthConfig: {
object: {
resource_type: "deployment_config",
},
action: "read",
},
viewDeploymentStats: {
object: {
resource_type: "deployment_stats",
},
action: "read",
},
readWorkspaceProxies: {
object: {
resource_type: "workspace_proxy",
},
action: "read",
},
editWorkspaceProxies: {
object: {
resource_type: "workspace_proxy",
},
action: "create",
},
createOrganization: {
object: {
resource_type: "organization",
},
action: "create",
},
viewAnyGroup: {
object: {
resource_type: "group",
},
action: "read",
},
createGroup: {
object: {
resource_type: "group",
},
action: "create",
},
viewAllLicenses: {
object: {
resource_type: "license",
},
action: "read",
},
viewNotificationTemplate: {
object: {
resource_type: "notification_template",
},
action: "read",
},
viewOrganizationIDPSyncSettings: {
object: {
resource_type: "idpsync_settings",
},
action: "read",
},
viewAnyMembers: {
object: {
resource_type: "organization_member",
any_org: true,
},
action: "read",
},
editAnyGroups: {
object: {
resource_type: "group",
any_org: true,
},
action: "update",
},
assignAnyRoles: {
object: {
resource_type: "assign_org_role",
any_org: true,
},
action: "assign",
},
viewAnyIdpSyncSettings: {
object: {
resource_type: "idpsync_settings",
any_org: true,
},
action: "read",
},
editAnySettings: {
object: {
resource_type: "organization",
any_org: true,
},
action: "update",
},
viewAnyAuditLog: {
object: {
resource_type: "audit_log",
any_org: true,
},
action: "read",
},
[checks.viewDeploymentValues]: {
viewDebugInfo: {
object: {
resource_type: "deployment_config",
resource_type: "debug_info",
},
action: "read",
},
[checks.editDeploymentValues]: {
object: {
resource_type: "deployment_config",
},
action: "update",
},
[checks.viewUpdateCheck]: {
object: {
resource_type: "deployment_config",
},
action: "read",
},
[checks.viewExternalAuthConfig]: {
object: {
resource_type: "deployment_config",
},
action: "read",
},
[checks.viewDeploymentStats]: {
object: {
resource_type: "deployment_stats",
},
action: "read",
},
[checks.readWorkspaceProxies]: {
object: {
resource_type: "workspace_proxy",
},
action: "read",
},
[checks.editWorkspaceProxies]: {
object: {
resource_type: "workspace_proxy",
},
action: "create",
},
[checks.createOrganization]: {
object: {
resource_type: "organization",
},
action: "create",
},
[checks.viewAnyGroup]: {
object: {
resource_type: "group",
},
action: "read",
},
[checks.createGroup]: {
object: {
resource_type: "group",
},
action: "create",
},
[checks.viewAllLicenses]: {
object: {
resource_type: "license",
},
action: "read",
},
[checks.viewNotificationTemplate]: {
object: {
resource_type: "notification_template",
},
action: "read",
},
[checks.viewOrganizationIDPSyncSettings]: {
object: {
resource_type: "idpsync_settings",
},
action: "read",
},
} as const satisfies Record<PermissionValue, AuthorizationCheck>;
} as const satisfies Record<string, AuthorizationCheck>;
export type Permissions = Record<PermissionValue, boolean>;
export const canViewDeploymentSettings = (
permissions: Permissions | undefined,
): permissions is Permissions => {
return (
permissions !== undefined &&
(permissions.viewDeploymentValues ||
permissions.viewAllLicenses ||
permissions.viewAllUsers ||
permissions.viewAnyGroup ||
permissions.viewNotificationTemplate ||
permissions.viewOrganizationIDPSyncSettings)
);
};
/**
* Checks if the user can view or edit members or groups for the organization
* that produced the given OrganizationPermissions.
*/
export const canViewAnyOrganization = (
permissions: Permissions | undefined,
): permissions is Permissions => {
return (
permissions !== undefined &&
(permissions.viewAnyMembers ||
permissions.editAnyGroups ||
permissions.assignAnyRoles ||
permissions.viewAnyIdpSyncSettings ||
permissions.editAnySettings)
);
};
@@ -1,10 +1,7 @@
import { appearance } from "api/queries/appearance";
import { entitlements } from "api/queries/entitlements";
import { experiments } from "api/queries/experiments";
import {
anyOrganizationPermissions,
organizations,
} from "api/queries/organizations";
import { organizations } from "api/queries/organizations";
import type {
AppearanceConfig,
Entitlements,
@@ -13,8 +10,9 @@ import type {
} from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { canViewAnyOrganization } from "contexts/auth/permissions";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { canViewAnyOrganization } from "modules/management/organizationPermissions";
import { type FC, type PropsWithChildren, createContext } from "react";
import { useQuery } from "react-query";
import { selectFeatureVisibility } from "./entitlements";
@@ -34,20 +32,17 @@ export const DashboardContext = createContext<DashboardValue | undefined>(
export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
const { metadata } = useEmbeddedMetadata();
const { permissions } = useAuthenticated();
const entitlementsQuery = useQuery(entitlements(metadata.entitlements));
const experimentsQuery = useQuery(experiments(metadata.experiments));
const appearanceQuery = useQuery(appearance(metadata.appearance));
const organizationsQuery = useQuery(organizations());
const anyOrganizationPermissionsQuery = useQuery(
anyOrganizationPermissions(),
);
const error =
entitlementsQuery.error ||
appearanceQuery.error ||
experimentsQuery.error ||
organizationsQuery.error ||
anyOrganizationPermissionsQuery.error;
organizationsQuery.error;
if (error) {
return <ErrorAlert error={error} />;
@@ -57,8 +52,7 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
!entitlementsQuery.data ||
!appearanceQuery.data ||
!experimentsQuery.data ||
!organizationsQuery.data ||
!anyOrganizationPermissionsQuery.data;
!organizationsQuery.data;
if (isLoading) {
return <Loader fullscreen />;
@@ -79,8 +73,7 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
organizations: organizationsQuery.data,
showOrganizations,
canViewOrganizationSettings:
showOrganizations &&
canViewAnyOrganization(anyOrganizationPermissionsQuery.data),
showOrganizations && canViewAnyOrganization(permissions),
}}
>
{children}
@@ -82,7 +82,7 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
{canViewDeployment && (
<MenuItem
component={NavLink}
to="/deployment/general"
to="/deployment"
css={styles.menuItem}
onClick={onPopoverClose}
>
@@ -220,7 +220,7 @@ const AdminSettingsSub: FC<MobileMenuPermissions> = ({
asChild
className={cn(itemStyles.default, itemStyles.sub)}
>
<Link to="/deployment/general">Deployment</Link>
<Link to="/deployment">Deployment</Link>
</DropdownMenuItem>
)}
{canViewOrganizations && (
+6 -5
View File
@@ -1,6 +1,7 @@
import { buildInfo } from "api/queries/buildInfo";
import { useProxy } from "contexts/ProxyContext";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { canViewDeploymentSettings } from "contexts/auth/permissions";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { useDashboard } from "modules/dashboard/useDashboard";
import type { FC } from "react";
@@ -11,16 +12,16 @@ import { NavbarView } from "./NavbarView";
export const Navbar: FC = () => {
const { metadata } = useEmbeddedMetadata();
const buildInfoQuery = useQuery(buildInfo(metadata["build-info"]));
const { appearance, canViewOrganizationSettings } = useDashboard();
const { user: me, permissions, signOut } = useAuthenticated();
const featureVisibility = useFeatureVisibility();
const proxyContextValue = useProxy();
const canViewDeployment = canViewDeploymentSettings(permissions);
const canViewOrganizations = canViewOrganizationSettings;
const canViewHealth = permissions.viewDebugInfo;
const canViewAuditLog =
featureVisibility.audit_log && permissions.viewAnyAuditLog;
const canViewDeployment = permissions.viewDeploymentValues;
const canViewOrganizations = canViewOrganizationSettings;
const proxyContextValue = useProxy();
const canViewHealth = canViewDeployment;
return (
<NavbarView
@@ -90,6 +90,6 @@ describe("NavbarView", () => {
await userEvent.click(deploymentMenu);
const deploymentSettingsLink =
await screen.findByText<HTMLAnchorElement>(/deployment/i);
expect(deploymentSettingsLink.href).toContain("/deployment/general");
expect(deploymentSettingsLink.href).toContain("/deployment");
});
});
@@ -3,7 +3,7 @@ import { fn, userEvent, within } from "@storybook/test";
import { getAuthorizationKey } from "api/queries/authCheck";
import { getPreferredProxy } from "contexts/ProxyContext";
import { AuthProvider } from "contexts/auth/AuthProvider";
import { permissionsToCheck } from "contexts/auth/permissions";
import { permissionChecks } from "contexts/auth/permissions";
import {
MockAuthMethodsAll,
MockPermissions,
@@ -45,7 +45,7 @@ const meta: Meta<typeof ProxyMenu> = {
{ key: ["authMethods"], data: MockAuthMethodsAll },
{ key: ["hasFirstUser"], data: true },
{
key: getAuthorizationKey({ checks: permissionsToCheck }),
key: getAuthorizationKey({ checks: permissionChecks }),
data: MockPermissions,
},
],
@@ -8,19 +8,31 @@ import {
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { RequirePermission } from "contexts/auth/RequirePermission";
import { canViewDeploymentSettings } from "contexts/auth/permissions";
import { type FC, Suspense } from "react";
import { Outlet } from "react-router-dom";
import { Navigate, Outlet, useLocation } from "react-router-dom";
import { DeploymentSidebar } from "./DeploymentSidebar";
const DeploymentSettingsLayout: FC = () => {
const { permissions } = useAuthenticated();
const location = useLocation();
// The deployment settings page also contains users, audit logs, and groups
// so this page must be visible if you can see any of these.
const canViewDeploymentSettingsPage =
permissions.viewDeploymentValues ||
permissions.viewAllUsers ||
permissions.viewAnyAuditLog;
if (location.pathname === "/deployment") {
return (
<Navigate
to={
permissions.viewDeploymentValues
? "/deployment/general"
: "/deployment/users"
}
replace
/>
);
}
// The deployment settings page also contains users and groups and more so
// this page must be visible if you can see any of these.
const canViewDeploymentSettingsPage = canViewDeploymentSettings(permissions);
return (
<RequirePermission isFeatureVisible={canViewDeploymentSettingsPage}>
@@ -2,8 +2,6 @@ import type { DeploymentConfig } from "api/api";
import { deploymentConfig } from "api/queries/deployment";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { RequirePermission } from "contexts/auth/RequirePermission";
import { type FC, createContext, useContext } from "react";
import { useQuery } from "react-query";
import { Outlet } from "react-router-dom";
@@ -28,19 +26,8 @@ export const useDeploymentSettings = (): DeploymentSettingsValue => {
};
const DeploymentSettingsProvider: FC = () => {
const { permissions } = useAuthenticated();
const deploymentConfigQuery = useQuery(deploymentConfig());
// The deployment settings page also contains users, audit logs, and groups
// so this page must be visible if you can see any of these.
const canViewDeploymentSettingsPage =
permissions.viewDeploymentValues ||
permissions.viewAllUsers ||
permissions.viewAnyAuditLog;
// Not a huge problem to unload the content in the event of an error,
// because the sidebar rendering isn't tied to this. Even if the user hits
// a 403 error, they'll still have navigation options
if (deploymentConfigQuery.error) {
return <ErrorAlert error={deploymentConfigQuery.error} />;
}
@@ -50,13 +37,11 @@ const DeploymentSettingsProvider: FC = () => {
}
return (
<RequirePermission isFeatureVisible={canViewDeploymentSettingsPage}>
<DeploymentSettingsContext.Provider
value={{ deploymentConfig: deploymentConfigQuery.data }}
>
<Outlet />
</DeploymentSettingsContext.Provider>
</RequirePermission>
<DeploymentSettingsContext.Provider
value={{ deploymentConfig: deploymentConfigQuery.data }}
>
<Outlet />
</DeploymentSettingsContext.Provider>
);
};
@@ -135,65 +135,3 @@ export const canEditOrganization = (
permissions.createOrgRoles)
);
};
export type AnyOrganizationPermissions = {
[k in AnyOrganizationPermissionName]: boolean;
};
export type AnyOrganizationPermissionName =
keyof typeof anyOrganizationPermissionChecks;
export const anyOrganizationPermissionChecks = {
viewAnyMembers: {
object: {
resource_type: "organization_member",
any_org: true,
},
action: "read",
},
editAnyGroups: {
object: {
resource_type: "group",
any_org: true,
},
action: "update",
},
assignAnyRoles: {
object: {
resource_type: "assign_org_role",
any_org: true,
},
action: "assign",
},
viewAnyIdpSyncSettings: {
object: {
resource_type: "idpsync_settings",
any_org: true,
},
action: "read",
},
editAnySettings: {
object: {
resource_type: "organization",
any_org: true,
},
action: "update",
},
} as const satisfies Record<string, AuthorizationCheck>;
/**
* Checks if the user can view or edit members or groups for the organization
* that produced the given OrganizationPermissions.
*/
export const canViewAnyOrganization = (
permissions: AnyOrganizationPermissions | undefined,
): permissions is AnyOrganizationPermissions => {
return (
permissions !== undefined &&
(permissions.viewAnyMembers ||
permissions.editAnyGroups ||
permissions.assignAnyRoles ||
permissions.viewAnyIdpSyncSettings ||
permissions.editAnySettings)
);
};
@@ -1,11 +1,10 @@
import type { Meta, StoryObj } from "@storybook/react";
import { getAuthorizationKey } from "api/queries/authCheck";
import { anyOrganizationPermissionsKey } from "api/queries/organizations";
import { workspaceByOwnerAndNameKey } from "api/queries/workspaces";
import type { Workspace, WorkspaceAgentLifecycle } from "api/typesGenerated";
import { AuthProvider } from "contexts/auth/AuthProvider";
import { RequireAuth } from "contexts/auth/RequireAuth";
import { permissionsToCheck } from "contexts/auth/permissions";
import { permissionChecks } from "contexts/auth/permissions";
import {
reactRouterOutlet,
reactRouterParameters,
@@ -74,10 +73,9 @@ const meta = {
{ key: ["appearance"], data: MockAppearanceConfig },
{ key: ["organizations"], data: [MockDefaultOrganization] },
{
key: getAuthorizationKey({ checks: permissionsToCheck }),
key: getAuthorizationKey({ checks: permissionChecks }),
data: { editWorkspaceProxies: true },
},
{ key: anyOrganizationPermissionsKey, data: {} },
],
chromatic: { delay: 300 },
},
+3 -2
View File
@@ -453,8 +453,6 @@ export const router = createBrowserRouter(
path="notifications"
element={<DeploymentNotificationsPage />}
/>
<Route path="idp-org-sync" element={<IdpOrgSyncPage />} />
<Route path="premium" element={<PremiumPage />} />
</Route>
<Route path="licenses">
@@ -476,6 +474,9 @@ export const router = createBrowserRouter(
<Route path="users/create" element={<CreateUserPage />} />
{groupsRouter()}
<Route path="idp-org-sync" element={<IdpOrgSyncPage />} />
<Route path="premium" element={<PremiumPage />} />
</Route>
<Route path="/settings" element={<UserSettingsLayout />}>
+35 -23
View File
@@ -2856,6 +2856,41 @@ export const MockPermissions: Permissions = {
viewAllLicenses: true,
viewNotificationTemplate: true,
viewOrganizationIDPSyncSettings: true,
viewDebugInfo: true,
assignAnyRoles: true,
editAnyGroups: true,
editAnySettings: true,
viewAnyIdpSyncSettings: true,
viewAnyMembers: true,
};
export const MockNoPermissions: Permissions = {
createTemplates: false,
createUser: false,
deleteTemplates: false,
updateTemplates: false,
viewAllUsers: false,
updateUsers: false,
viewAnyAuditLog: false,
viewDeploymentValues: false,
editDeploymentValues: false,
viewUpdateCheck: false,
viewDeploymentStats: false,
viewExternalAuthConfig: false,
readWorkspaceProxies: false,
editWorkspaceProxies: false,
createOrganization: false,
viewAnyGroup: false,
createGroup: false,
viewAllLicenses: false,
viewNotificationTemplate: false,
viewOrganizationIDPSyncSettings: false,
viewDebugInfo: false,
assignAnyRoles: false,
editAnyGroups: false,
editAnySettings: false,
viewAnyIdpSyncSettings: false,
viewAnyMembers: false,
};
export const MockOrganizationPermissions: OrganizationPermissions = {
@@ -2890,29 +2925,6 @@ export const MockNoOrganizationPermissions: OrganizationPermissions = {
editIdpSyncSettings: false,
};
export const MockNoPermissions: Permissions = {
createTemplates: false,
createUser: false,
deleteTemplates: false,
updateTemplates: false,
viewAllUsers: false,
updateUsers: false,
viewAnyAuditLog: false,
viewDeploymentValues: false,
editDeploymentValues: false,
viewUpdateCheck: false,
viewDeploymentStats: false,
viewExternalAuthConfig: false,
readWorkspaceProxies: false,
editWorkspaceProxies: false,
createOrganization: false,
viewAnyGroup: false,
createGroup: false,
viewAllLicenses: false,
viewNotificationTemplate: false,
viewOrganizationIDPSyncSettings: false,
};
export const MockDeploymentConfig: DeploymentConfig = {
config: {
enable_terraform_debug_mode: true,
+2 -2
View File
@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import type { CreateWorkspaceBuildRequest } from "api/typesGenerated";
import { permissionsToCheck } from "contexts/auth/permissions";
import { permissionChecks } from "contexts/auth/permissions";
import { http, HttpResponse } from "msw";
import * as M from "./entities";
import { MockGroup, MockWorkspaceQuota } from "./entities";
@@ -173,7 +173,7 @@ export const handlers = [
}),
http.post("/api/v2/authcheck", () => {
const permissions = [
...Object.keys(permissionsToCheck),
...Object.keys(permissionChecks),
"canUpdateTemplate",
"updateWorkspace",
];
+2 -2
View File
@@ -6,7 +6,7 @@ import { hasFirstUserKey, meKey } from "api/queries/users";
import type { Entitlements } from "api/typesGenerated";
import { GlobalSnackbar } from "components/GlobalSnackbar/GlobalSnackbar";
import { AuthProvider } from "contexts/auth/AuthProvider";
import { permissionsToCheck } from "contexts/auth/permissions";
import { permissionChecks } from "contexts/auth/permissions";
import { DashboardContext } from "modules/dashboard/DashboardProvider";
import { DeploymentSettingsContext } from "modules/management/DeploymentSettingsProvider";
import { OrganizationSettingsContext } from "modules/management/OrganizationSettingsLayout";
@@ -114,7 +114,7 @@ export const withAuthProvider = (Story: FC, { parameters }: StoryContext) => {
queryClient.setQueryData(meKey, parameters.user);
queryClient.setQueryData(hasFirstUserKey, true);
queryClient.setQueryData(
getAuthorizationKey({ checks: permissionsToCheck }),
getAuthorizationKey({ checks: permissionChecks }),
parameters.permissions ?? {},
);