/* eslint-disable */ // @ts-nocheck import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import type { PartialImportRealmRepresentation, PartialImportResponse, PartialImportResult, } from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; import { KeycloakSelect } from "../../shared/keycloak-ui-shared"; import { Alert, Button, ButtonVariant, Checkbox, DataList, DataListCell, DataListItem, DataListItemCells, DataListItemRow, Divider, Label, Modal, ModalVariant, SelectOption, Stack, StackItem, Text, TextContent, } from "../../shared/@patternfly/react-core"; import { FormEvent, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useAdminClient } from "../admin-client"; import { useAlerts } from "../../shared/keycloak-ui-shared"; import { JsonFileUpload } from "../components/json-file-upload/JsonFileUpload"; import { KeycloakDataTable } from "../../shared/keycloak-ui-shared"; import { useRealm } from "../context/realm-context/RealmContext"; export type PartialImportProps = { open: boolean; toggleDialog: () => void; }; // An imported JSON file can either be an array of realm objects // or a single realm object. type ImportedMultiRealm = RealmRepresentation | RealmRepresentation[]; type NonRoleResource = "users" | "clients" | "groups" | "identityProviders"; type RoleResource = "realmRoles" | "clientRoles"; type Resource = NonRoleResource | RoleResource; type CollisionOption = "FAIL" | "SKIP" | "OVERWRITE"; type ResourceChecked = { [k in Resource]: boolean }; const INITIAL_RESOURCES: Readonly = { users: false, clients: false, groups: false, identityProviders: false, realmRoles: false, clientRoles: false, }; export const PartialImportDialog = (props: PartialImportProps) => { const { adminClient } = useAdminClient(); const { t } = useTranslation(); const { realm } = useRealm(); const [importedFile, setImportedFile] = useState(); const isFileSelected = !!importedFile; const [isRealmSelectOpen, setIsRealmSelectOpen] = useState(false); const [isCollisionSelectOpen, setIsCollisionSelectOpen] = useState(false); const [importInProgress, setImportInProgress] = useState(false); const [collisionOption, setCollisionOption] = useState("FAIL"); const [targetRealm, setTargetRealm] = useState({}); const [importResponse, setImportResponse] = useState(); const { addError } = useAlerts(); const [resourcesToImport, setResourcesToImport] = useState(INITIAL_RESOURCES); const isAnyResourceChecked = Object.values(resourcesToImport).some( (checked) => checked, ); const resetResourcesToImport = () => { setResourcesToImport(INITIAL_RESOURCES); }; const resetInputState = () => { setImportedFile(undefined); setTargetRealm({}); setCollisionOption("FAIL"); resetResourcesToImport(); }; // when dialog opens or closes, clear state useEffect(() => { setImportInProgress(false); setImportResponse(undefined); resetInputState(); }, [props.open]); const handleFileChange = (value: ImportedMultiRealm) => { resetInputState(); setImportedFile(value); if (!Array.isArray(value)) { setTargetRealm(value); } else if (value.length > 0) { setTargetRealm(value[0]); } }; const handleRealmSelect = (realm: string | number | object) => { setTargetRealm(realm as RealmRepresentation); setIsRealmSelectOpen(false); resetResourcesToImport(); }; const handleResourceCheckBox = ( checked: boolean, event: FormEvent, ) => { const resource = event.currentTarget.name as Resource; setResourcesToImport({ ...resourcesToImport, [resource]: checked, }); }; const realmSelectOptions = (realms: RealmRepresentation[]) => realms.map((realm) => ( {realm.realm || realm.id} )); const handleCollisionSelect = (option: string | number | object) => { setCollisionOption(option as CollisionOption); setIsCollisionSelectOpen(false); }; const collisionOptions = () => { return [ {t("FAIL")} , {t("SKIP")} , {t("OVERWRITE")} , ]; }; const targetHasResources = () => { return ( targetHasResource("users") || targetHasResource("groups") || targetHasResource("clients") || targetHasResource("identityProviders") || targetHasRealmRoles() || targetHasClientRoles() ); }; const targetHasResource = (resource: NonRoleResource) => { const value = targetRealm[resource]; return value !== undefined && value.length > 0; }; const targetHasRealmRoles = () => { const value = targetRealm.roles?.realm; return value !== undefined && value.length > 0; }; const targetHasClientRoles = () => { const value = targetRealm.roles?.client; return value !== undefined && Object.keys(value).length > 0; }; const itemCount = (resource: Resource) => { if (!isFileSelected) return 0; if (resource === "realmRoles") { return targetRealm.roles?.realm?.length ?? 0; } if (resource === "clientRoles") { return targetHasClientRoles() ? clientRolesCount(targetRealm.roles!.client!) : 0; } return targetRealm[resource]?.length ?? 0; }; const clientRolesCount = ( clientRoles: Record, ) => Object.values(clientRoles).reduce((total, role) => total + role.length, 0); const resourceDataListItem = ( resource: Resource, resourceDisplayName: string, ) => { return ( handleResourceCheckBox(checked, event) } data-testid={resource + "-checkbox"} /> , ]} /> ); }; const jsonForImport = () => { const jsonToImport: PartialImportRealmRepresentation = { ifResourceExists: collisionOption, id: targetRealm.id, realm: targetRealm.realm, }; if (resourcesToImport["users"]) jsonToImport.users = targetRealm.users; if (resourcesToImport["groups"]) jsonToImport.groups = targetRealm.groups; if (resourcesToImport["identityProviders"]) jsonToImport.identityProviders = targetRealm.identityProviders; if (resourcesToImport["clients"]) jsonToImport.clients = targetRealm.clients; if (resourcesToImport["realmRoles"] || resourcesToImport["clientRoles"]) { jsonToImport.roles = targetRealm.roles; if (!resourcesToImport["realmRoles"]) delete jsonToImport.roles?.realm; if (!resourcesToImport["clientRoles"]) delete jsonToImport.roles?.client; } return jsonToImport; }; async function doImport() { if (importInProgress) return; setImportInProgress(true); try { const importResults = await adminClient.realms.partialImport({ realm, rep: jsonForImport(), }); setImportResponse(importResults); } catch (error) { addError("importFail", error); } setImportInProgress(false); } const importModal = () => { return ( { await doImport(); }} > {t("import")} , , ]} > {t("partialImportHeaderText")} {isFileSelected && targetHasResources() && ( <> {Array.isArray(importedFile) && importedFile.length > 1 && ( {t("selectRealm")}: setIsRealmSelectOpen(!isRealmSelectOpen)} selections={targetRealm.id} onSelect={(value) => handleRealmSelect(value)} placeholderText={targetRealm.realm || targetRealm.id} > {realmSelectOptions(importedFile)} )} {t("chooseResources")}: {targetHasResource("users") && resourceDataListItem("users", t("users"))} {targetHasResource("groups") && resourceDataListItem("groups", t("groups"))} {targetHasResource("clients") && resourceDataListItem("clients", t("clients"))} {targetHasResource("identityProviders") && resourceDataListItem( "identityProviders", t("identityProviders"), )} {targetHasRealmRoles() && resourceDataListItem("realmRoles", t("realmRoles"))} {targetHasClientRoles() && resourceDataListItem("clientRoles", t("clientRoles"))} {t("selectIfResourceExists")}: { setIsCollisionSelectOpen(!isCollisionSelectOpen); }} selections={collisionOption} onSelect={handleCollisionSelect} placeholderText={t(collisionOption)} > {collisionOptions()} )} ); }; const importCompleteMessage = () => { return `${t("importAdded", { count: importResponse?.added, })} ${t("importSkipped", { count: importResponse?.skipped, })} ${t("importOverwritten", { count: importResponse?.overwritten, })}`; }; const loader = async (first = 0, max = 15) => { if (!importResponse) { return []; } const last = Math.min(first + max, importResponse.results.length); return importResponse.results.slice(first, last); }; const ActionLabel = (importRecord: PartialImportResult) => { switch (importRecord.action) { case "ADDED": return ( ); case "SKIPPED": return ( ); case "OVERWRITTEN": return ( ); default: return ""; } }; const TypeRenderer = (importRecord: PartialImportResult) => { const typeMap = new Map([ ["CLIENT", t("clients")], ["REALM_ROLE", t("realmRoles")], ["USER", t("users")], ["CLIENT_ROLE", t("clientRoles")], ["IDP", t("identityProviders")], ["GROUP", t("groups")], ]); return {typeMap.get(importRecord.resourceType)}; }; const importCompletedModal = () => { return ( { props.toggleDialog(); }} > {t("close")} , ]} > ); }; if (!importResponse) { return importModal(); } return importCompletedModal(); };