Skip to content

Commit bc4fbac

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 bc4fbac

File tree

6 files changed

+90
-34
lines changed

6 files changed

+90
-34
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: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,69 @@ import {
1111
} from "react-router-dom";
1212
import { getErrorMessage } from "api/errors";
1313
import { groups } from "api/queries/groups";
14+
import { organizationPermissions } from "api/queries/organizations";
1415
import type { Organization } from "api/typesGenerated";
16+
import { EmptyState } from "components/EmptyState/EmptyState";
1517
import { displayError } from "components/GlobalSnackbar/utils";
18+
import { Loader } from "components/Loader/Loader";
1619
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader";
17-
import { useAuthenticated } from "contexts/auth/RequireAuth";
18-
import { useDashboard } from "modules/dashboard/useDashboard";
1920
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
2021
import { pageTitle } from "utils/page";
2122
import { useOrganizationSettings } from "../ManagementSettingsLayout";
2223
import GroupsPageView from "./GroupsPageView";
2324

2425
export const GroupsPage: FC = () => {
25-
const { permissions } = useAuthenticated();
26-
const { createGroup: canCreateGroup } = permissions;
2726
const feats = useFeatureVisibility();
28-
const { experiments } = useDashboard();
2927
const location = useLocation();
30-
const { organization = "default" } = useParams() as { organization: string };
31-
const groupsQuery = useQuery(groups(organization));
28+
const { organization: organizationName } = useParams() as {
29+
organization: string;
30+
};
31+
const groupsQuery = useQuery(
32+
organizationName
33+
? groups(organizationName)
34+
: { enabled: false },
35+
);
3236
const { organizations } = useOrganizationSettings();
37+
// TODO: If we could query permissions based on the name then we would not
38+
// have to cascade off the organizations query.
39+
const organization = organizations?.find((o) => o.name === organizationName);
40+
const permissionsQuery = useQuery(
41+
organization
42+
? organizationPermissions(organization.id)
43+
: { enabled: false },
44+
);
3345

3446
useEffect(() => {
3547
if (groupsQuery.error) {
3648
displayError(
37-
getErrorMessage(groupsQuery.error, "Error on loading groups."),
49+
getErrorMessage(groupsQuery.error, "Unable to load groups."),
3850
);
3951
}
4052
}, [groupsQuery.error]);
4153

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 />;
54+
useEffect(() => {
55+
if (permissionsQuery.error) {
56+
displayError(
57+
getErrorMessage(permissionsQuery.error, "Unable to load permissions."),
58+
);
59+
}
60+
}, [permissionsQuery.error]);
61+
62+
if (!organizations) {
63+
return <Loader />;
64+
}
65+
66+
if (!organizationName) {
67+
const defaultName = getOrganizationNameByDefault(organizations);
68+
if (defaultName) {
69+
return <Navigate to={`/organizations/${defaultName}/groups`} replace />;
70+
}
71+
return <EmptyState message="No default organization found" />;
72+
}
73+
74+
const permissions = permissionsQuery.data
75+
if (!permissions) {
76+
return <Loader />;
4877
}
4978

5079
return (
@@ -56,7 +85,7 @@ export const GroupsPage: FC = () => {
5685
<PageHeader
5786
actions={
5887
<>
59-
{canCreateGroup && feats.template_rbac && (
88+
{permissions.createGroup && feats.template_rbac && (
6089
<Button
6190
component={RouterLink}
6291
startIcon={<GroupAdd />}
@@ -73,7 +102,7 @@ export const GroupsPage: FC = () => {
73102

74103
<GroupsPageView
75104
groups={groupsQuery.data}
76-
canCreateGroup={canCreateGroup}
105+
canCreateGroup={permissions.createGroup}
77106
isTemplateRBACEnabled={feats.template_rbac}
78107
/>
79108
</>

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)