removed components that don't adhere to server configuration (#2091)
This commit is contained in:
parent
c84c983415
commit
10b53617a0
8 changed files with 87 additions and 364 deletions
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 <Component key={property.name} {...property} {...rest} />;
|
||||
} else {
|
||||
|
|
|
@ -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<EditClientPolicyConditionParams>();
|
||||
const adminClient = useAdminClient();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [clientScopes, setClientScopes] = useState<ClientScopeRepresentation[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
useFetch(
|
||||
() => adminClient.clientScopes.find(),
|
||||
(clientScopes) => {
|
||||
setClientScopes(clientScopes);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const toggleModal = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
label={t(label!)}
|
||||
labelIcon={
|
||||
<HelpItem helpText={t(helpText!)} fieldLabelId={`dynamic:${label}`} />
|
||||
}
|
||||
fieldId={name!}
|
||||
>
|
||||
<Controller
|
||||
name={`config.${name}`}
|
||||
control={control}
|
||||
defaultValue={[defaultValue]}
|
||||
rules={{ required: true }}
|
||||
render={({ onChange, value }) => (
|
||||
<>
|
||||
{open && name === "scopes" && (
|
||||
<AddScopeDialog
|
||||
clientScopes={clientScopes.filter(
|
||||
(scope) => !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" && (
|
||||
<GroupPickerDialog
|
||||
type="selectMany"
|
||||
text={{
|
||||
title: "users:selectGroups",
|
||||
ok: "users:join",
|
||||
}}
|
||||
onConfirm={(groups) => {
|
||||
onChange([...value, ...groups.map((group) => group.name)]);
|
||||
setOpen(false);
|
||||
}}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
filterGroups={value}
|
||||
/>
|
||||
)}
|
||||
{value.length === 0 && !conditionName && (
|
||||
<TextInput
|
||||
type="text"
|
||||
id="kc-scopes"
|
||||
value={value}
|
||||
data-testid="client-scope-input"
|
||||
name="config.client-scopes"
|
||||
isDisabled
|
||||
/>
|
||||
)}
|
||||
<ChipGroup
|
||||
className="kc-client-scopes-chip-group"
|
||||
isClosable
|
||||
onClick={() => {
|
||||
onChange([]);
|
||||
}}
|
||||
>
|
||||
{value.map((currentChip: string) => (
|
||||
<Chip
|
||||
key={currentChip}
|
||||
onClick={() => {
|
||||
onChange(
|
||||
value.filter((item: string) => item !== currentChip)
|
||||
);
|
||||
}}
|
||||
>
|
||||
{currentChip}
|
||||
</Chip>
|
||||
))}
|
||||
</ChipGroup>
|
||||
<Button
|
||||
isDisabled={isDisabled}
|
||||
data-testid="select-scope-button"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
toggleModal();
|
||||
}}
|
||||
>
|
||||
{t("common:select")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
|
@ -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<RoleRepresentation[]>([]);
|
||||
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<RoleRepresentation>((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 (
|
||||
<FormGroup
|
||||
label={t(label!)}
|
||||
labelIcon={
|
||||
<HelpItem helpText={t(helpText!)} fieldLabelId={`dynamic:${label}`} />
|
||||
}
|
||||
fieldId={name!}
|
||||
>
|
||||
<Controller
|
||||
name={fieldName}
|
||||
defaultValue={[]}
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
isDisabled={isDisabled}
|
||||
onToggle={(isExpanded) => setOpen(isExpanded)}
|
||||
isOpen={open}
|
||||
className="kc-role-select"
|
||||
data-testid="multivalued-role-select"
|
||||
variant={SelectVariant.typeaheadMulti}
|
||||
placeholderText={t("selectARole")}
|
||||
chipGroupProps={{
|
||||
numChips: 5,
|
||||
expandedText: t("common:hide"),
|
||||
collapsedText: t("common:showRemaining"),
|
||||
}}
|
||||
typeAheadAriaLabel={t("selectARole")}
|
||||
selections={value.map((v: MultiLine) => v.value)}
|
||||
isCreatable
|
||||
onSelect={(_, v) => {
|
||||
const option = v.toString();
|
||||
if (value.map((v: MultiLine) => v.value).includes(option)) {
|
||||
onChange(
|
||||
value.filter((item: MultiLine) => item.value !== option)
|
||||
);
|
||||
} else {
|
||||
onChange([...value, { value: option }]);
|
||||
}
|
||||
}}
|
||||
maxHeight={200}
|
||||
onClear={() => onChange([])}
|
||||
>
|
||||
{alphabetizedClientRoles.map((option) => (
|
||||
<SelectOption key={option} value={option} />
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
|
@ -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 (
|
||||
<FormGroup
|
||||
|
@ -22,14 +45,46 @@ export const MultiValuedStringComponent = ({
|
|||
}
|
||||
fieldId={name!}
|
||||
>
|
||||
<MultiLineInput
|
||||
name={`config.${name}`}
|
||||
aria-label={name}
|
||||
isDisabled={isDisabled}
|
||||
addButtonLabel={t("addMultivaluedLabel", {
|
||||
fieldLabel: t(label!).toLowerCase(),
|
||||
})}
|
||||
/>
|
||||
{fields.map((value: string, index: number) => (
|
||||
<Fragment key={index}>
|
||||
<InputGroup>
|
||||
<TextInput
|
||||
id={fieldName + index}
|
||||
onChange={(value) => {
|
||||
fields[index] = value;
|
||||
setValue(fieldName, [...fields]);
|
||||
}}
|
||||
name={`${fieldName}[${index}]`}
|
||||
value={value}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
<Button
|
||||
variant={ButtonVariant.link}
|
||||
onClick={() => remove(index)}
|
||||
tabIndex={-1}
|
||||
aria-label={t("common:remove")}
|
||||
isDisabled={index === fields.length - 1}
|
||||
>
|
||||
<MinusCircleIcon />
|
||||
</Button>
|
||||
</InputGroup>
|
||||
{index === fields.length - 1 && (
|
||||
<Button
|
||||
variant={ButtonVariant.link}
|
||||
onClick={append}
|
||||
tabIndex={-1}
|
||||
aria-label={t("common:add")}
|
||||
data-testid="addValue"
|
||||
isDisabled={!value}
|
||||
>
|
||||
<PlusCircleIcon />{" "}
|
||||
{t("addMultivaluedLabel", {
|
||||
fieldLabel: t(label!).toLowerCase(),
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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<EditClientPolicyConditionParams>();
|
||||
|
||||
const serverInfo = useServerInfo();
|
||||
const form = useForm<ClientPolicyConditionRepresentation>({
|
||||
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() {
|
|||
</FormGroup>
|
||||
|
||||
<FormProvider {...form}>
|
||||
{conditionProperties.map((property) => {
|
||||
const componentType = property.type!;
|
||||
if (property.name === "roles") {
|
||||
return <MultivaluedRoleComponent {...property} />;
|
||||
}
|
||||
|
||||
if (property.name === "scopes" || property.name === "groups") {
|
||||
return (
|
||||
<MultivaluedChipsComponent
|
||||
defaultValue={
|
||||
property.name === "scopes" ? "offline_access" : "topgroup"
|
||||
}
|
||||
{...property}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isValidComponentType(componentType)) {
|
||||
const Component = COMPONENTS[componentType];
|
||||
return <Component key={property.name} {...property} />;
|
||||
} else {
|
||||
console.warn(
|
||||
`There is no editor registered for ${componentType}`
|
||||
);
|
||||
}
|
||||
})}
|
||||
<DynamicComponents properties={conditionProperties} />
|
||||
</FormProvider>
|
||||
<ActionGroup>
|
||||
<Button
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { LocationDescriptorObject } from "history";
|
||||
import { lazy } from "react";
|
||||
import { generatePath } from "react-router-dom";
|
||||
import type { RouteDef } from "../../route-config";
|
||||
import NewClientPolicyCondition from "../NewClientPolicyCondition";
|
||||
|
||||
export type EditClientPolicyConditionParams = {
|
||||
realm: string;
|
||||
|
@ -11,7 +11,7 @@ export type EditClientPolicyConditionParams = {
|
|||
|
||||
export const EditClientPolicyConditionRoute: RouteDef = {
|
||||
path: "/:realm/realm-settings/clientPolicies/:policyName?/edit-policy/:conditionName/edit-condition",
|
||||
component: NewClientPolicyCondition,
|
||||
component: lazy(() => import("../NewClientPolicyCondition")),
|
||||
breadcrumb: (t) => t("realm-settings:editCondition"),
|
||||
access: "manage-clients",
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue