refacored the keys tab removed duplication (#2146)

This commit is contained in:
Erik Jan de Wit 2022-02-28 16:22:00 +01:00 committed by GitHub
parent 6e0414c557
commit 5c64ab6a4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 1462 additions and 4351 deletions

View file

@ -0,0 +1,25 @@
export default class KeysTab {
private readonly keysTab = "rs-keys-tab";
private readonly providersTab = "rs-providers-tab";
private readonly addProviderDropdown = "addProviderDropdown";
goToKeysTab() {
cy.findByTestId(this.keysTab).click();
return this;
}
goToProvidersTab() {
this.goToKeysTab();
cy.findByTestId(this.providersTab).click();
return this;
}
addProvider(provider: string) {
cy.findByTestId(this.addProviderDropdown).click();
cy.findByTestId(`option-${provider}`).click();
return this;
}
}

View file

@ -68,15 +68,15 @@ export default class RealmSettingsPage {
enableStartTlsCheck = "enable-start-tls"; enableStartTlsCheck = "enable-start-tls";
addProviderDropdown = "addProviderDropdown"; addProviderDropdown = "addProviderDropdown";
addProviderButton = "add-provider-button"; addProviderButton = "add-provider-button";
displayName = "display-name-input"; displayName = "name-input";
enableEvents = "eventsEnabled"; enableEvents = "eventsEnabled";
eventsUserSave = "save-user"; eventsUserSave = "save-user";
enableAdminEvents = "adminEventsEnabled"; enableAdminEvents = "adminEventsEnabled";
eventsAdminSave = "save-admin"; eventsAdminSave = "save-admin";
eventTypeColumn = 'tbody > tr > [data-label="Event saved type"]'; eventTypeColumn = 'tbody > tr > [data-label="Event saved type"]';
filterSelectMenu = ".kc-filter-type-select"; filterSelectMenu = ".kc-filter-type-select";
passiveKeysOption = "passive-keys-option"; passiveKeysOption = "PASSIVE-option";
disabledKeysOption = "disabled-keys-option"; disabledKeysOption = "DISABLED-option";
testConnectionButton = "test-connection-button"; testConnectionButton = "test-connection-button";
modalTestConnectionButton = "modal-test-connection-button"; modalTestConnectionButton = "modal-test-connection-button";
emailAddressInput = "email-address-input"; emailAddressInput = "email-address-input";

View file

@ -2,7 +2,10 @@ import React, { ReactNode, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { get } from "lodash-es"; import { get } from "lodash-es";
import { import {
ActionsColumn,
IAction,
TableComposable, TableComposable,
TableComposableProps,
Tbody, Tbody,
Td, Td,
Th, Th,
@ -17,10 +20,11 @@ export type Field<T> = {
cellRenderer?: (row: T) => ReactNode; cellRenderer?: (row: T) => ReactNode;
}; };
type DraggableTableProps<T> = { type DraggableTableProps<T> = Omit<TableComposableProps, "data" | "ref"> & {
keyField: string; keyField: string;
columns: Field<T>[]; columns: Field<T>[];
data: T[]; data: T[];
actions?: IAction[];
onDragFinish: (dragged: string, newOrder: string[]) => void; onDragFinish: (dragged: string, newOrder: string[]) => void;
}; };
@ -28,7 +32,9 @@ export function DraggableTable<T>({
keyField, keyField,
columns, columns,
data, data,
actions,
onDragFinish, onDragFinish,
...props
}: DraggableTableProps<T>) { }: DraggableTableProps<T>) {
const { t } = useTranslation("authentication"); const { t } = useTranslation("authentication");
const bodyRef = useRef<HTMLTableSectionElement>(null); const bodyRef = useRef<HTMLTableSectionElement>(null);
@ -169,6 +175,7 @@ export function DraggableTable<T>({
<TableComposable <TableComposable
aria-label="Draggable table" aria-label="Draggable table"
className={state.dragging ? styles.modifiers.dragOver : ""} className={state.dragging ? styles.modifiers.dragOver : ""}
{...props}
> >
<Thead> <Thead>
<Tr> <Tr>
@ -208,6 +215,11 @@ export function DraggableTable<T>({
: get(row, column.name)} : get(row, column.name)}
</Td> </Td>
))} ))}
{actions && (
<Td isActionCell>
<ActionsColumn items={actions} rowData={row} />
</Td>
)}
</Tr> </Tr>
))} ))}
</Tbody> </Tbody>

View file

@ -1,5 +1,5 @@
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation"; import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import React, { useState } from "react"; import React, { useState, KeyboardEvent } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
FormGroup, FormGroup,
@ -189,7 +189,7 @@ export const AuthorizationEvaluate = ({
} }
}; };
const handleKeyDown = (e: any) => { const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") { if (e.key === "Enter") {
onSearch(); onSearch();
} }

View file

@ -3,6 +3,7 @@ import React, {
FunctionComponent, FunctionComponent,
ReactNode, ReactNode,
useState, useState,
KeyboardEvent,
} from "react"; } from "react";
import { import {
Toolbar, Toolbar,
@ -55,7 +56,7 @@ export const TableToolbar: FunctionComponent<TableToolbarProps> = ({
} }
}; };
const handleKeyDown = (e: any) => { const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") { if (e.key === "Enter") {
onSearch(); onSearch();
} }

View file

@ -1,347 +0,0 @@
import React, { useState } from "react";
import {
AlertVariant,
Button,
ButtonVariant,
Form,
FormGroup,
Modal,
ModalVariant,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import { useAdminClient } from "../context/auth/AdminClient";
import { useAlerts } from "../components/alert/Alerts";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { KEY_PROVIDER_TYPE } from "../util";
type JavaKeystoreModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const JavaKeystoreModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: // save,
JavaKeystoreModalProps) => {
const { t } = useTranslation("groups");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { handleSubmit, control } = useForm({});
const [isEllipticCurveDropdownOpen, setIsEllipticCurveDropdownOpen] =
useState(false);
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh();
} catch (error) {
addError("groups:saveProviderError", error);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
actions={[
<Button
data-testid="add-provider-button"
key="confirm"
variant="primary"
type="submit"
form="add-provider"
>
{t("common:Add")}
</Button>,
<Button
id="modal-cancel"
data-testid="cancel"
key="cancel"
variant={ButtonVariant.link}
onClick={() => {
handleModalToggle!();
}}
>
{t("common:cancel")}
</Button>,
]}
>
<Form
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
onSubmit={handleSubmit(save!)}
>
<FormGroup
label={t("consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
fieldLabelId="realm-settings:consoleDisplayName"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</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={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={(value) => {
onChange([value + ""]);
}}
/>
)}
/>
</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 }) => (
<Switch
id="kc-active"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={(value) => {
onChange([value + ""]);
}}
/>
)}
/>
</FormGroup>
{providerType === "java-keystore" && (
<>
<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={["RS256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={() =>
setIsEllipticCurveDropdownOpen(
!isEllipticCurveDropdownOpen
)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsEllipticCurveDropdownOpen(false);
}}
selections={[value + ""]}
variant={SelectVariant.single}
aria-label={t("algorithm")}
isOpen={isEllipticCurveDropdownOpen}
placeholderText="Select one..."
data-testid="select-algorithm"
>
{allComponentTypes[3].properties[3].options!.map(
(p, idx) => (
<SelectOption
selected={p === value}
key={`algorithm-${idx}`}
value={p}
></SelectOption>
)
)}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("keystore")}
fieldId="kc-login-theme"
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 + ""]);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("keystorePassword")}
fieldId="kc-login-theme"
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 + ""]);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("keyAlias")}
fieldId="kc-login-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:keyAlias"
fieldLabelId="realm-settings:keyAlias"
/>
}
>
<Controller
name="config.keyAlias"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
onChange={(value) => {
onChange([value + ""]);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("keyPassword")}
fieldId="kc-login-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:keyPassword"
fieldLabelId="realm-settings:keyPassword"
/>
}
>
<Controller
name="config.keyPassword"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
onChange={(value) => {
onChange([value + ""]);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</FormGroup>
</>
)}
</Form>
</Modal>
);
};

View file

@ -1,479 +0,0 @@
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import {
AlertVariant,
Button,
ButtonVariant,
DataList,
DataListAction,
DataListCell,
DataListControl,
DataListDragButton,
DataListItem,
DataListItemCells,
DataListItemRow,
Dropdown,
DropdownItem,
DropdownPosition,
DropdownToggle,
InputGroup,
KebabToggle,
PageSection,
TextInput,
Toolbar,
ToolbarGroup,
ToolbarItem,
Tooltip,
} from "@patternfly/react-core";
import { SearchIcon } from "@patternfly/react-icons";
import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/keyMetadataRepresentation";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import type ComponentTypeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentTypeRepresentation";
import "./realm-settings-section.css";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useAdminClient } from "../context/auth/AdminClient";
import { useAlerts } from "../components/alert/Alerts";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { useRealm } from "../context/realm-context/RealmContext";
import { Link, useRouteMatch } from "react-router-dom";
import { AESGeneratedModal } from "./key-providers/aes-generated/AESGeneratedModal";
import { JavaKeystoreModal } from "./key-providers/java-keystore/JavaKeystoreModal";
import { HMACGeneratedModal } from "./key-providers/hmac-generated/HMACGeneratedModal";
import { ECDSAGeneratedModal } from "./key-providers/ecdsa-generated/ECDSAGeneratedModal";
import { RSAModal } from "./RSAModal";
import { RSAEncGeneratedModal } from "./key-providers/rsa-enc-generated/RSAEncGeneratedModal";
import { RSAGeneratedModal } from "./key-providers/rsa-generated/RSAGeneratedModal";
import { KEY_PROVIDER_TYPE } from "../util";
type ComponentData = KeyMetadataRepresentation & {
id?: string;
providerDescription?: string;
name?: string;
toggleHidden?: boolean;
config?: any;
parentId?: string;
};
type KeysTabInnerProps = {
components: ComponentData[];
realmComponents: ComponentRepresentation[];
keyProviderComponentTypes: ComponentTypeRepresentation[];
refresh: () => void;
};
export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => {
const { t } = useTranslation("realm-settings");
const { addAlert, addError } = useAlerts();
const adminClient = useAdminClient();
const { realm } = useRealm();
const { url } = useRouteMatch();
const [id, setId] = useState("");
const [searchVal, setSearchVal] = useState("");
const [filteredComponents, setFilteredComponents] = useState<ComponentData[]>(
[]
);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const serverInfo = useServerInfo();
const providerTypes = (
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []
).map((item) => item.id);
const [itemOrder, setItemOrder] = useState<string[]>([]);
const [providerDropdownOpen, setProviderDropdownOpen] = useState(false);
const [defaultConsoleDisplayName, setDefaultConsoleDisplayName] =
useState("");
const [selectedComponent, setSelectedComponent] =
useState<ComponentRepresentation>();
const [liveText, setLiveText] = useState("");
useEffect(() => {
const itemIds = components.map((component) => component.id!);
setItemOrder(["data", ...itemIds]);
}, [components, searchVal]);
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: "realm-settings:deleteProviderTitle",
messageKey: t("deleteProviderConfirm") + selectedComponent?.name + "?",
continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
await adminClient.components.del({
id: selectedComponent!.id!,
realm: realm,
});
refresh();
addAlert(t("deleteProviderSuccess"), AlertVariant.success);
} catch (error) {
addError("realm-settings:deleteProviderError", error);
}
},
});
const onDragStart = async (id: string) => {
setLiveText(t("common:onDragStart", { item: id }));
setId(id);
};
const onDragMove = () => {
setLiveText(t("common:onDragMove", { item: id }));
};
const onDragCancel = () => {
setLiveText(t("common:onDragCancel"));
};
const onDragFinish = async (itemOrder: string[]) => {
setItemOrder(itemOrder);
setLiveText(t("common:onDragFinish"));
const updateAll = components.map((component: ComponentData) => {
const componentToSave = { ...component };
delete componentToSave.providerDescription;
return adminClient.components.update(
{ id: component.id! },
{
...componentToSave,
config: {
priority: [
(
itemOrder.length -
itemOrder.indexOf(component.id!) +
100
).toString(),
],
},
}
);
});
try {
await Promise.all(updateAll);
refresh();
addAlert(
t("realm-settings:saveProviderListSuccess"),
AlertVariant.success
);
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
const onSearch = () => {
if (searchVal !== "") {
setSearchVal(searchVal);
const x = components.filter((v) => {
return v.name?.includes(searchVal) || v.providerId?.includes(searchVal);
});
setFilteredComponents(x);
} else {
setSearchVal("");
setFilteredComponents(components);
}
};
const handleKeyDown = (e: any) => {
if (e.key === "Enter") {
onSearch();
}
};
const handleInputChange = (value: string) => {
setSearchVal(value);
};
const handleModalToggle = () => {
setIsCreateModalOpen(!isCreateModalOpen);
};
const [actionListOpen, setActionListOpen] = useState<boolean[]>(
components.map(() => false)
);
const toggleActionList = (index: number) => {
actionListOpen[index] = !actionListOpen[index];
setActionListOpen([...actionListOpen]);
};
return (
<>
{defaultConsoleDisplayName === "aes-generated" && (
<AESGeneratedModal
handleModalToggle={handleModalToggle}
providerType={defaultConsoleDisplayName}
refresh={refresh}
open={isCreateModalOpen}
/>
)}
{defaultConsoleDisplayName === "ecdsa-generated" && (
<ECDSAGeneratedModal
handleModalToggle={handleModalToggle}
providerType={defaultConsoleDisplayName}
refresh={refresh}
open={isCreateModalOpen}
/>
)}
{defaultConsoleDisplayName === "hmac-generated" && (
<HMACGeneratedModal
handleModalToggle={handleModalToggle}
providerType={defaultConsoleDisplayName}
refresh={refresh}
open={isCreateModalOpen}
/>
)}
{defaultConsoleDisplayName === "java-keystore" && (
<JavaKeystoreModal
handleModalToggle={handleModalToggle}
providerType={defaultConsoleDisplayName}
refresh={refresh}
open={isCreateModalOpen}
/>
)}
{defaultConsoleDisplayName === "rsa" && (
<RSAModal
handleModalToggle={handleModalToggle}
providerType={defaultConsoleDisplayName}
refresh={refresh}
open={isCreateModalOpen}
/>
)}
{defaultConsoleDisplayName === "rsa-generated" && (
<RSAGeneratedModal
handleModalToggle={handleModalToggle}
providerType={defaultConsoleDisplayName}
refresh={refresh}
open={isCreateModalOpen}
/>
)}
{defaultConsoleDisplayName === "rsa-enc-generated" && (
<RSAEncGeneratedModal
handleModalToggle={handleModalToggle}
providerType={defaultConsoleDisplayName}
refresh={refresh}
open={isCreateModalOpen}
/>
)}
<DeleteConfirm />
<PageSection variant="light" padding={{ default: "noPadding" }}>
<Toolbar>
<ToolbarGroup className="providers-toolbar">
<ToolbarItem>
<InputGroup>
<TextInput
name={"inputGroupName"}
id={"inputGroupName"}
type="search"
aria-label={t("common:search")}
placeholder={t("common:search")}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
/>
<Button
variant={ButtonVariant.control}
aria-label={t("common:search")}
>
<SearchIcon />
</Button>
</InputGroup>
</ToolbarItem>
<ToolbarItem>
<Dropdown
data-testid="addProviderDropdown"
className="add-provider-dropdown"
isOpen={providerDropdownOpen}
toggle={
<DropdownToggle
onToggle={(val) => setProviderDropdownOpen(val)}
isPrimary
>
{t("realm-settings:addProvider")}
</DropdownToggle>
}
dropdownItems={[
providerTypes.map((item) => (
<DropdownItem
onClick={() => {
handleModalToggle();
setProviderDropdownOpen(false);
setDefaultConsoleDisplayName(item);
}}
data-testid={`option-${item}`}
key={item}
>
{item}
</DropdownItem>
)),
]}
/>
</ToolbarItem>
</ToolbarGroup>
</Toolbar>
<DataList
aria-label={t("common:groups")}
onDragFinish={onDragFinish}
onDragStart={onDragStart}
onDragMove={onDragMove}
onDragCancel={onDragCancel}
itemOrder={itemOrder}
isCompact
>
<DataListItem aria-labelledby={"aria"} id="data" key="data">
<DataListItemRow className="test" data-testid="data-list-row">
<DataListDragButton
className="header-drag-button"
aria-label="Reorder"
aria-describedby={t("common-help:dragHelp")}
aria-pressed="false"
isDisabled
/>
<DataListItemCells
className="data-list-cells"
dataListCells={[
<DataListCell className="name" key="name">
<>{t("realm-settings:name")}</>
</DataListCell>,
<DataListCell className="provider" key="provider">
<>{t("realm-settings:provider")}</>
</DataListCell>,
<DataListCell
className="provider-description"
key="provider-description"
>
<>{t("realm-settings:providerDescription")}</>
</DataListCell>,
]}
/>
</DataListItemRow>
</DataListItem>
{(filteredComponents.length === 0
? components
: filteredComponents
).map((component, idx) => (
<DataListItem
draggable
aria-labelledby={"aria"}
key={component.id}
id={component.id}
>
<DataListItemRow data-testid="data-list-row">
<DataListControl>
<Tooltip content={t("dragInstruction")} position="top">
<DataListDragButton
className="kc-row-drag-button"
aria-label="Reorder"
aria-describedby={t("common-help:dragHelp")}
aria-pressed="false"
/>
</Tooltip>
</DataListControl>
<DataListItemCells
dataListCells={[
<DataListCell
data-testid="provider-name"
key={`name-${idx}`}
>
<Link
key={component.name}
data-testid="provider-name-link"
to={`${url}/${component.id}/${component.providerId}/settings`}
>
{component.name}
</Link>
</DataListCell>,
<DataListCell key={`providerId-${idx}`}>
{component.providerId}
</DataListCell>,
<DataListCell key={`providerDescription-${idx}`}>
{component.providerDescription}
</DataListCell>,
<DataListAction
aria-labelledby="data-list-action"
aria-label="Actions"
isPlainButtonAction
key={`data-action-list-${idx}`}
id={`data-action-list-${idx}`}
>
<Dropdown
isPlain
position={DropdownPosition.right}
isOpen={actionListOpen[idx]}
toggle={
<KebabToggle
data-testid="provider-action"
onToggle={() => {
toggleActionList(idx);
}}
/>
}
dropdownItems={[
<DropdownItem
key="action"
component="button"
data-testid="delete-action"
onClick={() => {
setSelectedComponent(component);
toggleDeleteDialog();
toggleActionList(idx);
}}
>
{t("common:delete")}
</DropdownItem>,
]}
/>
</DataListAction>,
]}
/>
</DataListItemRow>
</DataListItem>
))}
</DataList>
<div className="pf-screen-reader" aria-live="assertive">
{liveText}
</div>
</PageSection>
</>
);
};
type KeysProps = {
realmComponents: ComponentRepresentation[];
keyProviderComponentTypes: ComponentTypeRepresentation[];
refresh: () => void;
};
export const KeysProvidersTab = ({
keyProviderComponentTypes,
realmComponents,
refresh,
...props
}: KeysProps) => {
return (
<KeysTabInner
components={realmComponents.map((component) => {
const provider = keyProviderComponentTypes.find(
(componentType: ComponentTypeRepresentation) =>
component.providerId === componentType.id
);
return {
...component,
providerDescription: provider?.helpText,
};
})}
keyProviderComponentTypes={keyProviderComponentTypes}
refresh={refresh}
realmComponents={realmComponents}
{...props}
/>
);
};

View file

@ -1,286 +0,0 @@
import React, { useState } from "react";
import {
AlertVariant,
Button,
ButtonVariant,
Form,
FormGroup,
Modal,
ModalVariant,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import { useAdminClient } from "../context/auth/AdminClient";
import { useAlerts } from "../components/alert/Alerts";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { KEY_PROVIDER_TYPE } from "../util";
type RSAGeneratedModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const RSAGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: RSAGeneratedModalProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { handleSubmit, control } = useForm({});
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const [isRSAalgDropdownOpen, setIsRSAalgDropdownOpen] = useState(false);
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh();
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
actions={[
<Button
data-testid="add-provider-button"
key="confirm"
variant="primary"
type="submit"
form="add-provider"
>
{t("common:Add")}
</Button>,
<Button
id="modal-cancel"
data-testid="cancel"
key="cancel"
variant={ButtonVariant.link}
onClick={() => {
handleModalToggle!();
}}
>
{t("common:cancel")}
</Button>,
]}
>
<Form
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
onSubmit={handleSubmit(save!)}
>
<FormGroup
label={t("consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
fieldLabelId="realm-settings:loginTheme"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText="realm-settings: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={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={(value) => {
onChange([value + ""]);
}}
/>
)}
/>
</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 }) => (
<Switch
id="kc-active"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={(value) => {
onChange([value + ""]);
}}
/>
)}
/>
</FormGroup>
{providerType === "rsa-generated" && (
<>
<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={["RS256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-algorithm"
onToggle={() =>
setIsRSAalgDropdownOpen(!isRSAalgDropdownOpen)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsRSAalgDropdownOpen(false);
}}
selections={[value + ""]}
variant={SelectVariant.single}
aria-label={t("algorithm")}
isOpen={isRSAalgDropdownOpen}
data-testid="select-rsa-algorithm"
>
{allComponentTypes[5].properties[3].options!.map(
(p, idx) => (
<SelectOption
selected={p === value}
key={`rsa-algorithm-${idx}`}
value={p}
></SelectOption>
)
)}
</Select>
)}
/>
</FormGroup>
<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={["2048"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-keysize"
onToggle={() =>
setIsKeySizeDropdownOpen(!isKeySizeDropdownOpen)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsKeySizeDropdownOpen(false);
}}
selections={[value + ""]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("keySize")}
data-testid="select-secret-size"
>
{allComponentTypes[5].properties[4].options!.map(
(item, idx) => (
<SelectOption
selected={item === value}
key={`rsa-generated-key-size-${idx}`}
value={item}
/>
)
)}
</Select>
)}
/>
</FormGroup>
</>
)}
</Form>
</Modal>
);
};

View file

@ -1,319 +0,0 @@
import React, { useState } from "react";
import {
AlertVariant,
Button,
ButtonVariant,
FileUpload,
Form,
FormGroup,
Modal,
ModalVariant,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import { useAdminClient } from "../context/auth/AdminClient";
import { useAlerts } from "../components/alert/Alerts";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useParams } from "react-router-dom";
import { KEY_PROVIDER_TYPE } from "../util";
type RSAModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const RSAModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: RSAModalProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { handleSubmit, control } = useForm({});
const [isRSAalgDropdownOpen, setIsRSAalgDropdownOpen] = useState(false);
const { id } = useParams<{ id: string }>();
const [keyFileName, setKeyFileName] = useState("");
const [certificateFileName, setCertificateFileName] = useState("");
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
actions={[
<Button
data-testid="add-provider-button"
key="confirm"
variant="primary"
type="submit"
form="add-provider"
>
{t("common:Add")}
</Button>,
<Button
id="modal-cancel"
data-testid="cancel"
key="cancel"
variant={ButtonVariant.link}
onClick={() => {
handleModalToggle!();
}}
>
{t("common:cancel")}
</Button>,
]}
>
<Form
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
onSubmit={handleSubmit(save!)}
>
<FormGroup
label={t("consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
fieldLabelId="realm-settings:consoleDisplayName"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</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={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={(value) => {
onChange([value + ""]);
}}
/>
)}
/>
</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 }) => (
<Switch
id="kc-active"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={(value) => {
onChange([value + ""]);
}}
/>
)}
/>
</FormGroup>
{providerType === "rsa" && (
<>
<FormGroup
label={t("algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
fieldLabelId="realm-settings:algorithm"
/>
}
>
<Controller
name="algorithm"
defaultValue=""
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-algorithm"
onToggle={() =>
setIsRSAalgDropdownOpen(!isRSAalgDropdownOpen)
}
onSelect={(_, value) => {
onChange(value as string);
setIsRSAalgDropdownOpen(false);
}}
selections={[value + ""]}
variant={SelectVariant.single}
aria-label={t("algorithm")}
isOpen={isRSAalgDropdownOpen}
data-testid="select-rsa-algorithm"
>
{allComponentTypes[4].properties[3].options!.map(
(p, idx) => (
<SelectOption
selected={p === value}
key={`rsa-algorithm-${idx}`}
value={p}
></SelectOption>
)
)}
</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[0]}
filenamePlaceholder="Upload a PEM file or paste key below"
filename={keyFileName}
onChange={(value, fileName) => {
setKeyFileName(fileName);
onChange([value]);
}}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("x509Certificate")}
fieldId="kc-aes-keysize"
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[0]}
filenamePlaceholder="Upload a PEM file or paste key below"
filename={certificateFileName}
onChange={(value, fileName) => {
setCertificateFileName(fileName);
onChange([value]);
}}
/>
)}
/>
</FormGroup>
</>
)}
</Form>
</Modal>
);
};

View file

@ -1,63 +1,27 @@
import React, { useState } from "react"; import React, { useState } from "react";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
import { useAdminClient, useFetch } from "../context/auth/AdminClient"; import { useAdminClient, useFetch } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext"; import { useRealm } from "../context/realm-context/RealmContext";
import { KEY_PROVIDER_TYPE } from "../util";
import { RealmSettingsTabs } from "./RealmSettingsTabs"; import { RealmSettingsTabs } from "./RealmSettingsTabs";
const sortByPriority = (components: ComponentRepresentation[]) => {
const sortedComponents = [...components].sort((a, b) => {
const priorityA = Number(a.config?.priority);
const priorityB = Number(b.config?.priority);
return (
(!isNaN(priorityB) ? priorityB : 0) - (!isNaN(priorityA) ? priorityA : 0)
);
});
return sortedComponents;
};
export default function RealmSettingsSection() { export default function RealmSettingsSection() {
const adminClient = useAdminClient(); const adminClient = useAdminClient();
const { realm: realmName } = useRealm(); const { realm: realmName } = useRealm();
const [realm, setRealm] = useState<RealmRepresentation>(); const [realm, setRealm] = useState<RealmRepresentation>();
const [realmComponents, setRealmComponents] =
useState<ComponentRepresentation[]>();
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
const refresh = () => { const refresh = () => {
setKey(key + 1); setKey(key + 1);
}; };
useFetch( useFetch(() => adminClient.realms.findOne({ realm: realmName }), setRealm, [
async () => { key,
const realm = await adminClient.realms.findOne({ realm: realmName }); ]);
const realmComponents = await adminClient.components.find({
type: KEY_PROVIDER_TYPE,
realm: realmName,
});
return { realm, realmComponents }; if (!realm) {
},
({ realm, realmComponents }) => {
setRealmComponents(sortByPriority(realmComponents));
setRealm(realm);
},
[key]
);
if (!realm || !realmComponents) {
return <KeycloakSpinner />; return <KeycloakSpinner />;
} }
return ( return <RealmSettingsTabs realm={realm} refresh={refresh} />;
<RealmSettingsTabs
realm={realm}
refresh={refresh}
realmComponents={realmComponents}
/>
);
} }

View file

@ -13,7 +13,6 @@ import {
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { import {
@ -24,20 +23,16 @@ import { useRealm } from "../context/realm-context/RealmContext";
import { useRealms } from "../context/RealmsContext"; import { useRealms } from "../context/RealmsContext";
import { ViewHeader } from "../components/view-header/ViewHeader"; import { ViewHeader } from "../components/view-header/ViewHeader";
import { useAdminClient } from "../context/auth/AdminClient"; import { useAdminClient } from "../context/auth/AdminClient";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useAlerts } from "../components/alert/Alerts"; import { useAlerts } from "../components/alert/Alerts";
import { import {
convertFormValuesToObject, convertFormValuesToObject,
convertToFormValues, convertToFormValues,
KEY_PROVIDER_TYPE,
toUpperCase, toUpperCase,
} from "../util"; } from "../util";
import { RealmSettingsEmailTab } from "./EmailTab"; import { RealmSettingsEmailTab } from "./EmailTab";
import { EventsTab } from "./event-config/EventsTab"; import { EventsTab } from "./event-config/EventsTab";
import { RealmSettingsGeneralTab } from "./GeneralTab"; import { RealmSettingsGeneralTab } from "./GeneralTab";
import { KeysListTab } from "./KeysListTab";
import { KeysProvidersTab } from "./KeysProvidersTab";
import { RealmSettingsLoginTab } from "./LoginTab"; import { RealmSettingsLoginTab } from "./LoginTab";
import { SecurityDefences } from "./security-defences/SecurityDefences"; import { SecurityDefences } from "./security-defences/SecurityDefences";
import { RealmSettingsSessionsTab } from "./SessionsTab"; import { RealmSettingsSessionsTab } from "./SessionsTab";
@ -57,7 +52,7 @@ import helpUrls from "../help-urls";
import { UserProfileTab } from "./user-profile/UserProfileTab"; import { UserProfileTab } from "./user-profile/UserProfileTab";
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled"; import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
import { ClientPoliciesTab, toClientPolicies } from "./routes/ClientPolicies"; import { ClientPoliciesTab, toClientPolicies } from "./routes/ClientPolicies";
import { KeySubTab, toKeysTab } from "./routes/KeysTab"; import { KeysTab } from "./keys/KeysTab";
type RealmSettingsHeaderProps = { type RealmSettingsHeaderProps = {
onChange: (value: boolean) => void; onChange: (value: boolean) => void;
@ -166,12 +161,10 @@ const RealmSettingsHeader = ({
type RealmSettingsTabsProps = { type RealmSettingsTabsProps = {
realm: RealmRepresentation; realm: RealmRepresentation;
refresh: () => void; refresh: () => void;
realmComponents: ComponentRepresentation[];
}; };
export const RealmSettingsTabs = ({ export const RealmSettingsTabs = ({
realm, realm,
realmComponents,
refresh, refresh,
}: RealmSettingsTabsProps) => { }: RealmSettingsTabsProps) => {
const { t } = useTranslation("realm-settings"); const { t } = useTranslation("realm-settings");
@ -182,9 +175,6 @@ export const RealmSettingsTabs = ({
const history = useHistory(); const history = useHistory();
const isFeatureEnabled = useIsFeatureEnabled(); const isFeatureEnabled = useIsFeatureEnabled();
const kpComponentTypes =
useServerInfo().componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const form = useForm({ mode: "onChange", shouldUnregister: false }); const form = useForm({ mode: "onChange", shouldUnregister: false });
const { control, getValues, setValue, reset: resetForm } = form; const { control, getValues, setValue, reset: resetForm } = form;
@ -247,11 +237,6 @@ export const RealmSettingsTabs = ({
history, history,
}); });
const keysRoute = (tab: KeySubTab) =>
routableTab({
to: toKeysTab({ realm: realmName, tab }),
history,
});
return ( return (
<> <>
<Controller <Controller
@ -321,32 +306,7 @@ export const RealmSettingsTabs = ({
data-testid="rs-keys-tab" data-testid="rs-keys-tab"
{...route("keys")} {...route("keys")}
> >
<RoutableTabs <KeysTab />
defaultLocation={toKeysTab({ realm: realmName, tab: "list" })}
>
<Tab
id="keysList"
data-testid="rs-keys-list-tab"
aria-label="keys-list-subtab"
title={<TabTitleText>{t("keysList")}</TabTitleText>}
{...keysRoute("list")}
>
<KeysListTab realmComponents={realmComponents} />
</Tab>
<Tab
id="providers"
data-testid="rs-providers-tab"
aria-label="rs-providers-tab"
title={<TabTitleText>{t("providers")}</TabTitleText>}
{...keysRoute("providers")}
>
<KeysProvidersTab
realmComponents={realmComponents}
keyProviderComponentTypes={kpComponentTypes}
refresh={refresh}
/>
</Tab>
</RoutableTabs>
</Tab> </Tab>
<Tab <Tab
title={<TabTitleText>{t("events")}</TabTitleText>} title={<TabTitleText>{t("events")}</TabTitleText>}

View file

@ -1,336 +0,0 @@
import React, { useState } from "react";
import {
ActionGroup,
AlertVariant,
Button,
FormGroup,
PageSection,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { useServerInfo } from "../../../context/server-info/ServerInfoProvider";
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
import { useParams, useRouteMatch } from "react-router-dom";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util";
import { useAlerts } from "../../../components/alert/Alerts";
type AESGeneratedFormProps = {
handleModalToggle?: () => void;
refresh?: () => void;
editMode?: boolean;
providerType: string;
};
export interface MatchParams {
providerType: string;
}
export const AESGeneratedForm = ({
editMode,
providerType,
handleModalToggle,
refresh,
}: AESGeneratedFormProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { id } = useParams<{ id: string }>();
const providerId =
useRouteMatch<MatchParams>("/:providerType?")?.params.providerType;
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle?.();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh?.();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
const form = useForm<ComponentRepresentation>({ mode: "onChange" });
const setupForm = (component: ComponentRepresentation) => {
form.reset();
convertToFormValues(component, form.setValue);
};
useFetch(
async () => {
if (editMode) return await adminClient.components.findOne({ id: id });
},
(result) => {
if (result) {
setupForm(result);
}
},
[]
);
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const aesSecretSizeOptions = allComponentTypes[0].properties[3].options;
return (
<FormAccess
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
{editMode && (
<FormGroup
label={t("providerId")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="id"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={form.register()}
id="id"
type="text"
name="id"
isReadOnly={editMode}
aria-label={t("consoleDisplayName")}
defaultValue={id}
data-testid="display-name-input"
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="name"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
{!editMode && (
<Controller
name="name"
control={form.control}
defaultValue={providerType}
render={({ onChange, value }) => {
return (
<TextInput
id="name"
type="text"
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
value={value}
onChange={(value) => onChange(value)}
data-testid="display-name-input"
/>
);
}}
/>
)}
{editMode && (
<TextInput
ref={form.register()}
type="text"
id="name"
name="name"
defaultValue={providerId}
validated={
form.errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
)}
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
fieldLabelId="enabled"
/>
}
>
<Controller
name="config.enabled"
control={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => (
<Switch
id="kc-enabled"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
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={form.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={
value[0] === "true"
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={(value) => {
onChange([value.toString()]);
}}
/>
);
}}
/>
</FormGroup>
<FormGroup
label={t("AESKeySize")}
fieldId="kc-aes-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:AESKeySize"
fieldLabelId="realm-settings:AESKeySize"
/>
}
>
<Controller
name="config.secretSize"
control={form.control}
defaultValue={["16"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-aes-keysize"
onToggle={() => setIsKeySizeDropdownOpen(!isKeySizeDropdownOpen)}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsKeySizeDropdownOpen(false);
}}
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>
<ActionGroup className="kc-AESform-buttons">
<Button
className="kc-AESform-save-button"
data-testid="add-provider-button"
variant="primary"
type="submit"
>
{t("common:save")}
</Button>
<Button
className="kc-AESform-cancel-button"
onClick={(!editMode && handleModalToggle) || undefined}
variant="link"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</FormAccess>
);
};
export default function AESGeneratedSettings() {
const { t } = useTranslation("realm-settings");
const providerId = useRouteMatch<MatchParams>(
"/:realm/realm-settings/keys/:id?/:providerType?/settings"
)?.params.providerType;
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={providerId} />
<PageSection variant="light">
<AESGeneratedForm providerType={providerId!} editMode />
</PageSection>
</>
);
}

View file

@ -1,36 +0,0 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { AESGeneratedForm } from "./AESGeneratedForm";
type AESGeneratedModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const AESGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: AESGeneratedModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
>
<AESGeneratedForm
providerType={providerType}
handleModalToggle={handleModalToggle}
refresh={refresh}
/>
</Modal>
);
};

View file

@ -1,327 +0,0 @@
import React, { useState } from "react";
import {
ActionGroup,
AlertVariant,
Button,
FormGroup,
PageSection,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { useServerInfo } from "../../../context/server-info/ServerInfoProvider";
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
import { useParams, useRouteMatch } from "react-router-dom";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util";
import { useAlerts } from "../../../components/alert/Alerts";
type ECDSAGeneratedFormProps = {
handleModalToggle?: () => void;
refresh?: () => void;
editMode?: boolean;
providerType?: string;
};
export interface MatchParams {
providerType: string;
}
export const ECDSAGeneratedForm = ({
editMode,
providerType,
handleModalToggle,
refresh,
}: ECDSAGeneratedFormProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { id } = useParams<{ id: string }>();
const providerId =
useRouteMatch<MatchParams>("/:providerType?")?.params.providerType;
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle?.();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh?.();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
const form = useForm<ComponentRepresentation>({ mode: "onChange" });
const setupForm = (component: ComponentRepresentation) => {
form.reset();
convertToFormValues(component, form.setValue);
};
useFetch(
async () => {
if (editMode) return await adminClient.components.findOne({ id: id });
},
(result) => {
if (result) {
setupForm(result);
}
},
[]
);
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const ecdsaEllipticCurveOptions = allComponentTypes[1].properties[3].options;
return (
<FormAccess
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
{editMode && (
<FormGroup
label={t("providerId")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="client-scopes:providerId"
/>
}
fieldId="id"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={form.register()}
id="id"
type="text"
name="id"
isReadOnly={editMode}
aria-label={t("consoleDisplayName")}
defaultValue={id}
data-testid="display-name-input"
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="name"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
{!editMode && (
<Controller
name="name"
control={form.control}
defaultValue={providerType}
render={({ onChange, value }) => {
return (
<TextInput
id="name"
type="text"
aria-label={t("consoleDisplayName")}
value={value}
onChange={(value) => onChange(value)}
data-testid="display-name-input"
/>
);
}}
/>
)}
{editMode && (
<TextInput
ref={form.register()}
type="text"
id="name"
name="name"
defaultValue={providerId}
validated={
form.errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
)}
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
fieldLabelId="enabled"
/>
}
>
<Controller
name="config.enabled"
control={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => (
<Switch
id="kc-enabled-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "enabled" : "disabled"}
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={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => {
return (
<Switch
id="kc-active-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "active" : "passive"}
onChange={(value) => {
onChange([value.toString()]);
}}
/>
);
}}
/>
</FormGroup>
<FormGroup
label={t("ellipticCurve")}
fieldId="kc-elliptic-curve"
labelIcon={
<HelpItem
helpText="realm-settings-help:ellipticCurve"
fieldLabelId="realm-settings:ellipticCurve"
/>
}
>
<Controller
name="config.ecdsaEllipticCurveKey"
control={form.control}
defaultValue={["P-256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-ecdsa-elliptic-curve"
onToggle={(isExpanded) => setIsKeySizeDropdownOpen(isExpanded)}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsKeySizeDropdownOpen(false);
}}
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>
<ActionGroup className="kc-ecdsa-form-buttons">
<Button
className="kc-ecdsa-form-save-button"
data-testid="add-provider-button"
variant="primary"
type="submit"
>
{t("common:save")}
</Button>
<Button
className="kc-ecdsa-form-cancel-button"
onClick={(!editMode && handleModalToggle) || undefined}
variant="link"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</FormAccess>
);
};
export default function ECDSAGeneratedSettings() {
const { t } = useTranslation("realm-settings");
const providerId = useRouteMatch<MatchParams>(
"/:realm/realm-settings/keys/:id?/:providerType?/settings"
)?.params.providerType;
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={providerId} />
<PageSection variant="light">
<ECDSAGeneratedForm providerType={providerId} editMode />
</PageSection>
</>
);
}

View file

@ -1,36 +0,0 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { ECDSAGeneratedForm } from "./ECDSAGeneratedForm";
type ECDSAGeneratedModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const ECDSAGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: ECDSAGeneratedModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
>
<ECDSAGeneratedForm
providerType={providerType}
handleModalToggle={handleModalToggle}
refresh={refresh}
/>
</Modal>
);
};

View file

@ -1,374 +0,0 @@
import React, { useState } from "react";
import {
ActionGroup,
AlertVariant,
Button,
FormGroup,
PageSection,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { useServerInfo } from "../../../context/server-info/ServerInfoProvider";
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
import { useParams, useRouteMatch } from "react-router-dom";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util";
import { useAlerts } from "../../../components/alert/Alerts";
type HMACGeneratedFormProps = {
handleModalToggle?: () => void;
refresh?: () => void;
editMode?: boolean;
providerType?: string;
};
export interface MatchParams {
providerType: string;
}
export const HMACGeneratedForm = ({
editMode,
providerType,
handleModalToggle,
refresh,
}: HMACGeneratedFormProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const [isEllipticCurveDropdownOpen, setIsEllipticCurveDropdownOpen] =
useState(false);
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { id } = useParams<{ id: string }>();
const providerId =
useRouteMatch<MatchParams>("/:providerType?")?.params.providerType;
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle?.();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh?.();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
const form = useForm<ComponentRepresentation>({ mode: "onChange" });
const setupForm = (component: ComponentRepresentation) => {
form.reset();
convertToFormValues(component, form.setValue);
};
useFetch(
async () => {
if (editMode) return await adminClient.components.findOne({ id: id });
},
(result) => {
if (result) {
setupForm(result);
}
},
[]
);
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const hmacSecretSizeOptions = allComponentTypes[2].properties[3].options;
const hmacAlgorithmOptions = allComponentTypes[2].properties[4].options;
return (
<FormAccess
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
{editMode && (
<FormGroup
label={t("providerId")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="realm-settings:providerId"
/>
}
fieldId="id"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={form.register()}
id="id"
type="text"
name="id"
isReadOnly={editMode}
aria-label={t("consoleDisplayName")}
defaultValue={id}
data-testid="display-name-input"
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="name"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
{!editMode && (
<Controller
name="name"
control={form.control}
defaultValue={providerType}
render={({ onChange, value }) => {
return (
<TextInput
id="name"
type="text"
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
value={value}
onChange={(value) => onChange(value)}
data-testid="display-name-input"
/>
);
}}
/>
)}
{editMode && (
<TextInput
ref={form.register()}
type="text"
id="name"
name="name"
defaultValue={providerId}
validated={
form.errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
)}
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
fieldLabelId="enabled"
/>
}
>
<Controller
name="config.enabled"
control={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => (
<Switch
id="kc-enabled-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "enabled" : "disabled"}
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={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => {
return (
<Switch
id="kc-active-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "active" : "passive"}
onChange={(value) => {
onChange([value.toString()]);
}}
/>
);
}}
/>
</FormGroup>
<FormGroup
label={t("secretSize")}
fieldId="kc-hmac-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:secretSize"
fieldLabelId="realm-settings:secretSize"
/>
}
>
<Controller
name="config.secretSize"
control={form.control}
defaultValue={["64"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-hmac-keysize"
onToggle={(isExpanded) => setIsKeySizeDropdownOpen(isExpanded)}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsKeySizeDropdownOpen(false);
}}
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={form.control}
defaultValue={["HS-256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={(isExpanded) =>
setIsEllipticCurveDropdownOpen(isExpanded)
}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsEllipticCurveDropdownOpen(false);
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("emailTheme")}
isOpen={isEllipticCurveDropdownOpen}
placeholderText="Select one..."
data-testid="select-email-theme"
>
{hmacAlgorithmOptions!.map((p, idx) => (
<SelectOption
selected={p === value}
key={`email-theme-${idx}`}
value={p}
></SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<ActionGroup className="kc-hmac-form-buttons">
<Button
className="kc-hmac-form-save-button"
data-testid="add-provider-button"
variant="primary"
type="submit"
>
{t("common:save")}
</Button>
<Button
className="kc-hmac-form-cancel-button"
onClick={(!editMode && handleModalToggle) || undefined}
variant="link"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</FormAccess>
);
};
export default function HMACGeneratedSettings() {
const { t } = useTranslation("realm-settings");
const providerId = useRouteMatch<MatchParams>(
"/:realm/realm-settings/keys/:id?/:providerType?/settings"
)?.params.providerType;
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={providerId} />
<PageSection variant="light">
<HMACGeneratedForm providerType={providerId} editMode />
</PageSection>
</>
);
}

View file

@ -1,36 +0,0 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { HMACGeneratedForm } from "./HMACGeneratedForm";
type HMACGeneratedModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const HMACGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: HMACGeneratedModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
>
<HMACGeneratedForm
providerType={providerType}
handleModalToggle={handleModalToggle}
refresh={refresh}
/>
</Modal>
);
};

View file

@ -1,430 +0,0 @@
import React, { useState } from "react";
import {
ActionGroup,
AlertVariant,
Button,
FormGroup,
PageSection,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { useServerInfo } from "../../../context/server-info/ServerInfoProvider";
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
import { useParams, useRouteMatch } from "react-router-dom";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util";
import { useAlerts } from "../../../components/alert/Alerts";
type JavaKeystoreFormProps = {
handleModalToggle?: () => void;
refresh?: () => void;
editMode?: boolean;
providerType?: string;
};
export interface MatchParams {
providerType: string;
}
export const JavaKeystoreForm = ({
editMode,
providerType,
handleModalToggle,
refresh,
}: JavaKeystoreFormProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const [isAlgorithmDropdownOpen, setIsAlgorithmDropdownOpen] = useState(false);
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { id } = useParams<{ id: string }>();
const providerId =
useRouteMatch<MatchParams>("/:providerType?")?.params.providerType;
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle?.();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh?.();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
const form = useForm<ComponentRepresentation>({ mode: "onChange" });
const setupForm = (component: ComponentRepresentation) => {
form.reset();
convertToFormValues(component, form.setValue);
};
useFetch(
async () => {
if (editMode) return await adminClient.components.findOne({ id: id });
},
(result) => {
if (result) {
setupForm(result);
}
},
[]
);
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const javaKeystoreAlgorithmOptions =
allComponentTypes[3].properties[3].options;
return (
<FormAccess
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
{editMode && (
<FormGroup
label={t("providerId")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="realm-settings:providerId"
/>
}
fieldId="id"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={form.register()}
id="id"
type="text"
name="id"
isReadOnly={editMode}
aria-label={t("consoleDisplayName")}
defaultValue={id}
data-testid="display-name-input"
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="name"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
{!editMode && (
<Controller
name="name"
control={form.control}
defaultValue={providerType}
render={({ onChange, value }) => {
return (
<TextInput
id="name"
type="text"
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
value={value}
onChange={(value) => onChange(value)}
data-testid="display-name-input"
/>
);
}}
/>
)}
{editMode && (
<TextInput
ref={form.register()}
type="text"
id="name"
name="name"
defaultValue={providerId}
validated={
form.errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
)}
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
fieldLabelId="realm-settings:enabled"
/>
}
>
<Controller
name="config.enabled"
control={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => (
<Switch
id="kc-enabled-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "enabled" : "disabled"}
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={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => {
return (
<Switch
id="kc-active-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "active" : "passive"}
onChange={(value) => {
onChange([value.toString()]);
}}
/>
);
}}
/>
</FormGroup>
<FormGroup
label={t("algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
fieldLabelId="realm-settings:algorithm"
/>
}
>
<Controller
name="config.algorithm"
control={form.control}
defaultValue={["RS256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={(isExpanded) => setIsAlgorithmDropdownOpen(isExpanded)}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsAlgorithmDropdownOpen(false);
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("algorithm")}
isOpen={isAlgorithmDropdownOpen}
placeholderText="Select one..."
data-testid="select-algorithm"
>
{javaKeystoreAlgorithmOptions!.map((p, idx) => (
<SelectOption
selected={p === value}
key={`algorithm-${idx}`}
value={p}
></SelectOption>
))}
</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={form.control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("keystore")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</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={form.control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</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={form.control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("keyAlias")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="key-alias"
></TextInput>
)}
/>
</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={form.control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("keyPassword")}
onChange={(value) => {
onChange([value.toString()]);
}}
data-testid="key-password"
></TextInput>
)}
/>
</FormGroup>
<ActionGroup className="kc-java-keystore-form-buttons">
<Button
className="kc-java-keystore-form-save-button"
data-testid="add-provider-button"
variant="primary"
type="submit"
>
{t("common:save")}
</Button>
<Button
className="kc-java-keystore-form-cancel-button"
onClick={(!editMode && handleModalToggle) || undefined}
variant="link"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</FormAccess>
);
};
export default function JavaKeystoreSettings() {
const { t } = useTranslation("realm-settings");
const providerId = useRouteMatch<MatchParams>(
"/:realm/realm-settings/keys/:id?/:providerType?/settings"
)?.params.providerType;
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={providerId} />
<PageSection variant="light">
<JavaKeystoreForm providerType={providerId} editMode />
</PageSection>
</>
);
}

View file

@ -1,36 +0,0 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { JavaKeystoreForm } from "./JavaKeystoreForm";
type JavaKeystoreModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const JavaKeystoreModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: JavaKeystoreModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
>
<JavaKeystoreForm
providerType={providerType}
handleModalToggle={handleModalToggle}
refresh={refresh}
/>
</Modal>
);
};

View file

@ -1,26 +0,0 @@
import React from "react";
import { PageSection } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { useRouteMatch } from "react-router-dom";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import {
MatchParams,
RSAGeneratedForm,
} from "../rsa-generated/RSAGeneratedForm";
export default function RSAEncGeneratedSettings() {
const { t } = useTranslation("realm-settings");
const providerId = useRouteMatch<MatchParams>(
"/:realm/realm-settings/keys/:id?/:providerType?/settings"
)?.params.providerType;
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={providerId} />
<PageSection variant="light">
<RSAGeneratedForm providerType={providerId} editMode />
</PageSection>
</>
);
}

View file

@ -1,37 +0,0 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { RSAGeneratedForm } from "../rsa-generated/RSAGeneratedForm";
type RSAEncGeneratedModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const RSAEncGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: RSAEncGeneratedModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
>
<RSAGeneratedForm
isRSAEncGenerated
providerType={providerType}
handleModalToggle={handleModalToggle}
refresh={refresh}
/>
</Modal>
);
};

View file

@ -1,392 +0,0 @@
import React, { useState } from "react";
import {
ActionGroup,
AlertVariant,
Button,
FormGroup,
PageSection,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { useServerInfo } from "../../../context/server-info/ServerInfoProvider";
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
import { useParams, useRouteMatch } from "react-router-dom";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util";
import { useAlerts } from "../../../components/alert/Alerts";
type RSAGeneratedFormProps = {
handleModalToggle?: () => void;
refresh?: () => void;
editMode?: boolean;
providerType?: string;
isRSAEncGenerated?: boolean;
};
export interface MatchParams {
providerType: string;
}
export const RSAGeneratedForm = ({
editMode,
providerType,
handleModalToggle,
refresh,
isRSAEncGenerated,
}: RSAGeneratedFormProps) => {
const { t } = useTranslation("realm-settings");
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const [isEllipticCurveDropdownOpen, setIsEllipticCurveDropdownOpen] =
useState(false);
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { id } = useParams<{ id: string }>();
const { url } = useRouteMatch();
const serverInfo = useServerInfo();
const form = useForm<ComponentRepresentation>({ mode: "onChange" });
const setupForm = (component: ComponentRepresentation) => {
form.reset();
convertToFormValues(component, form.setValue);
};
const providerId =
useRouteMatch<MatchParams>("/:providerType?")?.params.providerType;
const isTypeRSAEncGenerated =
url.includes("rsa-enc-generated") || isRSAEncGenerated;
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 ?? [];
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle?.();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh?.();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
useFetch(
async () => {
if (editMode) return await adminClient.components.findOne({ id: id });
},
(result) => {
if (result) {
setupForm(result);
}
},
[]
);
return (
<FormAccess
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
{editMode && (
<FormGroup
label={t("providerId")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="client-scopes:providerId"
/>
}
fieldId="id"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={form.register()}
id="id"
type="text"
name="id"
isReadOnly={editMode}
aria-label={t("consoleDisplayName")}
defaultValue={id}
data-testid="display-name-input"
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="name"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
{!editMode && (
<Controller
name="name"
control={form.control}
defaultValue={providerType}
render={({ onChange, value }) => (
<TextInput
id="name"
type="text"
aria-label={t("consoleDisplayName")}
value={value}
onChange={(value) => onChange(value)}
data-testid="display-name-input"
/>
)}
/>
)}
{editMode && (
<TextInput
ref={form.register()}
type="text"
id="name"
name="name"
defaultValue={providerId}
validated={
form.errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
)}
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText="realm-settings-help:enabled"
fieldLabelId="enabled"
/>
}
>
<Controller
name="config.enabled"
control={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => (
<Switch
id="kc-enabled-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "enabled" : "disabled"}
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={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => {
return (
<Switch
id="kc-active-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "active" : "passive"}
onChange={(value) => {
onChange([value.toString()]);
}}
/>
);
}}
/>
</FormGroup>
<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={form.control}
defaultValue={isTypeRSAEncGenerated ? ["4096"] : ["2048"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-generated-keysize"
onToggle={(isExpanded) => setIsKeySizeDropdownOpen(isExpanded)}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsKeySizeDropdownOpen(false);
}}
selections={[value.toString()]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("KeySize")}
data-testid="select-secret-size"
>
{(isTypeRSAEncGenerated
? 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={form.control}
defaultValue={isTypeRSAEncGenerated ? ["RSA-OAEP"] : ["RS256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={(isExpanded) =>
setIsEllipticCurveDropdownOpen(isExpanded)
}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsEllipticCurveDropdownOpen(false);
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("emailTheme")}
isOpen={isEllipticCurveDropdownOpen}
placeholderText="Select one..."
data-testid="select-email-theme"
>
{(isTypeRSAEncGenerated
? rsaEncGeneratedAlgorithmOptions
: rsaGeneratedAlgorithmOptions
).map((p, idx) => (
<SelectOption
selected={p === value}
key={`email-theme-${idx}`}
value={p}
></SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<ActionGroup className="kc-rsa-generated-form-buttons">
<Button
className="kc-rsa-generated-form-save-button"
data-testid="add-provider-button"
variant="primary"
type="submit"
>
{t("common:save")}
</Button>
<Button
className="kc-rsa-generated-form-cancel-button"
onClick={(!editMode && handleModalToggle) || undefined}
variant="link"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</FormAccess>
);
};
export default function RSAGeneratedSettings() {
const { t } = useTranslation("realm-settings");
const providerId = useRouteMatch<MatchParams>(
"/:realm/realm-settings/keys/:id?/:providerType?/settings"
)?.params.providerType;
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={providerId} />
<PageSection variant="light">
<RSAGeneratedForm providerType={providerId} editMode />
</PageSection>
</>
);
}

View file

@ -1,36 +0,0 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { RSAGeneratedForm } from "./RSAGeneratedForm";
type RSAGeneratedModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const RSAGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: RSAGeneratedModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
>
<RSAGeneratedForm
providerType={providerType}
handleModalToggle={handleModalToggle}
refresh={refresh}
/>
</Modal>
);
};

View file

@ -1,394 +0,0 @@
import React, { useState } from "react";
import {
ActionGroup,
AlertVariant,
Button,
FileUpload,
FormGroup,
PageSection,
Select,
SelectOption,
SelectVariant,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { useServerInfo } from "../../../context/server-info/ServerInfoProvider";
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
import { useParams, useRouteMatch } from "react-router-dom";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util";
import { useAlerts } from "../../../components/alert/Alerts";
type RSAFormProps = {
handleModalToggle?: () => void;
refresh?: () => void;
editMode?: boolean;
providerType?: string;
};
export interface MatchParams {
providerType: string;
}
export const RSAForm = ({
editMode,
providerType,
handleModalToggle,
refresh,
}: RSAFormProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const [component, setComponent] = useState<ComponentRepresentation>();
const [isRSAalgDropdownOpen, setIsRSAalgDropdownOpen] = useState(false);
const [keyFileName, setKeyFileName] = useState("");
const [certificateFileName, setCertificateFileName] = useState("");
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { id } = useParams<{ id: string }>();
const providerId =
useRouteMatch<MatchParams>("/:providerType?")?.params.providerType;
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
parentId: component.parentId,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { priority: ["0"] },
});
handleModalToggle?.();
addAlert(t("saveProviderSuccess"), AlertVariant.success);
refresh?.();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
const form = useForm<ComponentRepresentation>({ mode: "onChange" });
const setupForm = (component: ComponentRepresentation) => {
form.reset();
convertToFormValues(component, form.setValue);
};
useFetch(
async () => {
if (editMode) return await adminClient.components.findOne({ id: id });
},
(result) => {
if (result) {
setupForm(result);
setComponent(result);
}
},
[]
);
const allComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const rsaAlgOptions = allComponentTypes[4].properties[3].options;
return (
<FormAccess
isHorizontal
id="add-provider"
className="pf-u-mt-lg"
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
{editMode && (
<FormGroup
label={t("providerId")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="realm-settings:providerId"
/>
}
fieldId="id"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={form.register()}
id="id"
type="text"
name="id"
isReadOnly={editMode}
aria-label={t("consoleDisplayName")}
defaultValue={id}
data-testid="display-name-input"
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="name"
isRequired
validated={
form.errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
{!editMode && (
<Controller
name="name"
control={form.control}
defaultValue={providerType}
render={({ onChange, value }) => {
return (
<TextInput
id="name"
type="text"
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
value={value}
onChange={(value) => onChange(value)}
data-testid="display-name-input"
/>
);
}}
/>
)}
{editMode && (
<TextInput
ref={form.register()}
type="text"
id="name"
name="name"
defaultValue={providerId}
validated={
form.errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
)}
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
fieldLabelId="enabled"
/>
}
>
<Controller
name="config.enabled"
control={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => (
<Switch
id="kc-enabled-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "enabled" : "disabled"}
onChange={(value) => {
onChange([value.toString()]);
}}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
fieldLabelId="active"
/>
}
>
<Controller
name="config.active"
control={form.control}
defaultValue={["true"]}
render={({ onChange, value }) => {
return (
<Switch
id="kc-active-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value[0] === "true"}
data-testid={value[0] === "true" ? "active" : "passive"}
onChange={(value) => {
onChange([value.toString()]);
}}
/>
);
}}
/>
</FormGroup>
<FormGroup
label={t("algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
fieldLabelId="realm-settings:algorithm"
/>
}
>
<Controller
name="config.algorithm"
defaultValue={["RS256"]}
control={form.control}
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-algorithm"
onToggle={() => setIsRSAalgDropdownOpen(!isRSAalgDropdownOpen)}
onSelect={(_, value) => {
onChange([value.toString()]);
setIsRSAalgDropdownOpen(false);
}}
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={`rsa-algorithm-${idx}`}
value={p}
></SelectOption>
))}
</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={form.control}
defaultValue={[]}
render={({ onChange }) => (
<FileUpload
id="importPrivateKey"
type="text"
value={component?.config?.privateKey[0]}
filenamePlaceholder="Upload a PEM file or paste key below"
filename={keyFileName}
onChange={(value, fileName) => {
setKeyFileName(fileName);
onChange([value]);
}}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("x509Certificate")}
fieldId="kc-x509Certificate"
labelIcon={
<HelpItem
helpText="realm-settings-help:x509Certificate"
fieldLabelId="realm-settings:x509Certificate"
/>
}
>
<Controller
name="config.certificate"
control={form.control}
defaultValue={[]}
render={({ onChange }) => (
<FileUpload
id="importCertificate"
type="text"
value={component?.config?.certificate[0]}
filenamePlaceholder="Upload a PEM file or paste key below"
filename={certificateFileName}
onChange={(value, fileName) => {
setCertificateFileName(fileName);
onChange([value]);
}}
/>
)}
/>
</FormGroup>
<ActionGroup className="kc-hmac-form-buttons">
<Button
className="kc-hmac-form-save-button"
data-testid="add-provider-button"
variant="primary"
type="submit"
>
{t("common:save")}
</Button>
<Button
className="kc-hmac-form-cancel-button"
onClick={(!editMode && handleModalToggle) || undefined}
variant="link"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</FormAccess>
);
};
export default function RSASettings() {
const { t } = useTranslation("realm-settings");
const providerId = useRouteMatch<MatchParams>(
"/:realm/realm-settings/keys/:id?/:providerType?/settings"
)?.params.providerType;
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={providerId} />
<PageSection variant="light">
<RSAForm providerType={providerId} editMode />
</PageSection>
</>
);
}

View file

@ -1,36 +0,0 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { RSAForm } from "./RSAForm";
type RSAModalProps = {
providerType: string;
handleModalToggle: () => void;
refresh: () => void;
open: boolean;
};
export const RSAModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: RSAModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen={open}
onClose={handleModalToggle}
>
<RSAForm
providerType={providerType}
handleModalToggle={handleModalToggle}
refresh={refresh}
/>
</Modal>
);
};

View file

@ -1,5 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useHistory, useRouteMatch } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
Button, Button,
@ -10,18 +10,21 @@ import {
SelectVariant, SelectVariant,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { cellWidth } from "@patternfly/react-table"; import { cellWidth } from "@patternfly/react-table";
import { FilterIcon } from "@patternfly/react-icons";
import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/keyMetadataRepresentation"; import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/keyMetadataRepresentation";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation"; import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
import { emptyFormatter } from "../util"; import { emptyFormatter } from "../../util";
import { useAdminClient } from "../context/auth/AdminClient"; import { useAdminClient } from "../../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext"; import { useRealm } from "../../context/realm-context/RealmContext";
import { toKeysTab } from "../routes/KeysTab";
import "./realm-settings-section.css"; import "../realm-settings-section.css";
import { FilterIcon } from "@patternfly/react-icons";
const FILTER_OPTIONS = ["ACTIVE", "PASSIVE", "DISABLED"] as const;
type KeyData = KeyMetadataRepresentation & { type KeyData = KeyMetadataRepresentation & {
provider?: string; provider?: string;
@ -32,18 +35,19 @@ type KeysListTabProps = {
}; };
export const KeysListTab = ({ realmComponents }: KeysListTabProps) => { export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
const { t } = useTranslation("roles"); const { t } = useTranslation("realm-settings");
const history = useHistory(); const history = useHistory();
const { url } = useRouteMatch();
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
const [publicKey, setPublicKey] = useState(""); const [publicKey, setPublicKey] = useState("");
const [certificate, setCertificate] = useState(""); const [certificate, setCertificate] = useState("");
const [filterDropdownOpen, setFilterDropdownOpen] = useState(false); const [filterDropdownOpen, setFilterDropdownOpen] = useState(false);
const [filterType, setFilterType] = useState("Active keys"); const [filterType, setFilterType] = useState<typeof FILTER_OPTIONS[number]>(
FILTER_OPTIONS[0]
);
const refresh = () => { const refresh = () => {
setKey(new Date().getTime()); setKey(key + 1);
}; };
const adminClient = useAdminClient(); const adminClient = useAdminClient();
@ -55,8 +59,12 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
}); });
const keys = keysMetaData.keys; const keys = keysMetaData.keys;
const filtered =
filterType !== FILTER_OPTIONS[0]
? keys?.filter(({ status }) => status === filterType)
: keys;
return keys?.map((key) => { return filtered?.map((key) => {
const provider = realmComponents.find( const provider = realmComponents.find(
(component: ComponentRepresentation) => component.id === key.providerId (component: ComponentRepresentation) => component.id === key.providerId
); );
@ -64,56 +72,8 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
})!; })!;
}; };
const activeKeysLoader = async () => {
const keysMetaData = await adminClient.realms.getKeys({
realm: realmName,
});
const keys = keysMetaData.keys;
const activeKeysCopy = keys!.filter((i) => i.status === "ACTIVE");
return activeKeysCopy.map((key) => {
const provider = realmComponents.find(
(component: ComponentRepresentation) => component.id === key.providerId
);
return { ...key, provider: provider?.name } as KeyData;
})!;
};
const passiveKeysLoader = async () => {
const keysMetaData = await adminClient.realms.getKeys({
realm: realmName,
});
const keys = keysMetaData.keys;
const passiveKeys = keys!.filter((i) => i.status === "PASSIVE");
return passiveKeys.map((key) => {
const provider = realmComponents.find(
(component: ComponentRepresentation) => component.id === key.providerId
);
return { ...key, provider: provider?.name } as KeyData;
})!;
};
const disabledKeysLoader = async () => {
const keysMetaData = await adminClient.realms.getKeys({
realm: realmName,
});
const keys = keysMetaData.keys;
const disabledKeys = keys!.filter((i) => i.status === "DISABLED");
return disabledKeys.map((key) => {
const provider = realmComponents!.find(
(component: ComponentRepresentation) => component.id === key.providerId
);
return { ...key, provider: provider?.name } as KeyData;
})!;
};
const [togglePublicKeyDialog, PublicKeyDialog] = useConfirmDialog({ const [togglePublicKeyDialog, PublicKeyDialog] = useConfirmDialog({
titleKey: t("realm-settings:publicKeys").slice(0, -1), titleKey: t("publicKeys").slice(0, -1),
messageKey: publicKey, messageKey: publicKey,
continueButtonLabel: "common:close", continueButtonLabel: "common:close",
continueButtonVariant: ButtonVariant.primary, continueButtonVariant: ButtonVariant.primary,
@ -121,15 +81,13 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
}); });
const [toggleCertificateDialog, CertificateDialog] = useConfirmDialog({ const [toggleCertificateDialog, CertificateDialog] = useConfirmDialog({
titleKey: t("realm-settings:certificate"), titleKey: t("certificate"),
messageKey: certificate, messageKey: certificate,
continueButtonLabel: "common:close", continueButtonLabel: "common:close",
continueButtonVariant: ButtonVariant.primary, continueButtonVariant: ButtonVariant.primary,
onConfirm: () => Promise.resolve(), onConfirm: () => Promise.resolve(),
}); });
const goToCreate = () => history.push(`${url}/add-role`);
const ProviderRenderer = ({ provider }: KeyData) => provider; const ProviderRenderer = ({ provider }: KeyData) => provider;
const ButtonRenderer = ({ type, publicKey, certificate }: KeyData) => { const ButtonRenderer = ({ type, publicKey, certificate }: KeyData) => {
@ -143,7 +101,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
variant="secondary" variant="secondary"
id="kc-public-key" id="kc-public-key"
> >
{t("realm-settings:publicKeys").slice(0, -1)} {t("publicKeys").slice(0, -1)}
</Button> </Button>
); );
} else if (type === "RSA") { } else if (type === "RSA") {
@ -157,7 +115,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
variant="secondary" variant="secondary"
id="kc-rsa-public-key" id="kc-rsa-public-key"
> >
{t("realm-settings:publicKeys").slice(0, -1)} {t("publicKeys").slice(0, -1)}
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
@ -167,32 +125,13 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
variant="secondary" variant="secondary"
id="kc-certificate" id="kc-certificate"
> >
{t("realm-settings:certificate")} {t("certificate")}
</Button> </Button>
</div> </div>
); );
} }
}; };
const options = [
<SelectOption
key={1}
data-testid="active-keys-option"
value={t("realm-settings:activeKeys")}
isPlaceholder
/>,
<SelectOption
data-testid="passive-keys-option"
key={2}
value={t("realm-settings:passiveKeys")}
/>,
<SelectOption
data-testid="disabled-keys-option"
key={3}
value={t("realm-settings:disabledKeys")}
/>,
];
return ( return (
<PageSection variant="light" padding={{ default: "noPadding" }}> <PageSection variant="light" padding={{ default: "noPadding" }}>
<PublicKeyDialog /> <PublicKeyDialog />
@ -200,16 +139,8 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
<KeycloakDataTable <KeycloakDataTable
isNotCompact={true} isNotCompact={true}
key={key} key={key}
loader={ loader={loader}
filterType === "Active keys" ariaLabelKey="keysList"
? activeKeysLoader
: filterType === "Passive keys"
? passiveKeysLoader
: filterType === "Disabled keys"
? disabledKeysLoader
: loader
}
ariaLabelKey="realm-settings:keysList"
searchPlaceholderKey="realm-settings:searchKey" searchPlaceholderKey="realm-settings:searchKey"
searchTypeComponent={ searchTypeComponent={
<Select <Select
@ -221,61 +152,71 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
onToggle={() => setFilterDropdownOpen(!filterDropdownOpen)} onToggle={() => setFilterDropdownOpen(!filterDropdownOpen)}
toggleIcon={<FilterIcon />} toggleIcon={<FilterIcon />}
onSelect={(_, value) => { onSelect={(_, value) => {
setFilterType(value as string); setFilterType(
FILTER_OPTIONS.find((o) => o === value.toString()) ||
FILTER_OPTIONS[0]
);
refresh(); refresh();
setFilterDropdownOpen(false); setFilterDropdownOpen(false);
}} }}
selections={filterType} selections={filterType}
> >
{options} {FILTER_OPTIONS.map((option) => (
<SelectOption
key={option}
data-testid={`${option}-option`}
value={option}
>
{t(`keysFilter.${option}`)}
</SelectOption>
))}
</Select> </Select>
} }
canSelectAll canSelectAll
columns={[ columns={[
{ {
name: "algorithm", name: "algorithm",
displayKey: "realm-settings:algorithm", displayKey: "algorithm",
cellFormatters: [emptyFormatter()], cellFormatters: [emptyFormatter()],
transforms: [cellWidth(15)], transforms: [cellWidth(15)],
}, },
{ {
name: "type", name: "type",
displayKey: "realm-settings:type", displayKey: "type",
cellFormatters: [emptyFormatter()], cellFormatters: [emptyFormatter()],
transforms: [cellWidth(10)], transforms: [cellWidth(10)],
}, },
{ {
name: "kid", name: "kid",
displayKey: "realm-settings:kid", displayKey: "kid",
cellFormatters: [emptyFormatter()], cellFormatters: [emptyFormatter()],
transforms: [cellWidth(10)], transforms: [cellWidth(10)],
}, },
{ {
name: "provider", name: "provider",
displayKey: "realm-settings:provider", displayKey: "provider",
cellRenderer: ProviderRenderer, cellRenderer: ProviderRenderer,
cellFormatters: [emptyFormatter()], cellFormatters: [emptyFormatter()],
transforms: [cellWidth(10)], transforms: [cellWidth(10)],
}, },
{ {
name: "publicKeys", name: "publicKeys",
displayKey: "realm-settings:publicKeys", displayKey: "publicKeys",
cellRenderer: ButtonRenderer, cellRenderer: ButtonRenderer,
cellFormatters: [], cellFormatters: [],
transforms: [cellWidth(20)], transforms: [cellWidth(20)],
}, },
]} ]}
isSearching={!!filterType} isSearching={filterType !== FILTER_OPTIONS[0]}
emptyState={ emptyState={
<ListEmptyState <ListEmptyState
hasIcon={true} hasIcon
message={t("realm-settings:noKeys")} message={t("noKeys")}
instructions={ instructions={t("noKeysDescription")}
t(`realm-settings:noKeysDescription`) + primaryActionText={t("addProvider")}
`${filterType.toLocaleLowerCase()}.` onPrimaryAction={() =>
history.push(toKeysTab({ realm: realmName, tab: "providers" }))
} }
primaryActionText={t("createRole")}
onPrimaryAction={goToCreate}
/> />
} }
/> />

View file

@ -0,0 +1,283 @@
import React, { useMemo, useState, KeyboardEvent } from "react";
import { useTranslation } from "react-i18next";
import {
AlertVariant,
Button,
ButtonVariant,
Dropdown,
DropdownItem,
DropdownToggle,
InputGroup,
PageSection,
TextInput,
Toolbar,
ToolbarGroup,
ToolbarItem,
} from "@patternfly/react-core";
import { SearchIcon } from "@patternfly/react-icons";
import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/keyMetadataRepresentation";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import type ComponentTypeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentTypeRepresentation";
import type { ProviderType } from "../routes/KeyProvider";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
import { useAdminClient } from "../../context/auth/AdminClient";
import { useAlerts } from "../../components/alert/Alerts";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
import { useRealm } from "../../context/realm-context/RealmContext";
import { Link, useRouteMatch } from "react-router-dom";
import { KEY_PROVIDER_TYPE } from "../../util";
import { DraggableTable } from "../../authentication/components/DraggableTable";
import { KeyProviderModal } from "./key-providers/KeyProviderModal";
import useToggle from "../../utils/useToggle";
import "../realm-settings-section.css";
type ComponentData = KeyMetadataRepresentation & {
id?: string;
providerDescription?: string;
name?: string;
toggleHidden?: boolean;
config?: any;
parentId?: string;
};
type KeysProvidersTabProps = {
realmComponents: ComponentRepresentation[];
refresh: () => void;
};
export const KeysProvidersTab = ({
realmComponents,
refresh,
}: KeysProvidersTabProps) => {
const { t } = useTranslation("realm-settings");
const { addAlert, addError } = useAlerts();
const adminClient = useAdminClient();
const { realm } = useRealm();
const { url } = useRouteMatch();
const [searchVal, setSearchVal] = useState("");
const [filteredComponents, setFilteredComponents] = useState<ComponentData[]>(
[]
);
const [isCreateModalOpen, handleModalToggle] = useToggle();
const serverInfo = useServerInfo();
const keyProviderComponentTypes =
serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [];
const providerTypes = keyProviderComponentTypes.map((item) => item.id);
const [providerDropdownOpen, setProviderDropdownOpen] = useState(false);
const [defaultConsoleDisplayName, setDefaultConsoleDisplayName] =
useState<ProviderType>();
const [selectedComponent, setSelectedComponent] =
useState<ComponentRepresentation>();
const components = useMemo(
() =>
realmComponents.map((component) => {
const provider = keyProviderComponentTypes.find(
(componentType: ComponentTypeRepresentation) =>
component.providerId === componentType.id
);
return {
...component,
providerDescription: provider?.helpText,
};
}),
[realmComponents]
);
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: "realm-settings:deleteProviderTitle",
messageKey: t("deleteProviderConfirm", {
provider: selectedComponent?.name,
}),
continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
await adminClient.components.del({
id: selectedComponent!.id!,
realm: realm,
});
refresh();
addAlert(t("deleteProviderSuccess"), AlertVariant.success);
} catch (error) {
addError("realm-settings:deleteProviderError", error);
}
},
});
const onSearch = () => {
if (searchVal !== "") {
setSearchVal(searchVal);
const filteredComponents = components.filter(
(component) =>
component.name?.includes(searchVal) ||
component.providerId?.includes(searchVal)
);
setFilteredComponents(filteredComponents);
} else {
setSearchVal("");
setFilteredComponents(components);
}
};
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
onSearch();
}
};
const handleInputChange = (value: string) => {
setSearchVal(value);
};
return (
<>
{isCreateModalOpen && defaultConsoleDisplayName && (
<KeyProviderModal
providerType={defaultConsoleDisplayName}
onClose={() => {
handleModalToggle();
refresh();
}}
/>
)}
<DeleteConfirm />
<PageSection variant="light" padding={{ default: "noPadding" }}>
<Toolbar>
<ToolbarGroup className="providers-toolbar">
<ToolbarItem>
<InputGroup>
<TextInput
name={"inputGroupName"}
id={"inputGroupName"}
type="search"
aria-label={t("common:search")}
placeholder={t("common:search")}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
/>
<Button
variant={ButtonVariant.control}
aria-label={t("common:search")}
onClick={onSearch}
>
<SearchIcon />
</Button>
</InputGroup>
</ToolbarItem>
<ToolbarItem>
<Dropdown
data-testid="addProviderDropdown"
className="add-provider-dropdown"
isOpen={providerDropdownOpen}
toggle={
<DropdownToggle
onToggle={(val) => setProviderDropdownOpen(val)}
isPrimary
>
{t("addProvider")}
</DropdownToggle>
}
dropdownItems={[
providerTypes.map((item) => (
<DropdownItem
onClick={() => {
handleModalToggle();
setProviderDropdownOpen(false);
setDefaultConsoleDisplayName(item as ProviderType);
}}
data-testid={`option-${item}`}
key={item}
>
{item}
</DropdownItem>
)),
]}
/>
</ToolbarItem>
</ToolbarGroup>
</Toolbar>
<DraggableTable
variant="compact"
keyField="id"
data={
filteredComponents.length === 0 ? components : filteredComponents
}
onDragFinish={async (_, itemOrder) => {
const updateAll = components.map((component: ComponentData) => {
const componentToSave = { ...component };
delete componentToSave.providerDescription;
return adminClient.components.update(
{ id: component.id! },
{
...componentToSave,
config: {
priority: [
(
itemOrder.length -
itemOrder.indexOf(component.id!) +
100
).toString(),
],
},
}
);
});
try {
await Promise.all(updateAll);
refresh();
addAlert(t("saveProviderListSuccess"), AlertVariant.success);
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
}}
columns={[
{
name: "name",
displayKey: "realm-settings:name",
cellRenderer: (component) => (
<Link
key={component.name}
data-testid="provider-name-link"
to={`${url}/${component.id}/${component.providerId}/settings`}
>
{component.name}
</Link>
),
},
{
name: "providerId",
displayKey: "realm-settings:provider",
},
{
name: "providerDescription",
displayKey: "realm-settings:providerDescription",
},
]}
actions={[
{
title: t("common:delete"),
onClick: (_key, _idx, component) => {
setSelectedComponent(component as ComponentRepresentation);
toggleDeleteDialog();
},
},
]}
/>
</PageSection>
</>
);
};

View file

@ -0,0 +1,92 @@
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Tab, TabTitleText } from "@patternfly/react-core";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import { KEY_PROVIDER_TYPE } from "../../util";
import {
routableTab,
RoutableTabs,
} from "../../components/routable-tabs/RoutableTabs";
import { KeySubTab, toKeysTab } from "../routes/KeysTab";
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
import { KeysListTab } from "./KeysListTab";
import { KeysProvidersTab } from "./KeysProvidersTab";
const sortByPriority = (components: ComponentRepresentation[]) => {
const sortedComponents = [...components].sort((a, b) => {
const priorityA = Number(a.config?.priority);
const priorityB = Number(b.config?.priority);
return (
(!isNaN(priorityB) ? priorityB : 0) - (!isNaN(priorityA) ? priorityA : 0)
);
});
return sortedComponents;
};
export const KeysTab = () => {
const { t } = useTranslation("realm-settings");
const history = useHistory();
const adminClient = useAdminClient();
const { realm: realmName } = useRealm();
const [realmComponents, setRealmComponents] =
useState<ComponentRepresentation[]>();
const [key, setKey] = useState(0);
const refresh = () => {
setKey(key + 1);
};
useFetch(
() =>
adminClient.components.find({
type: KEY_PROVIDER_TYPE,
realm: realmName,
}),
(components) => setRealmComponents(sortByPriority(components)),
[key]
);
if (!realmComponents) {
return <KeycloakSpinner />;
}
const keysRoute = (tab: KeySubTab) =>
routableTab({
to: toKeysTab({ realm: realmName, tab }),
history,
});
return (
<RoutableTabs
mountOnEnter
unmountOnExit
defaultLocation={toKeysTab({ realm: realmName, tab: "list" })}
>
<Tab
id="keysList"
data-testid="rs-keys-list-tab"
aria-label="keys-list-subtab"
title={<TabTitleText>{t("keysList")}</TabTitleText>}
{...keysRoute("list")}
>
<KeysListTab realmComponents={realmComponents} />
</Tab>
<Tab
id="providers"
data-testid="rs-providers-tab"
aria-label="rs-providers-tab"
title={<TabTitleText>{t("providers")}</TabTitleText>}
{...keysRoute("providers")}
>
<KeysProvidersTab realmComponents={realmComponents} refresh={refresh} />
</Tab>
</RoutableTabs>
);
};

View file

@ -0,0 +1,257 @@
import React from "react";
import { useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Controller, FormProvider, useForm } from "react-hook-form";
import {
AlertVariant,
FormGroup,
ValidatedOptions,
Switch,
TextInput,
PageSection,
ActionGroup,
Button,
} from "@patternfly/react-core";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import type { KeyProviderParams, ProviderType } from "../../routes/KeyProvider";
import { useAlerts } from "../../../components/alert/Alerts";
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
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";
type KeyProviderFormProps = {
id?: string;
providerType: ProviderType;
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-generated":
return <RsaGeneratedView isEnc />;
case "rsa-generated":
return <RsaGeneratedView />;
default:
return <>invalid view type</>;
}
};
export const KeyProviderForm = ({
providerType,
onClose,
}: KeyProviderFormProps) => {
const { t } = useTranslation("realm-settings");
const { id } = useParams<{ id: string }>();
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const form = useForm<ComponentRepresentation>({
shouldUnregister: false,
mode: "onChange",
});
const { register, control, handleSubmit, errors, reset } = form;
const save = async (component: ComponentRepresentation) => {
try {
if (id) {
await adminClient.components.update(
{ id },
{
...component,
providerType: KEY_PROVIDER_TYPE,
}
);
addAlert(t("saveProviderSuccess"), AlertVariant.success);
} else {
await adminClient.components.create({
...component,
providerId: providerType,
providerType: KEY_PROVIDER_TYPE,
config: { ...component.config, priority: ["0"] },
});
addAlert(t("saveProviderSuccess"), AlertVariant.success);
onClose?.();
}
} catch (error) {
addError("realm-settings:saveProviderError", error);
}
};
useFetch(
async () => {
if (id) return await adminClient.components.findOne({ id });
},
(result) => {
if (result) {
reset({ ...result });
}
},
[]
);
return (
<FormAccess isHorizontal role="manage-realm" onSubmit={handleSubmit(save)}>
{id && (
<FormGroup
label={t("providerId")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="providerId"
/>
}
fieldId="providerId"
isRequired
>
<TextInput
ref={register}
id="id"
type="text"
name="id"
isReadOnly
aria-label={t("providerId")}
data-testid="providerId-input"
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
fieldLabelId="name"
/>
}
fieldId="name"
isRequired
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<Controller
name="name"
control={control}
rules={{ required: true }}
defaultValue={providerType}
render={({ onChange, value }) => (
<TextInput
id="name"
type="text"
aria-label={t("common:name")}
value={value}
onChange={onChange}
data-testid="name-input"
/>
)}
/>
</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} />
</FormProvider>
<ActionGroup>
<Button
data-testid="add-provider-button"
variant="primary"
type="submit"
>
{t("common:save")}
</Button>
<Button onClick={() => onClose?.()} variant="link">
{t("common:cancel")}
</Button>
</ActionGroup>
</FormAccess>
);
};
export default function KeyProviderFormPage() {
const { t } = useTranslation("realm-settings");
const params = useParams<KeyProviderParams>();
return (
<>
<ViewHeader titleKey={t("editProvider")} subKey={params.providerType} />
<PageSection variant="light">
<KeyProviderForm {...params} />
</PageSection>
</>
);
}

View file

@ -0,0 +1,28 @@
import React from "react";
import { Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { KeyProviderForm } from "./KeyProviderForm";
import type { ProviderType } from "../../routes/KeyProvider";
type KeyProviderModalProps = {
providerType: ProviderType;
onClose: () => void;
};
export const KeyProviderModal = ({
providerType,
onClose,
}: KeyProviderModalProps) => {
const { t } = useTranslation("realm-settings");
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("addProvider")}
isOpen
onClose={onClose}
>
<KeyProviderForm providerType={providerType} onClose={onClose} />
</Modal>
);
};

View file

@ -0,0 +1,64 @@
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

@ -0,0 +1,64 @@
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

@ -0,0 +1,109 @@
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

@ -0,0 +1,167 @@
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

@ -0,0 +1,123 @@
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

@ -0,0 +1,127 @@
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() {
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[4].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"
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}
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}
onChange={(value, filename) => {
onChange(value);
setCertificate(filename);
}}
filenamePlaceholder={t("filenamePlaceholder")}
/>
)}
/>
</FormGroup>
</>
);
}

View file

@ -10,7 +10,7 @@ export default {
"If you delete this realm, all associated data will be removed.", "If you delete this realm, all associated data will be removed.",
deleteProviderTitle: "Delete key provider?", deleteProviderTitle: "Delete key provider?",
deleteProviderConfirm: deleteProviderConfirm:
"Are you sure you want to permanently delete the key provider ", "Are you sure you want to permanently delete the key provider {{provider}}?",
deleteProviderSuccess: "Success. The provider has been deleted.", deleteProviderSuccess: "Success. The provider has been deleted.",
deleteProviderError: "Error deleting the provider", deleteProviderError: "Error deleting the provider",
deletedSuccess: "The realm has been deleted", deletedSuccess: "The realm has been deleted",
@ -84,6 +84,7 @@ export default {
AESKeySize: "AES Key Size", AESKeySize: "AES Key Size",
active: "Active", active: "Active",
privateRSAKey: "Private RSA Key", privateRSAKey: "Private RSA Key",
filenamePlaceholder: "Upload a PEM file or paste key below",
x509Certificate: "X509 Certificate", x509Certificate: "X509 Certificate",
ellipticCurve: "Elliptic Curve", ellipticCurve: "Elliptic Curve",
secretSize: "Secret size", secretSize: "Secret size",
@ -96,11 +97,13 @@ export default {
providerDescription: "Provider description", providerDescription: "Provider description",
addProvider: "Add provider", addProvider: "Add provider",
publicKeys: "Public keys", publicKeys: "Public keys",
activeKeys: "Active keys", keysFilter: {
passiveKeys: "Passive keys", ACTIVE: "Active keys",
disabledKeys: "Disabled keys", PASSIVE: "Passive keys",
DISABLED: "Disabled keys",
},
noKeys: "No keys", noKeys: "No keys",
noKeysDescription: "You haven't created any ", noKeysDescription: "You haven't created any active keys",
certificate: "Certificate", certificate: "Certificate",
loginScreenCustomization: "Login screen customization", loginScreenCustomization: "Login screen customization",
userRegistration: "User registration", userRegistration: "User registration",

View file

@ -1,11 +1,6 @@
import type { RouteDef } from "../route-config"; import type { RouteDef } from "../route-config";
import { AesGeneratedSettingsRoute } from "./routes/AesGeneratedSettings"; import { KeyProviderFormRoute } from "./routes/KeyProvider";
import { EcdsaGeneratedSettingsRoute } from "./routes/EcdsaGeneratedSettings";
import { HmacGeneratedSettingsRoute } from "./routes/HmacGeneratedSettings";
import { JavaKeystoreSettingsRoute } from "./routes/JavaKeystoreSettings";
import { RealmSettingsRoute } from "./routes/RealmSettings"; import { RealmSettingsRoute } from "./routes/RealmSettings";
import { RsaGeneratedSettingsRoute } from "./routes/RsaGeneratedSettings";
import { RsaSettingsRoute } from "./routes/RsaSettings";
import { ClientPoliciesRoute } from "./routes/ClientPolicies"; import { ClientPoliciesRoute } from "./routes/ClientPolicies";
import { AddClientProfileRoute } from "./routes/AddClientProfile"; import { AddClientProfileRoute } from "./routes/AddClientProfile";
import { ClientProfileRoute } from "./routes/ClientProfile"; import { ClientProfileRoute } from "./routes/ClientProfile";
@ -18,18 +13,11 @@ import { EditClientPolicyConditionRoute } from "./routes/EditCondition";
import { UserProfileRoute } from "./routes/UserProfile"; import { UserProfileRoute } from "./routes/UserProfile";
import { AddAttributeRoute } from "./routes/AddAttribute"; import { AddAttributeRoute } from "./routes/AddAttribute";
import { KeysRoute } from "./routes/KeysTab"; import { KeysRoute } from "./routes/KeysTab";
import { RsaEncGeneratedSettingsRoute } from "./routes/RsaEncGeneratedSettings";
const routes: RouteDef[] = [ const routes: RouteDef[] = [
RealmSettingsRoute, RealmSettingsRoute,
KeysRoute, KeysRoute,
AesGeneratedSettingsRoute, KeyProviderFormRoute,
EcdsaGeneratedSettingsRoute,
HmacGeneratedSettingsRoute,
JavaKeystoreSettingsRoute,
RsaEncGeneratedSettingsRoute,
RsaGeneratedSettingsRoute,
RsaSettingsRoute,
ClientPoliciesRoute, ClientPoliciesRoute,
AddClientProfileRoute, AddClientProfileRoute,
AddExecutorRoute, AddExecutorRoute,

View file

@ -1,24 +0,0 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type KeyProviderParams = {
realm: string;
id: string;
};
export const AesGeneratedSettingsRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/aes-generated/settings",
component: lazy(
() => import("../key-providers/aes-generated/AESGeneratedForm")
),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toAesGeneratedSettings = (
params: KeyProviderParams
): LocationDescriptorObject => ({
pathname: generatePath(AesGeneratedSettingsRoute.path, params),
});

View file

@ -1,24 +0,0 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type EcdsaGeneratedSettingsParams = {
realm: string;
id: string;
};
export const EcdsaGeneratedSettingsRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/ecdsa-generated/settings",
component: lazy(
() => import("../key-providers/ecdsa-generated/ECDSAGeneratedForm")
),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toEcdsaGeneratedSettings = (
params: EcdsaGeneratedSettingsParams
): LocationDescriptorObject => ({
pathname: generatePath(EcdsaGeneratedSettingsRoute.path, params),
});

View file

@ -1,24 +0,0 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type HmacGeneratedSettingsParams = {
realm: string;
id: string;
};
export const HmacGeneratedSettingsRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/hmac-generated/settings",
component: lazy(
() => import("../key-providers/hmac-generated/HMACGeneratedForm")
),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toHmacGeneratedSettings = (
params: HmacGeneratedSettingsParams
): LocationDescriptorObject => ({
pathname: generatePath(HmacGeneratedSettingsRoute.path, params),
});

View file

@ -1,24 +0,0 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type JavaKeystoreSettingsParams = {
realm: string;
id: string;
};
export const JavaKeystoreSettingsRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/java-keystore/settings",
component: lazy(
() => import("../key-providers/java-keystore/JavaKeystoreForm")
),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toJavaKeystoreSettings = (
params: JavaKeystoreSettingsParams
): LocationDescriptorObject => ({
pathname: generatePath(JavaKeystoreSettingsRoute.path, params),
});

View file

@ -0,0 +1,32 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type ProviderType =
| "aes-generated"
| "ecdsa-generated"
| "hmac-generated"
| "java-keystore"
| "rsa"
| "rsa-enc-generated"
| "rsa-generated";
export type KeyProviderParams = {
id: string;
providerType: ProviderType;
realm: string;
};
export const KeyProviderFormRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/:providerType/settings",
component: lazy(() => import("../keys/key-providers/KeyProviderForm")),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toKeyProvider = (
params: KeyProviderParams
): LocationDescriptorObject => ({
pathname: generatePath(KeyProviderFormRoute.path, params),
});

View file

@ -1,24 +0,0 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type RsaGeneratedSettingsParams = {
realm: string;
id: string;
};
export const RsaEncGeneratedSettingsRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/rsa-enc-generated/settings",
component: lazy(
() => import("../key-providers/rsa-generated/RSAGeneratedForm")
),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toRsaEncGeneratedSettings = (
params: RsaGeneratedSettingsParams
): LocationDescriptorObject => ({
pathname: generatePath(RsaEncGeneratedSettingsRoute.path, params),
});

View file

@ -1,24 +0,0 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type RsaGeneratedSettingsParams = {
realm: string;
id: string;
};
export const RsaGeneratedSettingsRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/rsa-generated/settings",
component: lazy(
() => import("../key-providers/rsa-generated/RSAGeneratedForm")
),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toRsaGeneratedSettings = (
params: RsaGeneratedSettingsParams
): LocationDescriptorObject => ({
pathname: generatePath(RsaGeneratedSettingsRoute.path, params),
});

View file

@ -1,22 +0,0 @@
import type { LocationDescriptorObject } from "history";
import { lazy } from "react";
import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
export type RsaSettingsParams = {
realm: string;
id: string;
};
export const RsaSettingsRoute: RouteDef = {
path: "/:realm/realm-settings/keys/providers/:id/rsa/settings",
component: lazy(() => import("../key-providers/rsa/RSAForm")),
breadcrumb: (t) => t("realm-settings:editProvider"),
access: "view-realm",
};
export const toRsaSettings = (
params: RsaSettingsParams
): LocationDescriptorObject => ({
pathname: generatePath(RsaSettingsRoute.path, params),
});