Skip to content

Commit 402cd88

Browse files
committed
Scope create group button to organizations
You will only see the button if you can create a group in that org. Now that the old site-wide group permission is unused, remove it.
1 parent 9d0eb17 commit 402cd88

File tree

6 files changed

+89
-41
lines changed

6 files changed

+89
-41
lines changed

site/src/api/queries/organizations.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,24 @@ export const organizations = () => {
107107
queryFn: () => API.getOrganizations(),
108108
};
109109
};
110+
111+
/**
112+
* Fetch permissions for a single organization.
113+
*/
114+
export const organizationPermissions = (organizationId: string) => {
115+
return {
116+
queryKey: ["organization", organizationId, "permissions"],
117+
queryFn: () =>
118+
API.checkAuthorization({
119+
checks: {
120+
createGroup: {
121+
object: {
122+
resource_type: "group",
123+
organization_id: organizationId,
124+
},
125+
action: "create",
126+
},
127+
},
128+
}),
129+
};
130+
};

site/src/contexts/auth/permissions.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export const checks = {
66
createAnyTemplate: "createAnyTemplate",
77
updateAllTemplates: "updateAllTemplates",
88
viewDeploymentValues: "viewDeploymentValues",
9-
createGroup: "createGroup",
109
viewUpdateCheck: "viewUpdateCheck",
1110
viewExternalAuthConfig: "viewExternalAuthConfig",
1211
viewDeploymentStats: "viewDeploymentStats",
@@ -58,12 +57,6 @@ export const permissionsToCheck = {
5857
},
5958
action: "read",
6059
},
61-
[checks.createGroup]: {
62-
object: {
63-
resource_type: "group",
64-
},
65-
action: "create",
66-
},
6760
[checks.viewUpdateCheck]: {
6861
object: {
6962
resource_type: "deployment_config",

site/src/pages/GroupsPage/GroupsPage.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,41 @@ import { Helmet } from "react-helmet-async";
33
import { useQuery } from "react-query";
44
import { getErrorMessage } from "api/errors";
55
import { groups } from "api/queries/groups";
6+
import { organizationPermissions } from "api/queries/organizations";
67
import { displayError } from "components/GlobalSnackbar/utils";
7-
import { useAuthenticated } from "contexts/auth/RequireAuth";
8+
import { Loader } from "components/Loader/Loader";
89
import { useDashboard } from "modules/dashboard/useDashboard";
910
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
1011
import { pageTitle } from "utils/page";
1112
import GroupsPageView from "./GroupsPageView";
1213

1314
export const GroupsPage: FC = () => {
14-
const { permissions } = useAuthenticated();
1515
const { organizationId } = useDashboard();
16-
const { createGroup: canCreateGroup } = permissions;
1716
const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility();
1817
const groupsQuery = useQuery(groups(organizationId));
18+
const permissionsQuery = useQuery(organizationPermissions(organizationId));
1919

2020
useEffect(() => {
2121
if (groupsQuery.error) {
2222
displayError(
23-
getErrorMessage(groupsQuery.error, "Error on loading groups."),
23+
getErrorMessage(groupsQuery.error, "Unable to load groups."),
2424
);
2525
}
2626
}, [groupsQuery.error]);
2727

28+
useEffect(() => {
29+
if (permissionsQuery.error) {
30+
displayError(
31+
getErrorMessage(permissionsQuery.error, "Unable to load permissions."),
32+
);
33+
}
34+
}, [permissionsQuery.error]);
35+
36+
const permissions = permissionsQuery.data;
37+
if (!permissions) {
38+
return <Loader />;
39+
}
40+
2841
return (
2942
<>
3043
<Helmet>
@@ -33,7 +46,7 @@ export const GroupsPage: FC = () => {
3346

3447
<GroupsPageView
3548
groups={groupsQuery.data}
36-
canCreateGroup={canCreateGroup}
49+
canCreateGroup={permissions.createGroup}
3750
isTemplateRBACEnabled={isTemplateRBACEnabled}
3851
/>
3952
</>

site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,69 @@ import Button from "@mui/material/Button";
33
import { type FC, useEffect } from "react";
44
import { Helmet } from "react-helmet-async";
55
import { useQuery } from "react-query";
6-
import {
7-
Navigate,
8-
Link as RouterLink,
9-
useLocation,
10-
useParams,
11-
} from "react-router-dom";
6+
import { Navigate, Link as RouterLink, useParams } from "react-router-dom";
127
import { getErrorMessage } from "api/errors";
138
import { groups } from "api/queries/groups";
9+
import { organizationPermissions } from "api/queries/organizations";
1410
import type { Organization } from "api/typesGenerated";
11+
import { EmptyState } from "components/EmptyState/EmptyState";
1512
import { displayError } from "components/GlobalSnackbar/utils";
13+
import { Loader } from "components/Loader/Loader";
1614
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader";
17-
import { useAuthenticated } from "contexts/auth/RequireAuth";
18-
import { useDashboard } from "modules/dashboard/useDashboard";
1915
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
2016
import { pageTitle } from "utils/page";
2117
import { useOrganizationSettings } from "../ManagementSettingsLayout";
2218
import GroupsPageView from "./GroupsPageView";
2319

2420
export const GroupsPage: FC = () => {
25-
const { permissions } = useAuthenticated();
26-
const { createGroup: canCreateGroup } = permissions;
2721
const feats = useFeatureVisibility();
28-
const { experiments } = useDashboard();
29-
const location = useLocation();
30-
const { organization = "default" } = useParams() as { organization: string };
31-
const groupsQuery = useQuery(groups(organization));
22+
const { organization: organizationName } = useParams() as {
23+
organization: string;
24+
};
25+
const groupsQuery = useQuery(
26+
organizationName ? groups(organizationName) : { enabled: false },
27+
);
3228
const { organizations } = useOrganizationSettings();
29+
// TODO: If we could query permissions based on the name then we would not
30+
// have to cascade off the organizations query.
31+
const organization = organizations?.find((o) => o.name === organizationName);
32+
const permissionsQuery = useQuery(
33+
organization
34+
? organizationPermissions(organization.id)
35+
: { enabled: false },
36+
);
3337

3438
useEffect(() => {
3539
if (groupsQuery.error) {
3640
displayError(
37-
getErrorMessage(groupsQuery.error, "Error on loading groups."),
41+
getErrorMessage(groupsQuery.error, "Unable to load groups."),
3842
);
3943
}
4044
}, [groupsQuery.error]);
4145

42-
const canViewOrganizations =
43-
feats.multiple_organizations && experiments.includes("multi-organization");
44-
if (canViewOrganizations && location.pathname === "/deployment/groups") {
45-
const defaultName =
46-
getOrganizationNameByDefault(organizations) ?? "default";
47-
return <Navigate to={`/organizations/${defaultName}/groups`} replace />;
46+
useEffect(() => {
47+
if (permissionsQuery.error) {
48+
displayError(
49+
getErrorMessage(permissionsQuery.error, "Unable to load permissions."),
50+
);
51+
}
52+
}, [permissionsQuery.error]);
53+
54+
if (!organizations) {
55+
return <Loader />;
56+
}
57+
58+
if (!organizationName) {
59+
const defaultName = getOrganizationNameByDefault(organizations);
60+
if (defaultName) {
61+
return <Navigate to={`/organizations/${defaultName}/groups`} replace />;
62+
}
63+
return <EmptyState message="No default organization found" />;
64+
}
65+
66+
const permissions = permissionsQuery.data;
67+
if (!permissions) {
68+
return <Loader />;
4869
}
4970

5071
return (
@@ -56,7 +77,7 @@ export const GroupsPage: FC = () => {
5677
<PageHeader
5778
actions={
5879
<>
59-
{canCreateGroup && feats.template_rbac && (
80+
{permissions.createGroup && feats.template_rbac && (
6081
<Button
6182
component={RouterLink}
6283
startIcon={<GroupAdd />}
@@ -73,7 +94,7 @@ export const GroupsPage: FC = () => {
7394

7495
<GroupsPageView
7596
groups={groupsQuery.data}
76-
canCreateGroup={canCreateGroup}
97+
canCreateGroup={permissions.createGroup}
7798
isTemplateRBACEnabled={feats.template_rbac}
7899
/>
79100
</>

site/src/pages/UsersPage/UsersLayout.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import GroupAdd from "@mui/icons-material/GroupAddOutlined";
22
import PersonAdd from "@mui/icons-material/PersonAddOutlined";
33
import Button from "@mui/material/Button";
44
import { type FC, Suspense } from "react";
5+
import { useQuery } from "react-query";
56
import {
67
Link as RouterLink,
78
Outlet,
89
useNavigate,
910
useLocation,
1011
} from "react-router-dom";
12+
import { organizationPermissions } from "api/queries/organizations";
1113
import { Loader } from "components/Loader/Loader";
1214
import { Margins } from "components/Margins/Margins";
1315
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader";
@@ -19,13 +21,12 @@ import { USERS_LINK } from "modules/navigation";
1921

2022
export const UsersLayout: FC = () => {
2123
const { permissions } = useAuthenticated();
22-
const { experiments } = useDashboard();
23-
const { createUser: canCreateUser, createGroup: canCreateGroup } =
24-
permissions;
24+
const { experiments, organizationId } = useDashboard();
2525
const navigate = useNavigate();
2626
const feats = useFeatureVisibility();
2727
const location = useLocation();
2828
const activeTab = location.pathname.endsWith("groups") ? "groups" : "users";
29+
const permissionsQuery = useQuery(organizationPermissions(organizationId));
2930

3031
const canViewOrganizations =
3132
feats.multiple_organizations && experiments.includes("multi-organization");
@@ -36,7 +37,7 @@ export const UsersLayout: FC = () => {
3637
<PageHeader
3738
actions={
3839
<>
39-
{canCreateUser && (
40+
{permissions.createUser && (
4041
<Button
4142
onClick={() => {
4243
navigate("/users/create");
@@ -46,7 +47,7 @@ export const UsersLayout: FC = () => {
4647
Create user
4748
</Button>
4849
)}
49-
{canCreateGroup && feats.template_rbac && (
50+
{permissionsQuery.data?.createGroup && feats.template_rbac && (
5051
<Button
5152
component={RouterLink}
5253
startIcon={<GroupAdd />}

site/src/testHelpers/entities.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2472,7 +2472,6 @@ export const MockTemplateExample2: TypesGen.TemplateExample = {
24722472
};
24732473

24742474
export const MockPermissions: Permissions = {
2475-
createGroup: true,
24762475
createAnyTemplate: true,
24772476
createUser: true,
24782477
updateAllTemplates: true,

0 commit comments

Comments
 (0)