Skip to content

Commit 86b61ef

Browse files
authored
fix: use correct permissions for CRUD of custom roles (coder#16854)
resolves coder/internal#428 The goal of the PR is to start using updateOrgRoles and deleteOrgRoles permissions to gate custom roles functionality ``` updateOrgRoles: { object: { resource_type: "assign_org_role", organization_id: organizationId, }, action: "update", }, deleteOrgRoles: { object: { resource_type: "assign_org_role", organization_id: organizationId, }, action: "delete", } ```
1 parent 101b62d commit 86b61ef

File tree

9 files changed

+93
-62
lines changed

9 files changed

+93
-62
lines changed

site/src/modules/permissions/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export type Permissions = {
66

77
export type PermissionName = keyof typeof permissionChecks;
88

9+
/**
10+
* Site-wide permission checks
11+
*/
912
export const permissionChecks = {
1013
viewAllUsers: {
1114
object: {

site/src/modules/permissions/organizations.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ export const organizationPermissionChecks = (organizationId: string) =>
7373
},
7474
action: "create",
7575
},
76+
updateOrgRoles: {
77+
object: {
78+
resource_type: "assign_org_role",
79+
organization_id: organizationId,
80+
},
81+
action: "update",
82+
},
83+
deleteOrgRoles: {
84+
object: {
85+
resource_type: "assign_org_role",
86+
organization_id: organizationId,
87+
},
88+
action: "delete",
89+
},
7690
viewProvisioners: {
7791
object: {
7892
resource_type: "provisioner_daemon",

site/src/pages/OrganizationSettingsPage/CustomRolesPage/CreateEditRolePage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ export const CreateEditRolePage: FC = () => {
4848
return (
4949
<RequirePermission
5050
isFeatureVisible={
51-
organizationPermissions.assignOrgRoles ||
52-
organizationPermissions.createOrgRoles
51+
role
52+
? organizationPermissions.updateOrgRoles
53+
: organizationPermissions.createOrgRoles
5354
}
5455
>
5556
<Helmet>
@@ -87,7 +88,6 @@ export const CreateEditRolePage: FC = () => {
8788
: createOrganizationRoleMutation.isLoading
8889
}
8990
organizationName={organizationName}
90-
canAssignOrgRole={organizationPermissions.assignOrgRoles}
9191
/>
9292
</RequirePermission>
9393
);

site/src/pages/OrganizationSettingsPage/CustomRolesPage/CreateEditRolePageView.stories.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export const Default: Story = {
2323
error: undefined,
2424
isLoading: false,
2525
organizationName: "my-org",
26-
canAssignOrgRole: true,
2726
},
2827
};
2928

@@ -81,7 +80,6 @@ export const InvalidCharsError: Story = {
8180
export const CannotEditRoleName: Story = {
8281
args: {
8382
...Default.args,
84-
canAssignOrgRole: false,
8583
},
8684
};
8785

site/src/pages/OrganizationSettingsPage/CustomRolesPage/CreateEditRolePageView.tsx

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export type CreateEditRolePageViewProps = {
4343
error?: unknown;
4444
isLoading: boolean;
4545
organizationName: string;
46-
canAssignOrgRole: boolean;
4746
allResources?: boolean;
4847
};
4948

@@ -53,7 +52,6 @@ export const CreateEditRolePageView: FC<CreateEditRolePageViewProps> = ({
5352
error,
5453
isLoading,
5554
organizationName,
56-
canAssignOrgRole,
5755
allResources = false,
5856
}) => {
5957
const navigate = useNavigate();
@@ -84,26 +82,24 @@ export const CreateEditRolePageView: FC<CreateEditRolePageViewProps> = ({
8482
title={`${role ? "Edit" : "Create"} Custom Role`}
8583
description="Set a name and permissions for this role."
8684
/>
87-
{canAssignOrgRole && (
88-
<div className="flex space-x-2 items-center">
89-
<Button
90-
variant="outline"
91-
onClick={() => {
92-
navigate(`/organizations/${organizationName}/roles`);
93-
}}
94-
>
95-
Cancel
96-
</Button>
97-
<Button
98-
onClick={() => {
99-
form.handleSubmit();
100-
}}
101-
>
102-
<Spinner loading={isLoading} />
103-
{role !== undefined ? "Save" : "Create Role"}
104-
</Button>
105-
</div>
106-
)}
85+
<div className="flex space-x-2 items-center">
86+
<Button
87+
variant="outline"
88+
onClick={() => {
89+
navigate(`/organizations/${organizationName}/roles`);
90+
}}
91+
>
92+
Cancel
93+
</Button>
94+
<Button
95+
onClick={() => {
96+
form.handleSubmit();
97+
}}
98+
>
99+
<Spinner loading={isLoading} />
100+
{role !== undefined ? "Save" : "Create Role"}
101+
</Button>
102+
</div>
107103
</Stack>
108104

109105
<VerticalForm onSubmit={form.handleSubmit}>
@@ -135,18 +131,16 @@ export const CreateEditRolePageView: FC<CreateEditRolePageViewProps> = ({
135131
allResources={allResources}
136132
/>
137133
</FormFields>
138-
{canAssignOrgRole && (
139-
<FormFooter>
140-
<Button onClick={onCancel} variant="outline">
141-
Cancel
142-
</Button>
134+
<FormFooter>
135+
<Button onClick={onCancel} variant="outline">
136+
Cancel
137+
</Button>
143138

144-
<Button type="submit" disabled={isLoading}>
145-
<Spinner loading={isLoading} />
146-
{role ? "Save role" : "Create Role"}
147-
</Button>
148-
</FormFooter>
149-
)}
139+
<Button type="submit" disabled={isLoading}>
140+
<Spinner loading={isLoading} />
141+
{role ? "Save role" : "Create Role"}
142+
</Button>
143+
</FormFooter>
150144
</VerticalForm>
151145
</>
152146
);

site/src/pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ export const CustomRolesPage: FC = () => {
8181
builtInRoles={builtInRoles}
8282
customRoles={customRoles}
8383
onDeleteRole={setRoleToDelete}
84-
canAssignOrgRole={organizationPermissions?.assignOrgRoles ?? false}
8584
canCreateOrgRole={organizationPermissions?.createOrgRoles ?? false}
85+
canUpdateOrgRole={organizationPermissions?.updateOrgRoles ?? false}
86+
canDeleteOrgRole={organizationPermissions?.deleteOrgRoles ?? false}
8687
isCustomRolesEnabled={isCustomRolesEnabled}
8788
/>
8889

site/src/pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPageView.stories.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const meta: Meta<typeof CustomRolesPageView> = {
1111
args: {
1212
builtInRoles: [MockRoleWithOrgPermissions],
1313
customRoles: [MockRoleWithOrgPermissions],
14-
canAssignOrgRole: true,
1514
canCreateOrgRole: true,
1615
isCustomRolesEnabled: true,
1716
},
@@ -31,7 +30,7 @@ export const NotEnabled: Story = {
3130
export const NotEnabledEmptyTable: Story = {
3231
args: {
3332
customRoles: [],
34-
canAssignOrgRole: true,
33+
canCreateOrgRole: true,
3534
isCustomRolesEnabled: false,
3635
},
3736
};
@@ -58,7 +57,6 @@ export const EmptyDisplayName: Story = {
5857
export const EmptyTableUserWithoutPermission: Story = {
5958
args: {
6059
customRoles: [],
61-
canAssignOrgRole: false,
6260
canCreateOrgRole: false,
6361
},
6462
};

site/src/pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPageView.tsx

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,19 @@ interface CustomRolesPageViewProps {
3434
builtInRoles: AssignableRoles[] | undefined;
3535
customRoles: AssignableRoles[] | undefined;
3636
onDeleteRole: (role: Role) => void;
37-
canAssignOrgRole: boolean;
3837
canCreateOrgRole: boolean;
38+
canUpdateOrgRole: boolean;
39+
canDeleteOrgRole: boolean;
3940
isCustomRolesEnabled: boolean;
4041
}
4142

4243
export const CustomRolesPageView: FC<CustomRolesPageViewProps> = ({
4344
builtInRoles,
4445
customRoles,
4546
onDeleteRole,
46-
canAssignOrgRole,
4747
canCreateOrgRole,
48+
canUpdateOrgRole,
49+
canDeleteOrgRole,
4850
isCustomRolesEnabled,
4951
}) => {
5052
return (
@@ -77,7 +79,9 @@ export const CustomRolesPageView: FC<CustomRolesPageViewProps> = ({
7779
<RoleTable
7880
roles={customRoles}
7981
isCustomRolesEnabled={isCustomRolesEnabled}
80-
canAssignOrgRole={canAssignOrgRole}
82+
canCreateOrgRole={canCreateOrgRole}
83+
canUpdateOrgRole={canUpdateOrgRole}
84+
canDeleteOrgRole={canDeleteOrgRole}
8185
onDeleteRole={onDeleteRole}
8286
/>
8387
<span>
@@ -90,7 +94,9 @@ export const CustomRolesPageView: FC<CustomRolesPageViewProps> = ({
9094
<RoleTable
9195
roles={builtInRoles}
9296
isCustomRolesEnabled={isCustomRolesEnabled}
93-
canAssignOrgRole={canAssignOrgRole}
97+
canCreateOrgRole={canCreateOrgRole}
98+
canUpdateOrgRole={canUpdateOrgRole}
99+
canDeleteOrgRole={canDeleteOrgRole}
94100
onDeleteRole={onDeleteRole}
95101
/>
96102
</Stack>
@@ -100,15 +106,19 @@ export const CustomRolesPageView: FC<CustomRolesPageViewProps> = ({
100106
interface RoleTableProps {
101107
roles: AssignableRoles[] | undefined;
102108
isCustomRolesEnabled: boolean;
103-
canAssignOrgRole: boolean;
109+
canCreateOrgRole: boolean;
110+
canUpdateOrgRole: boolean;
111+
canDeleteOrgRole: boolean;
104112
onDeleteRole: (role: Role) => void;
105113
}
106114

107115
const RoleTable: FC<RoleTableProps> = ({
108116
roles,
109117
isCustomRolesEnabled,
118+
canCreateOrgRole,
119+
canUpdateOrgRole,
120+
canDeleteOrgRole,
110121
onDeleteRole,
111-
canAssignOrgRole,
112122
}) => {
113123
const isLoading = roles === undefined;
114124
const isEmpty = Boolean(roles && roles.length === 0);
@@ -134,14 +144,14 @@ const RoleTable: FC<RoleTableProps> = ({
134144
<EmptyState
135145
message="No custom roles yet"
136146
description={
137-
canAssignOrgRole && isCustomRolesEnabled
147+
canCreateOrgRole && isCustomRolesEnabled
138148
? "Create your first custom role"
139149
: !isCustomRolesEnabled
140150
? "Upgrade to a premium license to create a custom role"
141151
: "You don't have permission to create a custom role"
142152
}
143153
cta={
144-
canAssignOrgRole &&
154+
canCreateOrgRole &&
145155
isCustomRolesEnabled && (
146156
<Button
147157
component={RouterLink}
@@ -165,7 +175,8 @@ const RoleTable: FC<RoleTableProps> = ({
165175
<RoleRow
166176
key={role.name}
167177
role={role}
168-
canAssignOrgRole={canAssignOrgRole}
178+
canUpdateOrgRole={canUpdateOrgRole}
179+
canDeleteOrgRole={canDeleteOrgRole}
169180
onDelete={() => onDeleteRole(role)}
170181
/>
171182
))}
@@ -179,11 +190,17 @@ const RoleTable: FC<RoleTableProps> = ({
179190

180191
interface RoleRowProps {
181192
role: AssignableRoles;
193+
canUpdateOrgRole: boolean;
194+
canDeleteOrgRole: boolean;
182195
onDelete: () => void;
183-
canAssignOrgRole: boolean;
184196
}
185197

186-
const RoleRow: FC<RoleRowProps> = ({ role, onDelete, canAssignOrgRole }) => {
198+
const RoleRow: FC<RoleRowProps> = ({
199+
role,
200+
onDelete,
201+
canUpdateOrgRole,
202+
canDeleteOrgRole,
203+
}) => {
187204
const navigate = useNavigate();
188205

189206
return (
@@ -195,20 +212,22 @@ const RoleRow: FC<RoleRowProps> = ({ role, onDelete, canAssignOrgRole }) => {
195212
</TableCell>
196213

197214
<TableCell>
198-
{!role.built_in && (
215+
{!role.built_in && (canUpdateOrgRole || canDeleteOrgRole) && (
199216
<MoreMenu>
200217
<MoreMenuTrigger>
201218
<ThreeDotsButton />
202219
</MoreMenuTrigger>
203220
<MoreMenuContent>
204-
<MoreMenuItem
205-
onClick={() => {
206-
navigate(role.name);
207-
}}
208-
>
209-
Edit
210-
</MoreMenuItem>
211-
{canAssignOrgRole && (
221+
{canUpdateOrgRole && (
222+
<MoreMenuItem
223+
onClick={() => {
224+
navigate(role.name);
225+
}}
226+
>
227+
Edit
228+
</MoreMenuItem>
229+
)}
230+
{canDeleteOrgRole && (
212231
<MoreMenuItem danger onClick={onDelete}>
213232
Delete&hellip;
214233
</MoreMenuItem>

site/src/testHelpers/entities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2900,6 +2900,8 @@ export const MockOrganizationPermissions: OrganizationPermissions = {
29002900
viewOrgRoles: true,
29012901
createOrgRoles: true,
29022902
assignOrgRoles: true,
2903+
updateOrgRoles: true,
2904+
deleteOrgRoles: true,
29032905
viewProvisioners: true,
29042906
viewProvisionerJobs: true,
29052907
viewIdpSyncSettings: true,
@@ -2916,6 +2918,8 @@ export const MockNoOrganizationPermissions: OrganizationPermissions = {
29162918
viewOrgRoles: false,
29172919
createOrgRoles: false,
29182920
assignOrgRoles: false,
2921+
updateOrgRoles: false,
2922+
deleteOrgRoles: false,
29192923
viewProvisioners: false,
29202924
viewProvisionerJobs: false,
29212925
viewIdpSyncSettings: false,

0 commit comments

Comments
 (0)
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