The role picker no longer queries all clients (#3197)
This commit is contained in:
parent
0bca44e733
commit
29cf962f19
4 changed files with 56 additions and 202 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue