From 1c296a16411f466e484fa8a75a19a9c2d36e29fe Mon Sep 17 00:00:00 2001 From: Stan Silvert Date: Thu, 21 Apr 2022 11:03:48 -0400 Subject: [PATCH] Fine-grained permissions for user section. (#2451) --- .../key-value-form/AttributeForm.tsx | 9 ++++++- src/user/UserAttributes.tsx | 1 + src/user/UserForm.tsx | 1 + src/user/UserGroups.tsx | 2 ++ src/user/UsersSection.tsx | 27 ++++++++++++------- src/user/UsersTabs.tsx | 9 ++++++- src/user/routes/User.ts | 2 +- 7 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/components/key-value-form/AttributeForm.tsx b/src/components/key-value-form/AttributeForm.tsx index 80c1a1833c..18f7896db6 100644 --- a/src/components/key-value-form/AttributeForm.tsx +++ b/src/components/key-value-form/AttributeForm.tsx @@ -16,9 +16,15 @@ export type AttributesFormProps = { form: UseFormMethods; save?: (model: AttributeForm) => void; reset?: () => void; + fineGrainedAccess?: boolean; }; -export const AttributesForm = ({ form, reset, save }: AttributesFormProps) => { +export const AttributesForm = ({ + form, + reset, + save, + fineGrainedAccess, +}: AttributesFormProps) => { const { t } = useTranslation("roles"); const noSaveCancelButtons = !save && !reset; const { @@ -30,6 +36,7 @@ export const AttributesForm = ({ form, reset, save }: AttributesFormProps) => { diff --git a/src/user/UserAttributes.tsx b/src/user/UserAttributes.tsx index 9456de491f..751a2fd968 100644 --- a/src/user/UserAttributes.tsx +++ b/src/user/UserAttributes.tsx @@ -56,6 +56,7 @@ export const UserAttributes = ({ user: defaultUser }: UserAttributesProps) => { form.reset({ attributes: convertAttributes(), diff --git a/src/user/UserForm.tsx b/src/user/UserForm.tsx index 3954a465c5..0f3407370a 100644 --- a/src/user/UserForm.tsx +++ b/src/user/UserForm.tsx @@ -153,6 +153,7 @@ export const UserForm = ({ isHorizontal onSubmit={handleSubmit(save)} role="manage-users" + fineGrainedAccess={user?.access?.manage} className="pf-u-mt-lg" > {open && ( diff --git a/src/user/UserGroups.tsx b/src/user/UserGroups.tsx index 2426ce530b..dfde9939ac 100644 --- a/src/user/UserGroups.tsx +++ b/src/user/UserGroups.tsx @@ -216,6 +216,7 @@ export const UserGroups = ({ user }: UserGroupsProps) => { data-testid={`leave-${group.name}`} onClick={() => leave([group])} variant="link" + isDisabled={!user.access?.manageGroupMembership} > {t("leave")} @@ -286,6 +287,7 @@ export const UserGroups = ({ user }: UserGroupsProps) => { className="kc-join-group-button" onClick={toggleModal} data-testid="add-group-button" + isDisabled={!user.access?.manageGroupMembership} > {t("joinGroup")} diff --git a/src/user/UsersSection.tsx b/src/user/UsersSection.tsx index 42cb5e1c4a..60168b2354 100644 --- a/src/user/UsersSection.tsx +++ b/src/user/UsersSection.tsx @@ -23,6 +23,7 @@ import { SearchIcon, WarningTriangleIcon, } from "@patternfly/react-icons"; +import type { IRowData } from "@patternfly/react-table"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; @@ -113,7 +114,10 @@ export default function UsersSection() { } try { - const users = await adminClient.users.find({ ...params }); + const users = await adminClient.users.find({ + briefRepresentation: true, + ...params, + }); if (realm?.bruteForceProtected) { const brutes = await Promise.all( users.map((user: BruteUser) => @@ -344,15 +348,20 @@ export default function UsersSection() { ) } toolbarItem={toolbar} - actions={[ - { - title: t("common:delete"), - onRowClick: (user) => { - setSelectedRows([user]); - toggleDeleteDialog(); + actionResolver={(rowData: IRowData) => { + const user: UserRepresentation = rowData.data; + if (!user.access?.manage) return []; + + return [ + { + title: t("common:delete"), + onClick: () => { + setSelectedRows([user]); + toggleDeleteDialog(); + }, }, - }, - ]} + ]; + }} columns={[ { name: "username", diff --git a/src/user/UsersTabs.tsx b/src/user/UsersTabs.tsx index b0b75e1584..2cbe1c6a69 100644 --- a/src/user/UsersTabs.tsx +++ b/src/user/UsersTabs.tsx @@ -157,11 +157,16 @@ const UsersTabs = () => { dropdownItems={[ toggleImpersonateDialog()} > {t("impersonate")} , - toggleDeleteDialog()}> + toggleDeleteDialog()} + > {t("common:delete")} , ]} @@ -196,6 +201,7 @@ const UsersTabs = () => { {t("common:credentials")}} > @@ -203,6 +209,7 @@ const UsersTabs = () => { {t("roleMapping")}} > diff --git a/src/user/routes/User.ts b/src/user/routes/User.ts index 75ffb33403..f9baf858a7 100644 --- a/src/user/routes/User.ts +++ b/src/user/routes/User.ts @@ -15,7 +15,7 @@ export const UserRoute: RouteDef = { path: "/:realm/users/:id/:tab", component: lazy(() => import("../UsersTabs")), breadcrumb: (t) => t("users:userDetails"), - access: "manage-users", + access: "view-users", }; export const toUser = (params: UserParams): LocationDescriptorObject => ({