Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: ensure user admins can always see users table#15226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
buenos-nachos merged 33 commits intomainfrommes/deploy-bug
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
33 commits
Select commitHold shift + click to select a range
1ad09fa
refactor: update layout so that sidebar always shows
buenos-nachosOct 24, 2024
620bd3a
refactor: consolidate logic more
buenos-nachosOct 24, 2024
7ee3cf0
fix: add redirect for base users
buenos-nachosOct 24, 2024
e889621
fix: update routing logic to not block on disabled queries
buenos-nachosOct 24, 2024
36e0376
fix: update type misalignment
buenos-nachosOct 24, 2024
032a75c
chore: format and tidy up
buenos-nachosOct 24, 2024
eea8bbd
fix: make sure you can't navigate directly to bare /deployment page
buenos-nachosOct 24, 2024
e6d4201
fix: add more granularity to redirect logic
buenos-nachosOct 24, 2024
14fc68e
fix: prevent infinite redirect
buenos-nachosOct 24, 2024
a8fd9e6
fix: update redirects for /organizations routes
buenos-nachosOct 24, 2024
0a36cd1
fix: format files
buenos-nachosOct 24, 2024
e6d772b
fix: tighten up types for permissions
buenos-nachosOct 25, 2024
3752af5
Merge branch 'main' into mes/deploy-bug
buenos-nachosOct 25, 2024
2068a4c
fix: update route checks to account for subroutes
buenos-nachosOct 25, 2024
2fe3d53
fix: add e2e check to ensure that redirect hasn't happened
buenos-nachosOct 25, 2024
3c41de0
fix: update stories to account for new dashboard logic
buenos-nachosOct 25, 2024
162642e
fix: update out-of-date comment
buenos-nachosOct 25, 2024
64c6f29
fix: update default value logic for storybook setup
buenos-nachosOct 25, 2024
e12a4aa
fix: update individual stories
buenos-nachosOct 25, 2024
b9b33c0
fix: finish storybook injection changes
buenos-nachosOct 25, 2024
f7df5ff
fix: add back separate empty state to satisfy tests
buenos-nachosOct 25, 2024
6925294
fix: add tests and update code to account for missing cases
buenos-nachosOct 25, 2024
9fc0afa
fix: apply formatting
buenos-nachosOct 25, 2024
498e7ba
break out `DeploymentSettingsProvider`
aslilacOct 28, 2024
234c606
fix: apply changes from previous PR
buenos-nachosOct 28, 2024
4496a75
fix: apply formatting
buenos-nachosOct 28, 2024
9fcf3e7
Merge branch 'main' into mes/deploy-bug
buenos-nachosOct 28, 2024
19fb6e5
Merge branch 'lilac/deployment-settings-provider' into mes/deploy-bug
buenos-nachosOct 28, 2024
804255e
fix: delete invalid tests
buenos-nachosOct 28, 2024
d341175
fix: revert changes not caught in merge
buenos-nachosOct 28, 2024
f3e6659
fix: update spellcheck
buenos-nachosOct 28, 2024
ce67a66
fix: revert nits
buenos-nachosOct 28, 2024
7e946a7
fix: apply feedback
buenos-nachosOct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions.vscode/settings.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -175,6 +175,7 @@
"unauthenticate",
"unconvert",
"untar",
"userauth",
"userspace",
"VMID",
"walkthrough",
Expand Down
1 change: 1 addition & 0 deletionssite/e2e/global.setup.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,7 @@ test("setup deployment", async ({ page }) => {
expect(constants.license.split(".").length).toBe(3); // otherwise it's invalid

await page.goto("/deployment/licenses", { waitUntil: "domcontentloaded" });
await expect(page).toHaveTitle("License Settings - Coder");

await page.getByText("Add a license").click();
await page.getByRole("textbox").fill(constants.license);
Expand Down
8 changes: 3 additions & 5 deletionssite/jest.setup.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import "@testing-library/jest-dom";
import "jest-location-mock";
import { cleanup } from "@testing-library/react";
import crypto from "crypto";
import crypto from "node:crypto";
import { useMemo } from "react";
import type { Region } from "api/typesGenerated";
import type { ProxyLatencyReport } from "contexts/useProxyLatency";
Expand DownExpand Up@@ -48,9 +48,7 @@ global.ResizeObserver = require("resize-observer-polyfill");
// Polyfill the getRandomValues that is used on utils/random.ts
Object.defineProperty(global.self, "crypto", {
value: {
getRandomValues: function (buffer: Buffer) {
return crypto.randomFillSync(buffer);
},
getRandomValues: crypto.randomFillSync,
},
});

Expand All@@ -72,5 +70,5 @@ afterEach(() => {
// Clean up after the tests are finished.
afterAll(() => server.close());

// This is needed because we are compiling under `--isolatedModules`
//biome-ignore lint/complexity/noUselessEmptyExport:This is needed because we are compiling under `--isolatedModules`
export {};
28 changes: 24 additions & 4 deletionssite/src/contexts/auth/permissions.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import type { AuthorizationCheck } from "api/typesGenerated";

export const checks = {
viewAllUsers: "viewAllUsers",
updateUsers: "updateUsers",
Expand All@@ -11,13 +13,20 @@ export const checks = {
viewUpdateCheck: "viewUpdateCheck",
viewExternalAuthConfig: "viewExternalAuthConfig",
viewDeploymentStats: "viewDeploymentStats",
readWorkspaceProxies: "readWorkspaceProxies",
editWorkspaceProxies: "editWorkspaceProxies",
createOrganization: "createOrganization",
editAnyOrganization: "editAnyOrganization",
viewAnyGroup: "viewAnyGroup",
createGroup: "createGroup",
viewAllLicenses: "viewAllLicenses",
} as const;
viewNotificationTemplate: "viewNotificationTemplate",
} as const satisfies Record<string, string>;

// 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 const permissionsToCheck = {
[checks.viewAllUsers]: {
Expand DownExpand Up@@ -94,6 +103,12 @@ export const permissionsToCheck = {
},
action: "read",
},
[checks.readWorkspaceProxies]: {
object: {
resource_type: "workspace_proxy",
},
action: "read",
},
[checks.editWorkspaceProxies]: {
object: {
resource_type: "workspace_proxy",
Expand All@@ -116,7 +131,6 @@ export const permissionsToCheck = {
[checks.viewAnyGroup]: {
object: {
resource_type: "group",
org_id: "any",
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Ended up deleting this because the property wasn't compatible. It also didn't seem to be used anywhere

@Emyrk Not sure if we should update the types so that this can be added back

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Can you delete this unused check then?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Oh, sorry – I meant that theorg_id property specifically didn't seem to be used anywhere

},
action: "read",
},
Expand All@@ -132,6 +146,12 @@ export const permissionsToCheck = {
},
action: "read",
},
} as const;
[checks.viewNotificationTemplate]: {
object: {
resource_type: "notification_template",
},
action: "read",
},
} as const satisfies Record<PermissionValue, AuthorizationCheck>;

export type Permissions = Record<keyof typeof permissionsToCheck, boolean>;
export type Permissions = Record<PermissionValue, boolean>;
64 changes: 64 additions & 0 deletionssite/src/modules/management/DeploymentSettingsProvider.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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";

export const DeploymentSettingsContext = createContext<
DeploymentSettingsValue | undefined
>(undefined);

type DeploymentSettingsValue = Readonly<{
deploymentConfig: DeploymentConfig;
}>;

export const useDeploymentSettings = (): DeploymentSettingsValue => {
const context = useContext(DeploymentSettingsContext);
if (!context) {
throw new Error(
`${useDeploymentSettings.name} should be used inside of ${DeploymentSettingsProvider.name}`,
);
}

return context;
};

const DeploymentSettingsProvider: FC = () => {
const { permissions } = useAuthenticated();
const deploymentConfigQuery = useQuery(deploymentConfig());

// The deployment settings page also contains users, audit logs, groups and
// organizations, so this page must be visible if you can see any of these.
const canViewDeploymentSettingsPage =
permissions.viewDeploymentValues ||
permissions.viewAllUsers ||
permissions.editAnyOrganization ||
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} />;
}

if (!deploymentConfigQuery.data) {
return <Loader />;
}

return (
<RequirePermission isFeatureVisible={canViewDeploymentSettingsPage}>
<DeploymentSettingsContext.Provider
value={{ deploymentConfig: deploymentConfigQuery.data }}
>
<Outlet />
</DeploymentSettingsContext.Provider>
</RequirePermission>
);
};

export default DeploymentSettingsProvider;
27 changes: 4 additions & 23 deletionssite/src/modules/management/ManagementSettingsLayout.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
import type { DeploymentConfig } from "api/api";
import { deploymentConfig } from "api/queries/deployment";
import type { AuthorizationResponse, Organization } from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { Margins } from "components/Margins/Margins";
import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { RequirePermission } from "contexts/auth/RequirePermission";
import { useDashboard } from "modules/dashboard/useDashboard";
import { type FC, Suspense, createContext, useContext } from "react";
import { useQuery } from "react-query";
import { Outlet, useParams } from "react-router-dom";
import { Sidebar } from "./Sidebar";

Expand All@@ -18,7 +14,6 @@ export const ManagementSettingsContext = createContext<
>(undefined);

type ManagementSettingsValue = Readonly<{
deploymentValues: DeploymentConfig;
organizations: readonly Organization[];
organization?: Organization;
}>;
Expand DownExpand Up@@ -48,15 +43,8 @@ export const canEditOrganization = (
);
};

/**
* A multi-org capable settings page layout.
*
* If multi-org is not enabled or licensed, this is the wrong layout to use.
* See DeploySettingsLayoutInner instead.
*/
export const ManagementSettingsLayout: FC = () => {
const ManagementSettingsLayout: FC = () => {
const { permissions } = useAuthenticated();
const deploymentConfigQuery = useQuery(deploymentConfig());
const { organizations } = useDashboard();
const { organization: orgName } = useParams() as {
organization?: string;
Expand All@@ -70,14 +58,6 @@ export const ManagementSettingsLayout: FC = () => {
permissions.editAnyOrganization ||
permissions.viewAnyAuditLog;

if (deploymentConfigQuery.error) {
return <ErrorAlert error={deploymentConfigQuery.error} />;
}

if (!deploymentConfigQuery.data) {
return <Loader />;
}

const organization =
organizations && orgName
? organizations.find((org) => org.name === orgName)
Expand All@@ -87,15 +67,14 @@ export const ManagementSettingsLayout: FC = () => {
<RequirePermission isFeatureVisible={canViewDeploymentSettingsPage}>
<ManagementSettingsContext.Provider
value={{
deploymentValues: deploymentConfigQuery.data,
organizations,
organization,
}}
>
<Margins>
<Stack css={{ padding: "48px 0" }} direction="row" spacing={6}>
<Sidebar />
<main css={{width: "100%" }}>
<main css={{flexGrow: 1 }}>
<Suspense fallback={<Loader />}>
<Outlet />
</Suspense>
Expand All@@ -106,3 +85,5 @@ export const ManagementSettingsLayout: FC = () => {
</RequirePermission>
);
};

export default ManagementSettingsLayout;
3 changes: 2 additions & 1 deletionsite/src/modules/management/SidebarView.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import {
MockNoPermissions,
MockOrganization,
MockOrganization2,
MockPermissions,
Expand DownExpand Up@@ -96,7 +97,7 @@ export const NoDeploymentValues: Story = {

export const NoPermissions: Story = {
args: {
permissions:{},
permissions:MockNoPermissions,
},
};

Expand Down
39 changes: 18 additions & 21 deletionssite/src/modules/management/SidebarView.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,19 +2,15 @@ import { cx } from "@emotion/css";
import type { Interpolation, Theme } from "@emotion/react";
import AddIcon from "@mui/icons-material/Add";
import SettingsIcon from "@mui/icons-material/Settings";
import type {
AuthorizationResponse,
Experiments,
Organization,
} from "api/typesGenerated";
import type { AuthorizationResponse, Organization } from "api/typesGenerated";
import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge";
import { Loader } from "components/Loader/Loader";
import { Sidebar as BaseSidebar } from "components/Sidebar/Sidebar";
import { Stack } from "components/Stack/Stack";
import { UserAvatar } from "components/UserAvatar/UserAvatar";
import type { Permissions } from "contexts/auth/permissions";
import { type ClassName, useClassName } from "hooks/useClassName";
import { useDashboard } from "modules/dashboard/useDashboard";
import { linkToUsers } from "modules/navigation";
import type { FC, ReactNode } from "react";
import { Link, NavLink } from "react-router-dom";

Expand All@@ -30,7 +26,7 @@ interface SidebarProps {
/** Organizations and their permissions or undefined if still fetching. */
organizations: OrganizationWithPermissions[] | undefined;
/** Site-wide permissions. */
permissions:AuthorizationResponse;
permissions:Permissions;
}

/**
Expand DownExpand Up@@ -72,7 +68,7 @@ interface DeploymentSettingsNavigationProps {
/** Whether a deployment setting page is being viewed. */
active: boolean;
/** Site-wide permissions. */
permissions:AuthorizationResponse;
permissions:Permissions;
}

/**
Expand DownExpand Up@@ -130,10 +126,11 @@ const DeploymentSettingsNavigation: FC<DeploymentSettingsNavigationProps> = ({
{permissions.viewDeploymentValues && (
<SidebarNavSubItem href="network">Network</SidebarNavSubItem>
)}
{/* All users can view workspace regions. */}
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This actually wasn't true, so Steven and I had to put this behind a condition

BrunoQuaresma and aslilac reacted with thumbs up emoji
<SidebarNavSubItem href="workspace-proxies">
Workspace Proxies
</SidebarNavSubItem>
{permissions.readWorkspaceProxies && (
<SidebarNavSubItem href="workspace-proxies">
Workspace Proxies
</SidebarNavSubItem>
)}
{permissions.viewDeploymentValues && (
<SidebarNavSubItem href="security">Security</SidebarNavSubItem>
)}
Expand All@@ -145,12 +142,14 @@ const DeploymentSettingsNavigation: FC<DeploymentSettingsNavigationProps> = ({
{permissions.viewAllUsers && (
<SidebarNavSubItem href="users">Users</SidebarNavSubItem>
)}
<SidebarNavSubItem href="notifications">
<Stack direction="row" alignItems="center" spacing={1}>
<span>Notifications</span>
<FeatureStageBadge contentType="beta" size="sm" />
</Stack>
</SidebarNavSubItem>
{permissions.viewNotificationTemplate && (
<SidebarNavSubItem href="notifications">
<Stack direction="row" alignItems="center" spacing={1}>
<span>Notifications</span>
<FeatureStageBadge contentType="beta" size="sm" />
</Stack>
</SidebarNavSubItem>
)}
</Stack>
)}
</div>
Expand All@@ -167,7 +166,7 @@ interface OrganizationsSettingsNavigationProps {
/** Organizations and their permissions or undefined if still fetching. */
organizations: OrganizationWithPermissions[] | undefined;
/** Site-wide permissions. */
permissions:AuthorizationResponse;
permissions:Permissions;
}

/**
Expand DownExpand Up@@ -241,8 +240,6 @@ interface OrganizationSettingsNavigationProps {
const OrganizationSettingsNavigation: FC<
OrganizationSettingsNavigationProps
> = ({ active, organization }) => {
const { experiments } = useDashboard();

return (
<>
<SidebarNavItem
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
import { Loader } from "components/Loader/Loader";
import {useManagementSettings } from "modules/management/ManagementSettingsLayout";
import {useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { ExternalAuthSettingsPageView } from "./ExternalAuthSettingsPageView";

const ExternalAuthSettingsPage: FC = () => {
const {deploymentValues } =useManagementSettings();
const {deploymentConfig } =useDeploymentSettings();

return (
<>
<Helmet>
<title>{pageTitle("External Authentication Settings")}</title>
</Helmet>

{deploymentValues ? (
<ExternalAuthSettingsPageView config={deploymentValues.config} />
) : (
<Loader />
)}
<ExternalAuthSettingsPageView config={deploymentConfig.config} />
</>
);
};
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp