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() {
|
addRoleToMapperForm() {
|
||||||
cy.get("#group-role-select-typeahead")
|
cy.findByTestId("add-roles").click();
|
||||||
.click()
|
cy.get("[aria-label='Select row 1']").click();
|
||||||
.get(".pf-c-select__menu-item")
|
cy.findByTestId("assign").click();
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
cy.get("#role-role-select-typeahead")
|
|
||||||
.click()
|
|
||||||
.get(".pf-c-select__menu-item")
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -371,16 +371,9 @@ export default class ProviderPage {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case this.hcLdapRoleMapper:
|
case this.hcLdapRoleMapper:
|
||||||
cy.get("#group-role-select-typeahead")
|
cy.findByTestId("add-roles").click();
|
||||||
.click()
|
cy.get("[aria-label='Select row 1']").click();
|
||||||
.get(".pf-c-select__menu-item")
|
cy.findByTestId("assign").click();
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
cy.get("#role-role-select-typeahead")
|
|
||||||
.click()
|
|
||||||
.get(".pf-c-select__menu-item")
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("Invalid mapper type.");
|
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 { useTranslation } from "react-i18next";
|
||||||
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
Divider,
|
Button,
|
||||||
|
Chip,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
Select,
|
|
||||||
SelectGroup,
|
|
||||||
SelectOption,
|
|
||||||
SelectVariant,
|
|
||||||
Split,
|
Split,
|
||||||
SplitItem,
|
SplitItem,
|
||||||
} from "@patternfly/react-core";
|
} 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 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";
|
import { convertToName } from "./DynamicComponents";
|
||||||
|
|
||||||
const RealmClient = (realm: string): ClientRepresentation => ({
|
const parseValue = (value: any) =>
|
||||||
name: "realmRoles",
|
value?.includes(".") ? value.split(".") : ["", value || ""];
|
||||||
clientId: realm,
|
|
||||||
});
|
const parseRow = (value: Row) =>
|
||||||
|
value.client?.clientId
|
||||||
|
? `${value.client.clientId}.${value.role.name}`
|
||||||
|
: value.role.name;
|
||||||
|
|
||||||
export const RoleComponent = ({
|
export const RoleComponent = ({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
helpText,
|
helpText,
|
||||||
|
defaultValue,
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
}: ComponentProps) => {
|
}: ComponentProps) => {
|
||||||
const { t } = useTranslation("dynamic");
|
const { t } = useTranslation("dynamic");
|
||||||
|
|
||||||
const { adminClient } = useAdminClient();
|
const [openModal, toggleModal] = useToggle();
|
||||||
const { realm } = useRealm();
|
const { control, errors } = useFormContext();
|
||||||
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 fieldName = convertToName(name!);
|
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 (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t(label!)}
|
label={t(label!)}
|
||||||
|
@ -145,76 +49,41 @@ export const RoleComponent = ({
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name={fieldName}
|
name={fieldName}
|
||||||
defaultValue=""
|
defaultValue={defaultValue || ""}
|
||||||
|
typeAheadAriaLabel="Select an action"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ onChange }) => (
|
render={({ onChange, value }) => (
|
||||||
<Split hasGutter>
|
<Split>
|
||||||
|
{openModal && (
|
||||||
|
<AddRoleMappingModal
|
||||||
|
id="id"
|
||||||
|
type="roles"
|
||||||
|
name={name}
|
||||||
|
onAssign={(rows) => onChange(parseRow(rows[0]))}
|
||||||
|
onClose={toggleModal}
|
||||||
|
isRadio
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{value !== "" && (
|
||||||
|
<SplitItem>
|
||||||
|
<Chip textMaxWidth="500px" onClick={() => onChange("")}>
|
||||||
|
<ServiceRole
|
||||||
|
role={{ name: parseValue(value)[1] }}
|
||||||
|
client={{ clientId: parseValue(value)[0] }}
|
||||||
|
/>
|
||||||
|
</Chip>
|
||||||
|
</SplitItem>
|
||||||
|
)}
|
||||||
<SplitItem>
|
<SplitItem>
|
||||||
{clients && (
|
<Button
|
||||||
<Select
|
onClick={toggleModal}
|
||||||
toggleId={`group-${name}`}
|
variant="secondary"
|
||||||
isDisabled={isDisabled}
|
data-testid="add-roles"
|
||||||
onToggle={() => setClientsOpen(!clientsOpen)}
|
disabled={isDisabled}
|
||||||
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>
|
|
||||||
)}
|
|
||||||
</SplitItem>
|
|
||||||
<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)}
|
|
||||||
>
|
>
|
||||||
{roleSelectOptions()}
|
{t("selectRole.label")}
|
||||||
</Select>
|
</Button>
|
||||||
</SplitItem>
|
</SplitItem>
|
||||||
</Split>
|
</Split>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -60,7 +60,7 @@ export const mapRoles = (
|
||||||
|
|
||||||
export const ServiceRole = ({ role, client }: Row) => (
|
export const ServiceRole = ({ role, client }: Row) => (
|
||||||
<>
|
<>
|
||||||
{client && (
|
{client?.clientId && (
|
||||||
<Badge isRead className="keycloak-admin--role-mapping__client-name">
|
<Badge isRead className="keycloak-admin--role-mapping__client-name">
|
||||||
{client.clientId}
|
{client.clientId}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
Loading…
Reference in a new issue