The role picker no longer queries all clients (#3197)

This commit is contained in:
Erik Jan de Wit 2022-09-06 15:34:11 +02:00 committed by GitHub
parent 0bca44e733
commit 29cf962f19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 202 deletions

View file

@ -87,17 +87,9 @@ export default class AddMapperPage {
}
addRoleToMapperForm() {
cy.get("#group-role-select-typeahead")
.click()
.get(".pf-c-select__menu-item")
.first()
.click();
cy.get("#role-role-select-typeahead")
.click()
.get(".pf-c-select__menu-item")
.first()
.click();
cy.findByTestId("add-roles").click();
cy.get("[aria-label='Select row 1']").click();
cy.findByTestId("assign").click();
return this;
}

View file

@ -371,16 +371,9 @@ export default class ProviderPage {
break;
case this.hcLdapRoleMapper:
cy.get("#group-role-select-typeahead")
.click()
.get(".pf-c-select__menu-item")
.first()
.click();
cy.get("#role-role-select-typeahead")
.click()
.get(".pf-c-select__menu-item")
.first()
.click();
cy.findByTestId("add-roles").click();
cy.get("[aria-label='Select row 1']").click();
cy.findByTestId("assign").click();
break;
default:
console.log("Invalid mapper type.");

View file

@ -1,138 +1,42 @@
import { useEffect, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Controller, useFormContext } from "react-hook-form";
import {
Divider,
Button,
Chip,
FormGroup,
Select,
SelectGroup,
SelectOption,
SelectVariant,
Split,
SplitItem,
} from "@patternfly/react-core";
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import { HelpItem } from "../help-enabler/HelpItem";
import type { ComponentProps } from "./components";
import { HelpItem } from "../help-enabler/HelpItem";
import useToggle from "../../utils/useToggle";
import { AddRoleMappingModal } from "../role-mapping/AddRoleMappingModal";
import { ServiceRole, Row } from "../role-mapping/RoleMapping";
import { convertToName } from "./DynamicComponents";
const RealmClient = (realm: string): ClientRepresentation => ({
name: "realmRoles",
clientId: realm,
});
const parseValue = (value: any) =>
value?.includes(".") ? value.split(".") : ["", value || ""];
const parseRow = (value: Row) =>
value.client?.clientId
? `${value.client.clientId}.${value.role.name}`
: value.role.name;
export const RoleComponent = ({
name,
label,
helpText,
defaultValue,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const {
control,
getValues,
formState: { errors },
} = useFormContext();
const [roleOpen, setRoleOpen] = useState(false);
const [clientsOpen, setClientsOpen] = useState(false);
const [clients, setClients] = useState<ClientRepresentation[]>();
const [selectedClient, setSelectedClient] = useState<ClientRepresentation>();
const [clientRoles, setClientRoles] = useState<RoleRepresentation[]>([]);
const [selectedRole, setSelectedRole] = useState<RoleRepresentation>();
const [openModal, toggleModal] = useToggle();
const { control, errors } = useFormContext();
const fieldName = convertToName(name!);
useFetch(
async () => {
const clients = await adminClient.clients.find();
const asyncFilter = async (
predicate: (client: ClientRepresentation) => Promise<boolean>
) => {
const results = await Promise.all(clients.map(predicate));
return clients.filter((_, index) => results[index]);
};
const filteredClients = await asyncFilter(
async (client) =>
(await adminClient.clients.listRoles({ id: client.id! })).length > 0
);
return filteredClients;
},
(filteredClients) => setClients(filteredClients),
[]
);
useEffect(() => {
const value = getValues(fieldName);
const [client, role] = value?.includes(".")
? value.split(".")
: ["", value || ""];
if (client) {
setSelectedClient(clients?.find((c) => c.clientId === client));
} else {
setSelectedClient(RealmClient(realm));
}
setSelectedRole({ name: role });
}, [clients, getValues]);
const createSelectGroup = (clients: ClientRepresentation[]) => {
return [
<SelectGroup key="role" label={t("roleGroup")}>
<SelectOption key="realmRoles" value={RealmClient(realm)}>
{realm}
</SelectOption>
</SelectGroup>,
<Divider key="divider" />,
<SelectGroup key="group" label={t("clientGroup")}>
{clients.map((client) => (
<SelectOption key={client.id} value={client}>
{client.clientId}
</SelectOption>
))}
</SelectGroup>,
];
};
const roleSelectOptions = () => {
const createItem = (role: RoleRepresentation) => (
<SelectOption key={role.id} value={role}>
{role.name}
</SelectOption>
);
return clientRoles.map((role) => createItem(role));
};
useFetch(
async () => {
if (selectedClient && selectedClient.name !== "realmRoles") {
const clientRoles = await adminClient.clients.listRoles({
id: selectedClient.id!,
});
return clientRoles;
} else {
return await adminClient.roles.find();
}
},
(clientRoles) => setClientRoles(clientRoles),
[selectedClient]
);
const onClear = (onChange: (value: string) => void) => {
setSelectedClient(undefined);
setSelectedRole(undefined);
onChange("");
};
return (
<FormGroup
label={t(label!)}
@ -145,76 +49,41 @@ export const RoleComponent = ({
>
<Controller
name={fieldName}
defaultValue=""
defaultValue={defaultValue || ""}
typeAheadAriaLabel="Select an action"
control={control}
render={({ onChange }) => (
<Split hasGutter>
<SplitItem>
{clients && (
<Select
toggleId={`group-${name}`}
isDisabled={isDisabled}
onToggle={() => setClientsOpen(!clientsOpen)}
isOpen={clientsOpen}
variant={SelectVariant.typeahead}
typeAheadAriaLabel={t("selectASourceOfRoles")}
placeholderText={t("selectASourceOfRoles")}
isGrouped
onFilter={(evt) => {
const textInput = evt?.target.value || "";
if (textInput === "") {
return createSelectGroup(clients);
} else {
return createSelectGroup(
clients.filter((client) =>
client
.name!.toLowerCase()
.includes(textInput.toLowerCase())
)
);
}
}}
selections={selectedClient?.clientId}
onClear={() => onClear(onChange)}
onSelect={(_, value) => {
onClear(onChange);
setSelectedClient(value as ClientRepresentation);
setClientsOpen(false);
}}
>
{createSelectGroup(clients)}
</Select>
render={({ onChange, value }) => (
<Split>
{openModal && (
<AddRoleMappingModal
id="id"
type="roles"
name={name}
onAssign={(rows) => onChange(parseRow(rows[0]))}
onClose={toggleModal}
isRadio
/>
)}
</SplitItem>
{value !== "" && (
<SplitItem>
<Select
toggleId={`role-${name}`}
onToggle={(isExpanded) => setRoleOpen(isExpanded)}
isOpen={roleOpen}
variant={SelectVariant.typeahead}
placeholderText={
selectedClient?.name !== "realmRoles"
? t("clientRoles")
: t("selectARole")
}
isDisabled={!selectedClient}
typeAheadAriaLabel={t("selectARole")}
selections={selectedRole?.name}
onSelect={(_, value) => {
const role = value as RoleRepresentation;
setSelectedRole(role);
onChange(
selectedClient?.name === "realmRoles"
? role.name
: `${selectedClient?.clientId}.${role.name}`
);
setRoleOpen(false);
}}
maxHeight={200}
onClear={() => onClear(onChange)}
<Chip textMaxWidth="500px" onClick={() => onChange("")}>
<ServiceRole
role={{ name: parseValue(value)[1] }}
client={{ clientId: parseValue(value)[0] }}
/>
</Chip>
</SplitItem>
)}
<SplitItem>
<Button
onClick={toggleModal}
variant="secondary"
data-testid="add-roles"
disabled={isDisabled}
>
{roleSelectOptions()}
</Select>
{t("selectRole.label")}
</Button>
</SplitItem>
</Split>
)}

View file

@ -60,7 +60,7 @@ export const mapRoles = (
export const ServiceRole = ({ role, client }: Row) => (
<>
{client && (
{client?.clientId && (
<Badge isRead className="keycloak-admin--role-mapping__client-name">
{client.clientId}
</Badge>