diff --git a/cypress/integration/realm_settings_client_policies_test.spec.ts b/cypress/integration/realm_settings_client_policies_test.spec.ts index d35eff993c..297b7da1fe 100644 --- a/cypress/integration/realm_settings_client_policies_test.spec.ts +++ b/cypress/integration/realm_settings_client_policies_test.spec.ts @@ -158,7 +158,6 @@ describe("Realm settings client policies tab tests", () => { // TODO: UNCOMMENT WHEN THE ISSUE 2050 IS FIXED //realmSettingsPage.checkAlertMessage("Could not create client policy: 'proposed client policy name duplicated.'"); - sidebarPage.waitForPageLoad(); sidebarPage.goToRealmSettings(); realmSettingsPage diff --git a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts index 1f8ea06e2b..549fb53e6f 100644 --- a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts +++ b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts @@ -218,8 +218,8 @@ export default class RealmSettingsPage { private eventListenersDrwDwnSelect = ".pf-c-button.pf-c-select__toggle-button.pf-m-plain"; private eventListenerRemove = '[data-ouia-component-id="Remove"]'; - private roleSelect = ".pf-c-select.kc-role-select"; - private selectScopeButton = "select-scope-button"; + private roleSelect = "#config\\.roles0"; + private selectScopeButton = "addValue"; private deleteClientRolesConditionBtn = "delete-client-roles-condition"; private deleteClientScopesConditionBtn = "delete-client-scopes-condition"; @@ -947,13 +947,7 @@ export default class RealmSettingsPage { cy.findByTestId(this.addConditionDrpDwnOption) .contains("client-roles") .click(); - cy.get(this.roleSelect).click().contains("impersonation").click(); - - cy.get(this.roleSelect).contains("manage-realm").click(); - - cy.get(this.roleSelect).contains("view-users").click(); - - cy.get(this.roleSelect).click(); + cy.get(this.roleSelect).clear().type("manage-realm"); cy.findByTestId(this.addConditionSaveBtn).click(); cy.get(this.alertMessage).should( @@ -964,18 +958,14 @@ export default class RealmSettingsPage { } addClientScopes() { + cy.get("#config\\.scopes0").clear().type("one"); cy.findByTestId(this.selectScopeButton).click(); - cy.get(".pf-c-table__check input[name=checkrow0]").click(); - cy.get(".pf-c-table__check input[name=checkrow1]").click(); - cy.get(".pf-c-table__check input[name=checkrow2]").click(); - - cy.findByTestId(this.modalConfirm).contains("Add").click(); + cy.get("#config\\.scopes1").clear().type("two"); + cy.findByTestId(this.selectScopeButton).click(); + cy.get("#config\\.scopes2").clear().type("three"); } shouldAddClientScopesCondition() { - cy.intercept(`/auth/admin/realms/${this.realmName}/client-scopes`).as( - "clientScopes" - ); cy.get(this.clientPolicy).click(); cy.findByTestId(this.addCondition).click(); cy.get(this.addConditionDrpDwn).click(); @@ -983,7 +973,6 @@ export default class RealmSettingsPage { .contains("client-scopes") .click(); - cy.wait("@clientScopes"); this.addClientScopes(); cy.findByTestId(this.addConditionSaveBtn).click(); @@ -999,10 +988,8 @@ export default class RealmSettingsPage { cy.findByTestId(this.clientRolesConditionLink).click(); - cy.get(this.roleSelect).click(); - cy.get(this.roleSelect).contains("create-client").click(); - - cy.get(this.roleSelect).click(); + cy.get(this.roleSelect).should("have.value", "manage-realm"); + cy.get(this.roleSelect).clear().type("admin"); cy.findByTestId(this.addConditionSaveBtn).click(); cy.get(this.alertMessage).should( @@ -1012,15 +999,11 @@ export default class RealmSettingsPage { } shouldEditClientScopesCondition() { - cy.intercept(`/auth/admin/realms/${this.realmName}/client-scopes`).as( - "clientScopes" - ); cy.get(this.clientPolicy).click(); cy.findByTestId(this.clientScopesConditionLink).click(); - cy.wait("@clientScopes"); - this.addClientScopes(); + cy.get("#config\\.scopes0").clear().type("edit"); cy.findByTestId(this.addConditionSaveBtn).click(); cy.get(this.alertMessage).should( diff --git a/src/components/dynamic/DynamicComponents.tsx b/src/components/dynamic/DynamicComponents.tsx index 504e7a90ea..5ab91ce9d7 100644 --- a/src/components/dynamic/DynamicComponents.tsx +++ b/src/components/dynamic/DynamicComponents.tsx @@ -16,7 +16,7 @@ export const DynamicComponents = ({ <> {properties.map((property) => { const componentType = property.type!; - if (isValidComponentType(componentType) && property.name !== "scopes") { + if (isValidComponentType(componentType)) { const Component = COMPONENTS[componentType]; return ; } else { diff --git a/src/components/dynamic/MultivaluedChipsComponent.tsx b/src/components/dynamic/MultivaluedChipsComponent.tsx deleted file mode 100644 index e79d3e6c38..0000000000 --- a/src/components/dynamic/MultivaluedChipsComponent.tsx +++ /dev/null @@ -1,144 +0,0 @@ -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"; - -export const MultivaluedChipsComponent = ({ - defaultValue, - name, - label, - helpText, - isDisabled = false, -}: ComponentProps) => { - const { t } = useTranslation("dynamic"); - const { control } = useFormContext(); - const { conditionName } = useParams(); - const adminClient = useAdminClient(); - const [open, setOpen] = useState(false); - const [clientScopes, setClientScopes] = useState( - [] - ); - - useFetch( - () => adminClient.clientScopes.find(), - (clientScopes) => { - setClientScopes(clientScopes); - }, - [] - ); - - const toggleModal = () => { - setOpen(!open); - }; - - return ( - - } - fieldId={name!} - > - ( - <> - {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)]); - setOpen(false); - }} - onClose={() => { - setOpen(false); - }} - filterGroups={value} - /> - )} - {value.length === 0 && !conditionName && ( - - )} - { - onChange([]); - }} - > - {value.map((currentChip: string) => ( - { - onChange( - value.filter((item: string) => item !== currentChip) - ); - }} - > - {currentChip} - - ))} - - - - )} - /> - - ); -}; diff --git a/src/components/dynamic/MultivaluedRoleComponent.tsx b/src/components/dynamic/MultivaluedRoleComponent.tsx deleted file mode 100644 index 9c610ef09a..0000000000 --- a/src/components/dynamic/MultivaluedRoleComponent.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { useState } from "react"; -import { Controller, useFormContext } from "react-hook-form"; -import { useTranslation } from "react-i18next"; -import { sortedUniq } from "lodash-es"; -import { - FormGroup, - Select, - SelectOption, - SelectVariant, -} from "@patternfly/react-core"; - -import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; -import type { ComponentProps } from "./components"; -import type { MultiLine } from "../multi-line-input/multi-line-convert"; -import { useAdminClient, useFetch } from "../../context/auth/AdminClient"; -import { HelpItem } from "../help-enabler/HelpItem"; -import { useWhoAmI } from "../../context/whoami/WhoAmI"; - -export const MultivaluedRoleComponent = ({ - name, - label, - helpText, - isDisabled = false, -}: ComponentProps) => { - const { t } = useTranslation("dynamic"); - const { whoAmI } = useWhoAmI(); - const fieldName = `config.${name}`; - - const adminClient = useAdminClient(); - const { control } = useFormContext(); - - const [clientRoles, setClientRoles] = useState([]); - const [open, setOpen] = useState(false); - - useFetch( - async () => { - const clients = await adminClient.clients.find(); - const clientRoles = await Promise.all( - clients.map(async (client) => { - const roles = await adminClient.clients.listRoles({ id: client.id! }); - - return roles.map((role) => ({ - ...role, - })); - }) - ); - - return clientRoles.flat(); - }, - (clientRoles) => { - setClientRoles(clientRoles); - }, - [] - ); - - const alphabetizedClientRoles = sortedUniq( - clientRoles.map((item) => item.name) - ).sort((a, b) => - a!.localeCompare(b!, whoAmI.getLocale(), { ignorePunctuation: true }) - ); - - return ( - - } - fieldId={name!} - > - ( - - )} - /> - - ); -}; diff --git a/src/components/dynamic/MultivaluedStringComponent.tsx b/src/components/dynamic/MultivaluedStringComponent.tsx index 5beb650d73..8d51014046 100644 --- a/src/components/dynamic/MultivaluedStringComponent.tsx +++ b/src/components/dynamic/MultivaluedStringComponent.tsx @@ -1,18 +1,41 @@ -import React from "react"; +import React, { Fragment, useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { FormGroup } from "@patternfly/react-core"; +import { useFormContext } from "react-hook-form"; +import { + Button, + ButtonVariant, + FormGroup, + InputGroup, + TextInput, +} from "@patternfly/react-core"; import { HelpItem } from "../../components/help-enabler/HelpItem"; import type { ComponentProps } from "./components"; -import { MultiLineInput } from "../../components/multi-line-input/MultiLineInput"; +import { MinusCircleIcon, PlusCircleIcon } from "@patternfly/react-icons"; export const MultiValuedStringComponent = ({ name, label, + defaultValue, helpText, isDisabled = false, }: ComponentProps) => { const { t } = useTranslation("dynamic"); + const fieldName = `config.${name}`; + const { register, setValue, watch } = useFormContext(); + + const fields = watch(fieldName, [defaultValue]); + + const remove = (id: number) => { + fields.splice(id, 1); + setValue(fieldName, [...fields]); + }; + + const append = () => { + setValue(fieldName, [...fields, ""]); + }; + + useEffect(() => register(`config.${name}`), [register]); return ( - + {fields.map((value: string, index: number) => ( + + + { + fields[index] = value; + setValue(fieldName, [...fields]); + }} + name={`${fieldName}[${index}]`} + value={value} + isDisabled={isDisabled} + /> + + + {index === fields.length - 1 && ( + + )} + + ))} ); }; diff --git a/src/realm-settings/NewClientPolicyCondition.tsx b/src/realm-settings/NewClientPolicyCondition.tsx index e9226e2b4f..571eb845a1 100644 --- a/src/realm-settings/NewClientPolicyCondition.tsx +++ b/src/realm-settings/NewClientPolicyCondition.tsx @@ -30,16 +30,8 @@ import { toEditClientPolicy, } from "./routes/EditClientPolicy"; import type { EditClientPolicyConditionParams } from "./routes/EditCondition"; -import { - convertToMultiline, - toValue, -} from "../components/multi-line-input/multi-line-convert"; -import { - COMPONENTS, - isValidComponentType, -} from "../components/dynamic/components"; -import { MultivaluedChipsComponent } from "../components/dynamic/MultivaluedChipsComponent"; -import { MultivaluedRoleComponent } from "../components/dynamic/MultivaluedRoleComponent"; +import { DynamicComponents } from "../components/dynamic/DynamicComponents"; + export type ItemType = { value: string }; type ConfigProperty = ConfigPropertyRepresentation & { @@ -69,7 +61,7 @@ export default function NewClientPolicyCondition() { const { conditionName } = useParams(); const serverInfo = useServerInfo(); - const form = useForm({ + const form = useForm({ shouldUnregister: false, }); @@ -80,28 +72,8 @@ export default function NewClientPolicyCondition() { const adminClient = useAdminClient(); - const setupForm = ( - condition: ClientPolicyConditionRepresentation, - properties: ConfigPropertyRepresentation[] - ) => { - form.reset(); - - Object.entries(condition.configuration!).map(([key, value]) => { - const formKey = `config.${key}`; - - const property = properties.find((p) => p.name === key); - if ( - property?.type === "MultivaluedString" && - property.name !== "scopes" && - property.name !== "groups" - ) { - form.setValue(formKey, convertToMultiline(value)); - } else if (property?.name === "client-scopes") { - form.setValue("config.scopes", value); - } else { - form.setValue(formKey, value); - } - }); + const setupForm = (condition: ClientPolicyConditionRepresentation) => { + form.reset({ config: condition.configuration || {} }); }; useFetch( @@ -125,7 +97,7 @@ export default function NewClientPolicyCondition() { setConditionData(typeAndConfigData!); setConditionProperties(currentCondition?.properties!); - setupForm(typeAndConfigData!, currentCondition?.properties!); + setupForm(typeAndConfigData!); } }, [] @@ -136,11 +108,7 @@ export default function NewClientPolicyCondition() { const writeConfig = () => { return conditionProperties.reduce((r: any, p) => { - p.type === "MultivaluedString" && - p.name !== "scopes" && - p.name !== "groups" - ? (r[p.name!] = toValue(configValues[p.name!])) - : (r[p.name!] = configValues[p.name!]); + r[p.name!] = configValues[p.name!]; return r; }, {}); }; @@ -284,32 +252,7 @@ export default function NewClientPolicyCondition() { - {conditionProperties.map((property) => { - const componentType = property.type!; - if (property.name === "roles") { - return ; - } - - if (property.name === "scopes" || property.name === "groups") { - return ( - - ); - } - - if (isValidComponentType(componentType)) { - const Component = COMPONENTS[componentType]; - return ; - } else { - console.warn( - `There is no editor registered for ${componentType}` - ); - } - })} +