/* eslint-disable */ // @ts-nocheck import PolicyProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyProviderRepresentation"; import type PolicyRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation"; import type ResourceRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceRepresentation"; import type { Clients, PolicyQuery, } from "@keycloak/keycloak-admin-client/lib/resources/clients"; import { KeycloakSelect, SelectVariant, useFetch, Variant, } from "../../../shared/keycloak-ui-shared"; import { Button, ButtonVariant, Chip, ChipGroup, SelectOption, } from "../../../shared/@patternfly/react-core"; import { useState } from "react"; import { Controller, ControllerRenderProps, useFormContext, useWatch, } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { useAdminClient } from "../../admin-client"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { useRealm } from "../../context/realm-context/RealmContext"; import useToggle from "../../utils/useToggle"; import { toCreatePolicy } from "../routes/NewPolicy"; import { toPolicyDetails } from "../routes/PolicyDetails"; import { toResourceDetails } from "../routes/Resource"; import { NewPolicyDialog } from "./NewPolicyDialog"; import { useIsAdminPermissionsClient } from "../../utils/useIsAdminPermissionsClient"; type Type = "resources" | "policies"; type ResourcesPolicySelectProps = { name: Type; clientId: string; permissionId?: string; variant?: Variant; preSelected?: string; isRequired?: boolean; }; type Policies = { id?: string; name?: string; type?: string; }; type TypeMapping = { [key in Type]: { searchFunction: keyof Pick; fetchFunction: keyof Pick< Clients, "getAssociatedPolicies" | "getAssociatedResources" >; }; }; const typeMapping: TypeMapping = { resources: { searchFunction: "listResources", fetchFunction: "getAssociatedResources", }, policies: { searchFunction: "listPolicies", fetchFunction: "getAssociatedPolicies", }, }; export const ResourcesPolicySelect = ({ name, clientId, permissionId, variant = SelectVariant.typeaheadMulti, preSelected, isRequired = false, }: ResourcesPolicySelectProps) => { const { adminClient } = useAdminClient(); const { realm } = useRealm(); const { t } = useTranslation(); const navigate = useNavigate(); const { control, formState: { errors, isDirty }, } = useFormContext(); const [items, setItems] = useState([]); const [search, setSearch] = useState(""); const [open, setOpen] = useState(false); const [createPolicyDialog, toggleCreatePolicyDialog] = useToggle(); const [policyProviders, setPolicyProviders] = useState(); const [onUnsavedChangesConfirm, setOnUnsavedChangesConfirm] = useState<() => void>(); const isAdminPermissionsClient = useIsAdminPermissionsClient(clientId); const [selected, setSelected] = useState([]); const functions = typeMapping[name]; const value = useWatch({ control, name: name!, defaultValue: preSelected ? [preSelected] : [], }); const convert = ( p: PolicyRepresentation | ResourceRepresentation, ): Policies => ({ id: "_id" in p ? p._id : "id" in p ? p.id : undefined, name: p.name, type: p.type, }); useFetch( async () => { const params: PolicyQuery = Object.assign( { id: clientId, first: 0, max: 10, permission: "false" }, search === "" ? null : { name: search }, ); return await Promise.all([ adminClient.clients.listPolicyProviders({ id: clientId }), adminClient.clients[functions.searchFunction](params), permissionId ? adminClient.clients[functions.fetchFunction]({ id: clientId, permissionId, }) : Promise.resolve([]), preSelected && name === "resources" ? adminClient.clients.getResource({ id: clientId, resourceId: preSelected, }) : Promise.resolve([]), ]); }, ([providers, ...policies]) => { setPolicyProviders( providers.filter((p) => p.type !== "resource" && p.type !== "scope"), ); setItems( policies .flat() .filter( (r): r is PolicyRepresentation | ResourceRepresentation => typeof r !== "string", ) .map(convert) .filter( ({ id }, index, self) => index === self.findIndex(({ id: otherId }) => id === otherId), ), ); }, [search], ); useFetch( async () => { if (name === "resources") return await Promise.all( (value || []).map((id) => adminClient.clients.getResource({ id: clientId, resourceId: id }), ), ); return await Promise.all( (value || []).map(async (id) => adminClient.clients.findOnePolicy({ id: clientId, policyId: id, }), ), ); }, (result: any[]) => setSelected(result.map((r) => convert(r))), [value], ); const [toggleUnsavedChangesDialog, UnsavedChangesConfirm] = useConfirmDialog({ titleKey: t("unsavedChangesTitle"), messageKey: t("unsavedChangesConfirm"), continueButtonLabel: t("continue"), continueButtonVariant: ButtonVariant.danger, onConfirm: () => onUnsavedChangesConfirm?.(), }); const to = (policy: Policies) => name === "policies" ? toPolicyDetails({ realm: realm, id: clientId, policyId: policy.id!, policyType: policy.type!, }) : toResourceDetails({ realm, id: clientId, resourceId: policy.id!, }); const toSelectOptions = () => items.map((p) => ( {p.name} )); const toChipGroupItems = ( field: ControllerRenderProps, ) => { return ( {selected.map((item) => ( { field.onChange(field.value?.filter((id) => id !== item.id) || []); setSelected(selected.filter((p) => p.id !== item.id)); }} > {!isAdminPermissionsClient ? ( { if (isDirty) { event.preventDefault(); setOnUnsavedChangesConfirm(() => () => navigate(to(item))); toggleUnsavedChangesDialog(); } }} > {item.name} ) : ( item.name )} ))} ); }; return ( <> {createPolicyDialog && ( { navigate( toCreatePolicy({ id: clientId, realm, policyType: p.type! }), ); }} toggleDialog={toggleCreatePolicyDialog} /> )} !isRequired || value!.length > 0 }} render={({ field }) => ( setOpen(val)} onFilter={(filter) => { setSearch(filter); return toSelectOptions(); }} onClear={() => { field.onChange([]); setSearch(""); }} selections={ variant === SelectVariant.typeaheadMulti ? field.value : selected.find((i) => i.id === field.value?.[0])?.name } onSelect={(selectedValue) => { const option = selectedValue.toString(); if (variant === SelectVariant.typeaheadMulti) { const changedValue = field.value?.find( (p: string) => p === option, ) ? field.value.filter((p: string) => p !== option) : [...field.value!, option]; field.onChange(changedValue); } else { field.onChange([option]); const selectedItem = items.find((i) => i.id === option); if (selectedItem) { setSelected([selectedItem]); } } setSearch(""); }} isOpen={open} aria-label={t(name)} validated={errors[name] ? "error" : "default"} typeAheadAriaLabel={t(name)} chipGroupComponent={toChipGroupItems(field)} footer={ name === "policies" && !isAdminPermissionsClient ? ( ) : undefined } > {toSelectOptions()} )} /> ); };