generate provider form with the dynmic components (#2198)

* generate provider form with the dynmic components

Added a FileComponent and removed the need for custom views

* fixed merge error

* fixed tests

* Update src/realm-settings/keys/key-providers/KeyProviderForm.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>

Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Erik Jan de Wit 2022-03-18 14:20:23 +01:00 committed by GitHub
parent 84d501c145
commit f3d1c0fc2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 7195 additions and 8004 deletions

View file

@ -0,0 +1,47 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Controller, useFormContext } from "react-hook-form";
import { FileUpload, FormGroup } from "@patternfly/react-core";
import { HelpItem } from "../help-enabler/HelpItem";
import type { ComponentProps } from "./components";
export const FileComponent = ({
name,
label,
helpText,
defaultValue,
isDisabled = false,
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
const [filename, setFilename] = useState("");
return (
<FormGroup
label={t(label!)}
labelIcon={
<HelpItem helpText={t(helpText!)} fieldLabelId={`dynamic:${label}`} />
}
fieldId={name!}
>
<Controller
name={`config.${name}`}
control={control}
defaultValue={defaultValue || ""}
render={({ onChange, value }) => (
<FileUpload
id={name!}
value={value}
filename={filename}
isDisabled={isDisabled}
onChange={(value, filename) => {
onChange(value);
setFilename(filename);
}}
/>
)}
/>
</FormGroup>
);
};

View file

@ -11,6 +11,7 @@ import { ClientSelectComponent } from "./ClientSelectComponent";
import { MultiValuedStringComponent } from "./MultivaluedStringComponent";
import { MultiValuedListComponent } from "./MultivaluedListComponent";
import { GroupComponent } from "./GroupComponent";
import { FileComponent } from "./FileComponent";
export type ComponentProps = Omit<ConfigPropertyRepresentation, "type"> & {
isDisabled?: boolean;
@ -27,6 +28,7 @@ const ComponentTypes = [
"MultivaluedList",
"ClientList",
"MultivaluedString",
"File",
] as const;
export type Components = typeof ComponentTypes[number];
@ -44,6 +46,7 @@ export const COMPONENTS: {
ClientList: ClientSelectComponent,
MultivaluedList: MultiValuedListComponent,
MultivaluedString: MultiValuedStringComponent,
File: FileComponent,
} as const;
export const isValidComponentType = (value: string): value is Components =>

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,6 @@ import {
AlertVariant,
FormGroup,
ValidatedOptions,
Switch,
TextInput,
PageSection,
ActionGroup,
@ -21,12 +20,8 @@ import { FormAccess } from "../../../components/form-access/FormAccess";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { KEY_PROVIDER_TYPE } from "../../../util";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import AesView from "./aes-generated/View";
import EcdsaView from "./ecdsa-generated/View";
import HmacView from "./hmac-generated/View";
import JavaKeystoreView from "./java-keystore/View";
import RsaView from "./rsa/View";
import RsaGeneratedView from "./rsa-generated/View";
import { DynamicComponents } from "../../../components/dynamic/DynamicComponents";
import { useServerInfo } from "../../../context/server-info/ServerInfoProvider";
type KeyProviderFormProps = {
id?: string;
@ -34,30 +29,6 @@ type KeyProviderFormProps = {
onClose?: () => void;
};
const SubView = ({ providerType }: { providerType: ProviderType }) => {
switch (providerType) {
case "aes-generated":
return <AesView />;
case "ecdsa-generated":
return <EcdsaView />;
case "hmac-generated":
return <HmacView />;
case "java-keystore":
return <JavaKeystoreView />;
case "rsa":
return <RsaView />;
case "rsa-enc":
return <RsaView isEnc />;
case "rsa-enc-generated":
return <RsaGeneratedView isEnc />;
case "rsa-generated":
return <RsaGeneratedView />;
default:
return <>invalid view type</>;
}
};
export const KeyProviderForm = ({
providerType,
onClose,
@ -67,6 +38,10 @@ export const KeyProviderForm = ({
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const serverInfo = useServerInfo();
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const form = useForm<ComponentRepresentation>({
shouldUnregister: false,
mode: "onChange",
@ -74,6 +49,11 @@ export const KeyProviderForm = ({
const { register, control, handleSubmit, errors, reset } = form;
const save = async (component: ComponentRepresentation) => {
if (component.config)
Object.entries(component.config).forEach(
([key, value]) =>
(component.config![key] = Array.isArray(value) ? value : [value])
);
try {
if (id) {
await adminClient.components.update(
@ -168,66 +148,13 @@ export const KeyProviderForm = ({
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
fieldLabelId="enabled"
/>
}
>
<Controller
name="config.enabled"
control={control}
defaultValue={["true"]}
render={({ onChange, value }) => (
<Switch
id="kc-enabled"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid="enabled"
onChange={(value) => {
onChange([value.toString()]);
}}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
fieldLabelId="realm-settings:active"
/>
}
>
<Controller
name="config.active"
control={control}
defaultValue={["true"]}
render={({ onChange, value }) => {
return (
<Switch
id="kc-active"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid="active"
onChange={(value) => {
onChange([value.toString()]);
}}
/>
);
}}
/>
</FormGroup>
<FormProvider {...form}>
<SubView providerType={providerType} />
<DynamicComponents
properties={
allComponentTypes.find((type) => type.id === providerType)
?.properties || []
}
/>
</FormProvider>
<ActionGroup>
<Button

View file

@ -1,64 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useFormContext, Controller } from "react-hook-form";
import {
FormGroup,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { useServerInfo } from "../../../../context/server-info/ServerInfoProvider";
import { HelpItem } from "../../../../components/help-enabler/HelpItem";
import { KEY_PROVIDER_TYPE } from "../../../../util";
import useToggle from "../../../../utils/useToggle";
export default function View() {
const { t } = useTranslation("realm-settings");
const { control } = useFormContext();
const [isKeySizeDropdownOpen, toggleDropdown] = useToggle();
const serverInfo = useServerInfo();
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const aesSecretSizeOptions = allComponentTypes[0].properties[3].options ?? [];
return (
<FormGroup
label={t("AESKeySize")}
fieldId="kc-aes-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:AESKeySize"
fieldLabelId="realm-settings:AESKeySize"
/>
}
>
<Controller
name="config.secretSize"
control={control}
defaultValue={[aesSecretSizeOptions[0]]}
render={({ onChange, value }) => (
<Select
toggleId="kc-aes-keysize"
onToggle={toggleDropdown}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleDropdown();
}}
selections={[value.toString()]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("aesKeySize")}
data-testid="select-secret-size"
>
{aesSecretSizeOptions.map((item) => (
<SelectOption selected={item === value} key={item} value={item} />
))}
</Select>
)}
/>
</FormGroup>
);
}

View file

@ -1,64 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useFormContext, Controller } from "react-hook-form";
import {
FormGroup,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { useServerInfo } from "../../../../context/server-info/ServerInfoProvider";
import { HelpItem } from "../../../../components/help-enabler/HelpItem";
import { KEY_PROVIDER_TYPE } from "../../../../util";
import useToggle from "../../../../utils/useToggle";
export default function View() {
const { t } = useTranslation("realm-settings");
const { control } = useFormContext();
const [isKeySizeDropdownOpen, toggleDropdown] = useToggle();
const serverInfo = useServerInfo();
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const ecdsaEllipticCurveOptions = allComponentTypes[1].properties[3].options!;
return (
<FormGroup
label={t("ellipticCurve")}
fieldId="kc-elliptic-curve"
labelIcon={
<HelpItem
helpText="realm-settings-help:ellipticCurve"
fieldLabelId="realm-settings:ellipticCurve"
/>
}
>
<Controller
name="config.ecdsaEllipticCurveKey"
control={control}
defaultValue={[ecdsaEllipticCurveOptions[0]]}
render={({ onChange, value }) => (
<Select
toggleId="kc-ecdsa-elliptic-curve"
onToggle={toggleDropdown}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleDropdown();
}}
selections={[value.toString()]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("ellipticCurve")}
data-testid="select-elliptic-curve-size"
>
{ecdsaEllipticCurveOptions.map((item) => (
<SelectOption selected={item === value} key={item} value={item} />
))}
</Select>
)}
/>
</FormGroup>
);
}

View file

@ -1,109 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useFormContext, Controller } from "react-hook-form";
import {
FormGroup,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { useServerInfo } from "../../../../context/server-info/ServerInfoProvider";
import { HelpItem } from "../../../../components/help-enabler/HelpItem";
import { KEY_PROVIDER_TYPE } from "../../../../util";
import useToggle from "../../../../utils/useToggle";
export default function View() {
const { t } = useTranslation("realm-settings");
const { control } = useFormContext();
const [isKeySizeDropdownOpen, toggleKeySizeDropdown] = useToggle();
const [isEllipticCurveDropdownOpen, toggleEllipticDropdown] = useToggle();
const serverInfo = useServerInfo();
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const hmacSecretSizeOptions =
allComponentTypes[2].properties[3].options ?? [];
const hmacAlgorithmOptions = allComponentTypes[2].properties[4].options ?? [];
return (
<>
<FormGroup
label={t("secretSize")}
fieldId="kc-hmac-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:secretSize"
fieldLabelId="realm-settings:secretSize"
/>
}
>
<Controller
name="config.secretSize"
control={control}
defaultValue={[hmacSecretSizeOptions[3]]}
render={({ onChange, value }) => (
<Select
toggleId="kc-hmac-keysize"
onToggle={toggleKeySizeDropdown}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleKeySizeDropdown();
}}
selections={[value.toString()]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("hmacKeySize")}
data-testid="select-secret-size"
>
{hmacSecretSizeOptions.map((item) => (
<SelectOption
selected={item === value}
key={item}
value={item}
/>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
fieldLabelId="realm-settings:algorithm"
/>
}
>
<Controller
name="config.algorithm"
control={control}
defaultValue={[hmacAlgorithmOptions[0]]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={toggleEllipticDropdown}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleEllipticDropdown();
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("emailTheme")}
isOpen={isEllipticCurveDropdownOpen}
>
{hmacAlgorithmOptions!.map((p, idx) => (
<SelectOption selected={p === value} key={idx} value={p} />
))}
</Select>
)}
/>
</FormGroup>
</>
);
}

View file

@ -1,167 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useFormContext, Controller } from "react-hook-form";
import {
FormGroup,
Select,
SelectOption,
SelectVariant,
TextInput,
} from "@patternfly/react-core";
import { useServerInfo } from "../../../../context/server-info/ServerInfoProvider";
import { HelpItem } from "../../../../components/help-enabler/HelpItem";
import { KEY_PROVIDER_TYPE } from "../../../../util";
import useToggle from "../../../../utils/useToggle";
export default function View() {
const { t } = useTranslation("realm-settings");
const { control } = useFormContext();
const [isAlgorithmDropdownOpen, toggleDropdown] = useToggle();
const serverInfo = useServerInfo();
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const javaKeystoreAlgorithmOptions =
allComponentTypes[3].properties[3].options ?? [];
return (
<>
<FormGroup
label={t("algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
fieldLabelId="realm-settings:algorithm"
/>
}
>
<Controller
name="config.algorithm"
control={control}
defaultValue={[javaKeystoreAlgorithmOptions[0]]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={toggleDropdown}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleDropdown();
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("algorithm")}
isOpen={isAlgorithmDropdownOpen}
>
{javaKeystoreAlgorithmOptions!.map((p, idx) => (
<SelectOption selected={p === value} key={idx} value={p} />
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("keystore")}
fieldId="kc-keystore"
labelIcon={
<HelpItem
helpText="realm-settings-help:keystore"
fieldLabelId="realm-settings:keystore"
/>
}
>
<Controller
name="config.keystore"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("keystore")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="keystore"
/>
)}
/>
</FormGroup>
<FormGroup
label={t("keystorePassword")}
fieldId="kc-keystore-password"
labelIcon={
<HelpItem
helpText="realm-settings-help:keystorePassword"
fieldLabelId="realm-settings:keystorePassword"
/>
}
>
<Controller
name="config.keystorePassword"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="keystorePassword"
/>
)}
/>
</FormGroup>
<FormGroup
label={t("keyAlias")}
fieldId="kc-key-alias"
labelIcon={
<HelpItem
helpText="realm-settings-help:keyAlias"
fieldLabelId="realm-settings:keyAlias"
/>
}
>
<Controller
name="config.keyAlias"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("keyAlias")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="key-alias"
/>
)}
/>
</FormGroup>
<FormGroup
label={t("keyPassword")}
fieldId="kc-key-password"
labelIcon={
<HelpItem
helpText="realm-settings-help:keyPassword"
fieldLabelId="realm-settings:keyPassword"
/>
}
>
<Controller
name="config.keyPassword"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("keyPassword")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="key-password"
/>
)}
/>
</FormGroup>
</>
);
}

View file

@ -1,123 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useFormContext, Controller } from "react-hook-form";
import {
FormGroup,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { useServerInfo } from "../../../../context/server-info/ServerInfoProvider";
import { HelpItem } from "../../../../components/help-enabler/HelpItem";
import { KEY_PROVIDER_TYPE } from "../../../../util";
import useToggle from "../../../../utils/useToggle";
export default function View({ isEnc = false }: { isEnc?: boolean }) {
const { t } = useTranslation("realm-settings");
const { control } = useFormContext();
const [isKeySizeDropdownOpen, toggleKeySizeDropdown] = useToggle();
const [isEllipticCurveDropdownOpen, toggleEllipticCurveDropdown] =
useToggle();
const serverInfo = useServerInfo();
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const rsaGeneratedKeySizeOptions =
allComponentTypes[6].properties[3].options ?? [];
const rsaGeneratedAlgorithmOptions =
allComponentTypes[6].properties[4].options ?? [];
const rsaEncGeneratedKeySizeOptions =
allComponentTypes[5].properties[3].options ?? [];
const rsaEncGeneratedAlgorithmOptions =
allComponentTypes[5].properties[4].options ?? [];
return (
<>
<FormGroup
label={t("keySize")}
fieldId="kc-rsa-generated-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:keySize"
fieldLabelId="realm-settings:keySize"
/>
}
>
<Controller
name="config.keySize"
control={control}
defaultValue={isEnc ? ["4096"] : ["2048"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-generated-keysize"
onToggle={toggleKeySizeDropdown}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleKeySizeDropdown();
}}
selections={[value.toString()]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("KeySize")}
data-testid="select-secret-size"
>
{(isEnc
? rsaEncGeneratedKeySizeOptions
: rsaGeneratedKeySizeOptions
).map((item) => (
<SelectOption
selected={item === value}
key={item}
value={item}
/>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
fieldLabelId="realm-settings:algorithm"
/>
}
>
<Controller
name="config.algorithm"
control={control}
defaultValue={isEnc ? ["RSA-OAEP"] : ["RS256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={toggleEllipticCurveDropdown}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleEllipticCurveDropdown();
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("algorithm")}
isOpen={isEllipticCurveDropdownOpen}
>
{(isEnc
? rsaEncGeneratedAlgorithmOptions
: rsaGeneratedAlgorithmOptions
).map((p, idx) => (
<SelectOption selected={p === value} key={idx} value={p} />
))}
</Select>
)}
/>
</FormGroup>
</>
);
}

View file

@ -1,130 +0,0 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { useFormContext, Controller } from "react-hook-form";
import {
FileUpload,
FormGroup,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { useServerInfo } from "../../../../context/server-info/ServerInfoProvider";
import { HelpItem } from "../../../../components/help-enabler/HelpItem";
import { KEY_PROVIDER_TYPE } from "../../../../util";
import useToggle from "../../../../utils/useToggle";
export default function View({ isEnc = false }: { isEnc?: boolean }) {
const { t } = useTranslation("realm-settings");
const { control } = useFormContext();
const [isRSAalgDropdownOpen, toggleDropdown] = useToggle();
const [privateKey, setPrivateKey] = useState("");
const [certificate, setCertificate] = useState("");
const serverInfo = useServerInfo();
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const rsaAlgOptions =
allComponentTypes[isEnc ? 5 : 4].properties[isEnc ? 5 : 3].options ?? [];
return (
<>
<FormGroup
label={t("algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
fieldLabelId="realm-settings:algorithm"
/>
}
>
<Controller
name="config.algorithm"
defaultValue={[rsaAlgOptions[0]]}
control={control}
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-algorithm"
onToggle={() => toggleDropdown()}
onSelect={(_, value) => {
onChange([value.toString()]);
toggleDropdown();
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("algorithm")}
isOpen={isRSAalgDropdownOpen}
data-testid="select-rsa-algorithm"
>
{rsaAlgOptions!.map((p, idx) => (
<SelectOption selected={p === value} key={idx} value={p} />
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("privateRSAKey")}
fieldId="kc-private-rsa-key"
labelIcon={
<HelpItem
helpText="realm-settings-help:privateRSAKey"
fieldLabelId="realm-settings:privateRSAKey"
/>
}
>
<Controller
name="config.privateKey"
control={control}
defaultValue={[]}
render={({ onChange, value }) => (
<FileUpload
id="importPrivateKey"
type="text"
value={value.value}
filename={privateKey}
hideDefaultPreview
onChange={(value, filename) => {
onChange(value);
setPrivateKey(filename);
}}
filenamePlaceholder={t("filenamePlaceholder")}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("x509Certificate")}
fieldId="kc-x509Certificate"
labelIcon={
<HelpItem
helpText="realm-settings-help:x509Certificate"
fieldLabelId="realm-settings:x509Certificate"
/>
}
>
<Controller
name="config.certificate"
control={control}
defaultValue={[]}
render={({ onChange, value }) => (
<FileUpload
id="importCertificate"
type="text"
value={value}
filename={certificate}
hideDefaultPreview
onChange={(value, filename) => {
onChange(value);
setCertificate(filename);
}}
filenamePlaceholder={t("filenamePlaceholder")}
/>
)}
/>
</FormGroup>
</>
);
}