From d08b55ae9a9c6ed59f4c6aa27d2dbb63e23f0f4a Mon Sep 17 00:00:00 2001 From: Jenny <32821331+jenny-s51@users.noreply.github.com> Date: Wed, 24 Nov 2021 11:19:28 -0500 Subject: [PATCH] Realm settings(client policies): add/edit client-updater-source-groups condition (#1577) --- src/clients/scopes/AddScopeDialog.tsx | 1 + src/components/dynamic/DynamicComponents.tsx | 2 +- .../dynamic/MultivaluedChipsComponent.tsx | 172 ++++++++++++++++++ src/components/group/GroupPickerDialog.tsx | 13 +- src/groups/GroupTable.tsx | 2 +- .../NewClientPolicyCondition.tsx | 22 ++- src/realm-settings/RealmSettingsSection.css | 4 + src/user/UserForm.tsx | 4 +- src/user/user-section.css | 5 + 9 files changed, 211 insertions(+), 14 deletions(-) create mode 100644 src/components/dynamic/MultivaluedChipsComponent.tsx diff --git a/src/clients/scopes/AddScopeDialog.tsx b/src/clients/scopes/AddScopeDialog.tsx index afe0fecfe9..7d2d92fba2 100644 --- a/src/clients/scopes/AddScopeDialog.tsx +++ b/src/clients/scopes/AddScopeDialog.tsx @@ -298,6 +298,7 @@ export const AddScopeDialog = ({ { name: "name", }, + { name: "protocol", displayKey: "Protocol" }, { name: "protocol", displayKey: "clients:protocol", diff --git a/src/components/dynamic/DynamicComponents.tsx b/src/components/dynamic/DynamicComponents.tsx index a6f692cc92..fd3b7ceea2 100644 --- a/src/components/dynamic/DynamicComponents.tsx +++ b/src/components/dynamic/DynamicComponents.tsx @@ -13,7 +13,7 @@ export const DynamicComponents = ({ properties }: DynamicComponentProps) => ( <> {properties.map((property) => { const componentType = property.type!; - if (isValidComponentType(componentType)) { + if (isValidComponentType(componentType) && property.name !== "scopes") { const Component = COMPONENTS[componentType]; return ; } else { diff --git a/src/components/dynamic/MultivaluedChipsComponent.tsx b/src/components/dynamic/MultivaluedChipsComponent.tsx new file mode 100644 index 0000000000..98dbf77e48 --- /dev/null +++ b/src/components/dynamic/MultivaluedChipsComponent.tsx @@ -0,0 +1,172 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Controller, useFormContext } from "react-hook-form"; +import { + Button, + Chip, + ChipGroup, + FormGroup, + TextInput, +} from "@patternfly/react-core"; + +import { HelpItem } from "../help-enabler/HelpItem"; +import type { ComponentProps } from "./components"; +import { AddScopeDialog } from "../../clients/scopes/AddScopeDialog"; +import { useAdminClient, useFetch } from "../../context/auth/AdminClient"; +import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation"; +import { useParams } from "react-router"; +import type { EditClientPolicyConditionParams } from "../../realm-settings/routes/EditCondition"; +import { GroupPickerDialog } from "../group/GroupPickerDialog"; +import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation"; + +export const MultivaluedChipsComponent = ({ + defaultValue, + name, +}: ComponentProps) => { + const { t } = useTranslation("dynamic"); + const { control } = useFormContext(); + const { conditionName } = useParams(); + const adminClient = useAdminClient(); + const [open, setOpen] = useState(false); + const [clientScopes, setClientScopes] = useState( + [] + ); + const [selectedGroups, setSelectedGroups] = useState( + [] + ); + + useFetch( + () => adminClient.clientScopes.find(), + (clientScopes) => { + setClientScopes(clientScopes); + }, + [] + ); + + const toggleModal = () => { + setOpen(!open); + }; + + return ( + + } + fieldId={name!} + > + { + return ( + <> + {open && name === "scopes" && ( + !value.includes(scope.name!) + )} + isClientScopesConditionType + open={open} + toggleDialog={() => setOpen(!open)} + onAdd={(scopes) => { + onChange([ + ...value, + ...scopes + .map((scope) => scope.scope) + .map((item) => item.name!), + ]); + }} + /> + )} + {open && name === "groups" && ( + { + onChange([...value, ...groups.map((group) => group.name)]); + setSelectedGroups([...selectedGroups!, ...groups]); + setOpen(false); + }} + onClose={() => { + setOpen(false); + }} + filterGroups={value} + /> + )} + {value.length === 0 && !conditionName && ( + + )} + { + onChange([]); + if (name === "groups") { + setSelectedGroups([]); + } + }} + > + {value.map((currentChip: string) => ( + { + onChange( + value.filter((item: string) => item !== currentChip) + ); + if (name === "groups") { + setSelectedGroups( + value.filter((item: string) => item !== currentChip) + ); + } + }} + > + {currentChip} + + ))} + + + + ); + }} + /> + + ); +}; diff --git a/src/components/group/GroupPickerDialog.tsx b/src/components/group/GroupPickerDialog.tsx index 73088efab5..51d37c9a5e 100644 --- a/src/components/group/GroupPickerDialog.tsx +++ b/src/components/group/GroupPickerDialog.tsx @@ -25,7 +25,7 @@ import { GroupPath } from "./GroupPath"; export type GroupPickerDialogProps = { id?: string; type: "selectOne" | "selectMany"; - filterGroups?: GroupRepresentation[]; + filterGroups?: string[]; text: { title: string; ok: string }; onConfirm: (groups: GroupRepresentation[]) => void; onClose: () => void; @@ -100,9 +100,10 @@ export const GroupPickerDialog = ({ ); const isRowDisabled = (row?: GroupRepresentation) => { - return !![...joinedGroups, ...(filterGroups || [])].find( - (group) => group.id === row?.id - ); + return [ + ...joinedGroups.map((item) => item.name), + ...(filterGroups || []), + ].some((group) => group === row?.name); }; const hasSubgroups = (group: GroupRepresentation) => { @@ -129,7 +130,7 @@ export const GroupPickerDialog = ({ {type === "selectMany" && ( { {move && ( p.name === key); if ( property?.type === "MultivaluedString" && - property.name !== "scopes" + property.name !== "scopes" && + property.name !== "groups" ) { form.setValue(formKey, convertToMultiline(value)); } else if (property?.name === "client-scopes") { @@ -135,7 +136,9 @@ export default function NewClientPolicyCondition() { const writeConfig = () => { return conditionProperties.reduce((r: any, p) => { - p.type === "MultivaluedString" && p.name !== "scopes" + p.type === "MultivaluedString" && + p.name !== "scopes" && + p.name !== "groups" ? (r[p.name!] = toValue(configValues[p.name!])) : (r[p.name!] = configValues[p.name!]); return r; @@ -300,11 +303,22 @@ export default function NewClientPolicyCondition() { conditionName === "client-scopes") ) { return ( - ); + } else if ( + property.name === "groups" && + (conditionType === "client-updater-source-groups" || + conditionName === "client-updater-source-groups") + ) { + return ( + + ); } else if (isValidComponentType(componentType)) { const Component = COMPONENTS[componentType]; return ; diff --git a/src/realm-settings/RealmSettingsSection.css b/src/realm-settings/RealmSettingsSection.css index a70ce217fb..34c83f9f21 100644 --- a/src/realm-settings/RealmSettingsSection.css +++ b/src/realm-settings/RealmSettingsSection.css @@ -265,3 +265,7 @@ input#kc-scopes { min-width: 585px; padding-left: none; } + +.kc-join-group-modal-check { + margin-right: var(--pf-global--spacer--sm); +} diff --git a/src/user/UserForm.tsx b/src/user/UserForm.tsx index 1d80e9f980..64048597ff 100644 --- a/src/user/UserForm.tsx +++ b/src/user/UserForm.tsx @@ -145,13 +145,13 @@ export const UserForm = ({ setOpen(false); }} onClose={() => setOpen(false)} - filterGroups={selectedGroups} + filterGroups={selectedGroups.map((group) => group.name!)} /> )} {user?.id ? ( <> - +