import { ActionGroup, AlertVariant, Button, Card, CardBody, CardHeader, CardTitle, FormGroup, PageSection, Switch, Text, TextContent, } from "@patternfly/react-core"; import { saveAs } from "file-saver"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import type CertificateRepresentation from "@keycloak/keycloak-admin-client/lib/defs/certificateRepresentation"; import type KeyStoreConfig from "@keycloak/keycloak-admin-client/lib/defs/keystoreConfig"; import { Controller, useFormContext, useWatch } from "react-hook-form-v7"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form-access/FormAccess"; import { HelpItem } from "../../components/help-enabler/HelpItem"; import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput"; import { useAdminClient, useFetch } from "../../context/auth/AdminClient"; import { convertAttributeNameToForm } from "../../util"; import useToggle from "../../utils/useToggle"; import { FormFields } from "../ClientDetails"; import { Certificate } from "./Certificate"; import { GenerateKeyDialog, getFileExtension } from "./GenerateKeyDialog"; import { ImportFile, ImportKeyDialog } from "./ImportKeyDialog"; type KeysProps = { save: () => void; clientId: string; hasConfigureAccess?: boolean; }; const attr = "jwt.credential"; export const Keys = ({ clientId, save, hasConfigureAccess }: KeysProps) => { const { t } = useTranslation("clients"); const { control, register, getValues, formState: { isDirty }, } = useFormContext(); const { adminClient } = useAdminClient(); const { addAlert, addError } = useAlerts(); const [keyInfo, setKeyInfo] = useState(); const [openGenerateKeys, toggleOpenGenerateKeys, setOpenGenerateKeys] = useToggle(); const [openImportKeys, toggleOpenImportKeys, setOpenImportKeys] = useToggle(); const [key, setKey] = useState(0); const refresh = () => setKey(key + 1); const useJwksUrl = useWatch({ control, name: convertAttributeNameToForm("attributes.use.jwks.url"), defaultValue: "false", }); useFetch( () => adminClient.clients.getKeyInfo({ id: clientId, attr }), (info) => setKeyInfo(info), [key] ); const generate = async (config: KeyStoreConfig) => { try { const keyStore = await adminClient.clients.generateAndDownloadKey( { id: clientId, attr, }, config ); saveAs( new Blob([keyStore], { type: "application/octet-stream" }), `keystore.${getFileExtension(config.format ?? "")}` ); addAlert(t("generateSuccess"), AlertVariant.success); refresh(); } catch (error) { addError("clients:generateError", error); } }; const importKey = async (importFile: ImportFile) => { try { const formData = new FormData(); const { file, ...rest } = importFile; Object.entries(rest).map((entry) => formData.append(entry[0], entry[1] as string) ); formData.append("file", file.value!); await adminClient.clients.uploadCertificate( { id: clientId, attr }, formData ); addAlert(t("importSuccess"), AlertVariant.success); refresh(); } catch (error) { addError("clients:importError", error); } }; return ( {openGenerateKeys && ( )} {openImportKeys && ( )} {t("jwksUrlConfig")} {t("keysIntro")} } > ( field.onChange(`${value}`)} aria-label={t("useJwksUrl")} /> )} /> {useJwksUrl !== "true" && (keyInfo ? ( ) : ( "No client certificate configured" ))} {useJwksUrl === "true" && ( } > )} ); };