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

Commit1d925ab

Browse files
fix: ensure user admins can always see users table (#15226)
Closes#15212## Changes made- Updated logic so that proxy config is only requested when appropriate,instead of for all users on all deployment pages- Split up the main context provider for the `/deployment` and`/organizations` routes, and updated layout logic for`ManagementSettingsLayout` layout component. This ensures the sidebar isalways visible, even if request errors happen- Added additional routing safeguards to make sure that even if a usercan view one page in the deployment section, they won't be able tonavigate directly to any arbitrary deployment page- Updated logic for sidebar navigation to ensure that nav items onlyappear when the user truly has permission- Centralized a lot of the orgs logic into the `useAuthenticated` hook- Added additional check cases to the `permissions.tsx` file, to givemore granularity, and added missing type-checking- Extended the API for the `RequirePermissions` component to let itredirect users anywhere- Updated some of our testing setup files to ensure that types weredefined correctly---------Co-authored-by: McKayla Washburn <mckayla@hey.com>
1 parentfd60e1c commit1d925ab

File tree

26 files changed

+243
-189
lines changed

26 files changed

+243
-189
lines changed

‎.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
"unauthenticate",
176176
"unconvert",
177177
"untar",
178+
"userauth",
178179
"userspace",
179180
"VMID",
180181
"walkthrough",

‎site/e2e/global.setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ test("setup deployment", async ({ page }) => {
3535
expect(constants.license.split(".").length).toBe(3);// otherwise it's invalid
3636

3737
awaitpage.goto("/deployment/licenses",{waitUntil:"domcontentloaded"});
38+
awaitexpect(page).toHaveTitle("License Settings - Coder");
3839

3940
awaitpage.getByText("Add a license").click();
4041
awaitpage.getByRole("textbox").fill(constants.license);

‎site/jest.setup.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import"@testing-library/jest-dom";
22
import"jest-location-mock";
33
import{cleanup}from"@testing-library/react";
4-
importcryptofrom"crypto";
4+
importcryptofrom"node:crypto";
55
import{useMemo}from"react";
66
importtype{Region}from"api/typesGenerated";
77
importtype{ProxyLatencyReport}from"contexts/useProxyLatency";
@@ -48,9 +48,7 @@ global.ResizeObserver = require("resize-observer-polyfill");
4848
// Polyfill the getRandomValues that is used on utils/random.ts
4949
Object.defineProperty(global.self,"crypto",{
5050
value:{
51-
getRandomValues:function(buffer:Buffer){
52-
returncrypto.randomFillSync(buffer);
53-
},
51+
getRandomValues:crypto.randomFillSync,
5452
},
5553
});
5654

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

75-
// This is needed because we are compiling under `--isolatedModules`
73+
//biome-ignore lint/complexity/noUselessEmptyExport:This is needed because we are compiling under `--isolatedModules`
7674
export{};

‎site/src/contexts/auth/permissions.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
importtype{AuthorizationCheck}from"api/typesGenerated";
2+
13
exportconstchecks={
24
viewAllUsers:"viewAllUsers",
35
updateUsers:"updateUsers",
@@ -11,13 +13,20 @@ export const checks = {
1113
viewUpdateCheck:"viewUpdateCheck",
1214
viewExternalAuthConfig:"viewExternalAuthConfig",
1315
viewDeploymentStats:"viewDeploymentStats",
16+
readWorkspaceProxies:"readWorkspaceProxies",
1417
editWorkspaceProxies:"editWorkspaceProxies",
1518
createOrganization:"createOrganization",
1619
editAnyOrganization:"editAnyOrganization",
1720
viewAnyGroup:"viewAnyGroup",
1821
createGroup:"createGroup",
1922
viewAllLicenses:"viewAllLicenses",
20-
}asconst;
23+
viewNotificationTemplate:"viewNotificationTemplate",
24+
}asconstsatisfiesRecord<string,string>;
25+
26+
// Type expression seems a little redundant (`keyof typeof checks` has the same
27+
// result), just because each key-value pair is currently symmetrical; this may
28+
// change down the line
29+
typePermissionValue=(typeofchecks)[keyoftypeofchecks];
2130

2231
exportconstpermissionsToCheck={
2332
[checks.viewAllUsers]:{
@@ -94,6 +103,12 @@ export const permissionsToCheck = {
94103
},
95104
action:"read",
96105
},
106+
[checks.readWorkspaceProxies]:{
107+
object:{
108+
resource_type:"workspace_proxy",
109+
},
110+
action:"read",
111+
},
97112
[checks.editWorkspaceProxies]:{
98113
object:{
99114
resource_type:"workspace_proxy",
@@ -116,7 +131,6 @@ export const permissionsToCheck = {
116131
[checks.viewAnyGroup]:{
117132
object:{
118133
resource_type:"group",
119-
org_id:"any",
120134
},
121135
action:"read",
122136
},
@@ -132,6 +146,12 @@ export const permissionsToCheck = {
132146
},
133147
action:"read",
134148
},
135-
}asconst;
149+
[checks.viewNotificationTemplate]:{
150+
object:{
151+
resource_type:"notification_template",
152+
},
153+
action:"read",
154+
},
155+
}asconstsatisfiesRecord<PermissionValue,AuthorizationCheck>;
136156

137-
exporttypePermissions=Record<keyoftypeofpermissionsToCheck,boolean>;
157+
exporttypePermissions=Record<PermissionValue,boolean>;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
importtype{DeploymentConfig}from"api/api";
2+
import{deploymentConfig}from"api/queries/deployment";
3+
import{ErrorAlert}from"components/Alert/ErrorAlert";
4+
import{Loader}from"components/Loader/Loader";
5+
import{useAuthenticated}from"contexts/auth/RequireAuth";
6+
import{RequirePermission}from"contexts/auth/RequirePermission";
7+
import{typeFC,createContext,useContext}from"react";
8+
import{useQuery}from"react-query";
9+
import{Outlet}from"react-router-dom";
10+
11+
exportconstDeploymentSettingsContext=createContext<
12+
DeploymentSettingsValue|undefined
13+
>(undefined);
14+
15+
typeDeploymentSettingsValue=Readonly<{
16+
deploymentConfig:DeploymentConfig;
17+
}>;
18+
19+
exportconstuseDeploymentSettings=():DeploymentSettingsValue=>{
20+
constcontext=useContext(DeploymentSettingsContext);
21+
if(!context){
22+
thrownewError(
23+
`${useDeploymentSettings.name} should be used inside of${DeploymentSettingsProvider.name}`,
24+
);
25+
}
26+
27+
returncontext;
28+
};
29+
30+
constDeploymentSettingsProvider:FC=()=>{
31+
const{ permissions}=useAuthenticated();
32+
constdeploymentConfigQuery=useQuery(deploymentConfig());
33+
34+
// The deployment settings page also contains users, audit logs, groups and
35+
// organizations, so this page must be visible if you can see any of these.
36+
constcanViewDeploymentSettingsPage=
37+
permissions.viewDeploymentValues||
38+
permissions.viewAllUsers||
39+
permissions.editAnyOrganization||
40+
permissions.viewAnyAuditLog;
41+
42+
// Not a huge problem to unload the content in the event of an error,
43+
// because the sidebar rendering isn't tied to this. Even if the user hits
44+
// a 403 error, they'll still have navigation options
45+
if(deploymentConfigQuery.error){
46+
return<ErrorAlerterror={deploymentConfigQuery.error}/>;
47+
}
48+
49+
if(!deploymentConfigQuery.data){
50+
return<Loader/>;
51+
}
52+
53+
return(
54+
<RequirePermissionisFeatureVisible={canViewDeploymentSettingsPage}>
55+
<DeploymentSettingsContext.Provider
56+
value={{deploymentConfig:deploymentConfigQuery.data}}
57+
>
58+
<Outlet/>
59+
</DeploymentSettingsContext.Provider>
60+
</RequirePermission>
61+
);
62+
};
63+
64+
exportdefaultDeploymentSettingsProvider;

‎site/src/modules/management/ManagementSettingsLayout.tsx

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
importtype{DeploymentConfig}from"api/api";
2-
import{deploymentConfig}from"api/queries/deployment";
31
importtype{AuthorizationResponse,Organization}from"api/typesGenerated";
4-
import{ErrorAlert}from"components/Alert/ErrorAlert";
52
import{Loader}from"components/Loader/Loader";
63
import{Margins}from"components/Margins/Margins";
74
import{Stack}from"components/Stack/Stack";
85
import{useAuthenticated}from"contexts/auth/RequireAuth";
96
import{RequirePermission}from"contexts/auth/RequirePermission";
107
import{useDashboard}from"modules/dashboard/useDashboard";
118
import{typeFC,Suspense,createContext,useContext}from"react";
12-
import{useQuery}from"react-query";
139
import{Outlet,useParams}from"react-router-dom";
1410
import{Sidebar}from"./Sidebar";
1511

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

2016
typeManagementSettingsValue=Readonly<{
21-
deploymentValues:DeploymentConfig;
2217
organizations:readonlyOrganization[];
2318
organization?:Organization;
2419
}>;
@@ -48,15 +43,8 @@ export const canEditOrganization = (
4843
);
4944
};
5045

51-
/**
52-
* A multi-org capable settings page layout.
53-
*
54-
* If multi-org is not enabled or licensed, this is the wrong layout to use.
55-
* See DeploySettingsLayoutInner instead.
56-
*/
57-
exportconstManagementSettingsLayout:FC=()=>{
46+
constManagementSettingsLayout:FC=()=>{
5847
const{ permissions}=useAuthenticated();
59-
constdeploymentConfigQuery=useQuery(deploymentConfig());
6048
const{ organizations}=useDashboard();
6149
const{organization:orgName}=useParams()as{
6250
organization?:string;
@@ -70,14 +58,6 @@ export const ManagementSettingsLayout: FC = () => {
7058
permissions.editAnyOrganization||
7159
permissions.viewAnyAuditLog;
7260

73-
if(deploymentConfigQuery.error){
74-
return<ErrorAlerterror={deploymentConfigQuery.error}/>;
75-
}
76-
77-
if(!deploymentConfigQuery.data){
78-
return<Loader/>;
79-
}
80-
8161
constorganization=
8262
organizations&&orgName
8363
?organizations.find((org)=>org.name===orgName)
@@ -87,15 +67,14 @@ export const ManagementSettingsLayout: FC = () => {
8767
<RequirePermissionisFeatureVisible={canViewDeploymentSettingsPage}>
8868
<ManagementSettingsContext.Provider
8969
value={{
90-
deploymentValues:deploymentConfigQuery.data,
9170
organizations,
9271
organization,
9372
}}
9473
>
9574
<Margins>
9675
<Stackcss={{padding:"48px 0"}}direction="row"spacing={6}>
9776
<Sidebar/>
98-
<maincss={{width:"100%"}}>
77+
<maincss={{flexGrow:1}}>
9978
<Suspensefallback={<Loader/>}>
10079
<Outlet/>
10180
</Suspense>
@@ -106,3 +85,5 @@ export const ManagementSettingsLayout: FC = () => {
10685
</RequirePermission>
10786
);
10887
};
88+
89+
exportdefaultManagementSettingsLayout;

‎site/src/modules/management/SidebarView.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
importtype{Meta,StoryObj}from"@storybook/react";
22
import{
3+
MockNoPermissions,
34
MockOrganization,
45
MockOrganization2,
56
MockPermissions,
@@ -96,7 +97,7 @@ export const NoDeploymentValues: Story = {
9697

9798
exportconstNoPermissions:Story={
9899
args:{
99-
permissions:{},
100+
permissions:MockNoPermissions,
100101
},
101102
};
102103

‎site/src/modules/management/SidebarView.tsx

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@ import { cx } from "@emotion/css";
22
importtype{Interpolation,Theme}from"@emotion/react";
33
importAddIconfrom"@mui/icons-material/Add";
44
importSettingsIconfrom"@mui/icons-material/Settings";
5-
importtype{
6-
AuthorizationResponse,
7-
Experiments,
8-
Organization,
9-
}from"api/typesGenerated";
5+
importtype{AuthorizationResponse,Organization}from"api/typesGenerated";
106
import{FeatureStageBadge}from"components/FeatureStageBadge/FeatureStageBadge";
117
import{Loader}from"components/Loader/Loader";
128
import{SidebarasBaseSidebar}from"components/Sidebar/Sidebar";
139
import{Stack}from"components/Stack/Stack";
1410
import{UserAvatar}from"components/UserAvatar/UserAvatar";
11+
importtype{Permissions}from"contexts/auth/permissions";
1512
import{typeClassName,useClassName}from"hooks/useClassName";
1613
import{useDashboard}from"modules/dashboard/useDashboard";
17-
import{linkToUsers}from"modules/navigation";
1814
importtype{FC,ReactNode}from"react";
1915
import{Link,NavLink}from"react-router-dom";
2016

@@ -30,7 +26,7 @@ interface SidebarProps {
3026
/** Organizations and their permissions or undefined if still fetching. */
3127
organizations:OrganizationWithPermissions[]|undefined;
3228
/** Site-wide permissions. */
33-
permissions:AuthorizationResponse;
29+
permissions:Permissions;
3430
}
3531

3632
/**
@@ -72,7 +68,7 @@ interface DeploymentSettingsNavigationProps {
7268
/** Whether a deployment setting page is being viewed. */
7369
active:boolean;
7470
/** Site-wide permissions. */
75-
permissions:AuthorizationResponse;
71+
permissions:Permissions;
7672
}
7773

7874
/**
@@ -130,10 +126,11 @@ const DeploymentSettingsNavigation: FC<DeploymentSettingsNavigationProps> = ({
130126
{permissions.viewDeploymentValues&&(
131127
<SidebarNavSubItemhref="network">Network</SidebarNavSubItem>
132128
)}
133-
{/* All users can view workspace regions. */}
134-
<SidebarNavSubItemhref="workspace-proxies">
135-
Workspace Proxies
136-
</SidebarNavSubItem>
129+
{permissions.readWorkspaceProxies&&(
130+
<SidebarNavSubItemhref="workspace-proxies">
131+
Workspace Proxies
132+
</SidebarNavSubItem>
133+
)}
137134
{permissions.viewDeploymentValues&&(
138135
<SidebarNavSubItemhref="security">Security</SidebarNavSubItem>
139136
)}
@@ -145,12 +142,14 @@ const DeploymentSettingsNavigation: FC<DeploymentSettingsNavigationProps> = ({
145142
{permissions.viewAllUsers&&(
146143
<SidebarNavSubItemhref="users">Users</SidebarNavSubItem>
147144
)}
148-
<SidebarNavSubItemhref="notifications">
149-
<Stackdirection="row"alignItems="center"spacing={1}>
150-
<span>Notifications</span>
151-
<FeatureStageBadgecontentType="beta"size="sm"/>
152-
</Stack>
153-
</SidebarNavSubItem>
145+
{permissions.viewNotificationTemplate&&(
146+
<SidebarNavSubItemhref="notifications">
147+
<Stackdirection="row"alignItems="center"spacing={1}>
148+
<span>Notifications</span>
149+
<FeatureStageBadgecontentType="beta"size="sm"/>
150+
</Stack>
151+
</SidebarNavSubItem>
152+
)}
154153
</Stack>
155154
)}
156155
</div>
@@ -167,7 +166,7 @@ interface OrganizationsSettingsNavigationProps {
167166
/** Organizations and their permissions or undefined if still fetching. */
168167
organizations:OrganizationWithPermissions[]|undefined;
169168
/** Site-wide permissions. */
170-
permissions:AuthorizationResponse;
169+
permissions:Permissions;
171170
}
172171

173172
/**
@@ -241,8 +240,6 @@ interface OrganizationSettingsNavigationProps {
241240
constOrganizationSettingsNavigation:FC<
242241
OrganizationSettingsNavigationProps
243242
>=({ active, organization})=>{
244-
const{ experiments}=useDashboard();
245-
246243
return(
247244
<>
248245
<SidebarNavItem

‎site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
import{Loader}from"components/Loader/Loader";
2-
import{useManagementSettings}from"modules/management/ManagementSettingsLayout";
2+
import{useDeploymentSettings}from"modules/management/DeploymentSettingsProvider";
33
importtype{FC}from"react";
44
import{Helmet}from"react-helmet-async";
55
import{pageTitle}from"utils/page";
66
import{ExternalAuthSettingsPageView}from"./ExternalAuthSettingsPageView";
77

88
constExternalAuthSettingsPage:FC=()=>{
9-
const{deploymentValues}=useManagementSettings();
9+
const{deploymentConfig}=useDeploymentSettings();
1010

1111
return(
1212
<>
1313
<Helmet>
1414
<title>{pageTitle("External Authentication Settings")}</title>
1515
</Helmet>
16-
17-
{deploymentValues ?(
18-
<ExternalAuthSettingsPageViewconfig={deploymentValues.config}/>
19-
) :(
20-
<Loader/>
21-
)}
16+
<ExternalAuthSettingsPageViewconfig={deploymentConfig.config}/>
2217
</>
2318
);
2419
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp