refacored the keys tab removed duplication (#2146)
This commit is contained in:
parent
6e0414c557
commit
5c64ab6a4a
46 changed files with 1462 additions and 4351 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>}
|
||||||
|
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
283
src/realm-settings/keys/KeysProvidersTab.tsx
Normal file
283
src/realm-settings/keys/KeysProvidersTab.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
92
src/realm-settings/keys/KeysTab.tsx
Normal file
92
src/realm-settings/keys/KeysTab.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
257
src/realm-settings/keys/key-providers/KeyProviderForm.tsx
Normal file
257
src/realm-settings/keys/key-providers/KeyProviderForm.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
28
src/realm-settings/keys/key-providers/KeyProviderModal.tsx
Normal file
28
src/realm-settings/keys/key-providers/KeyProviderModal.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
64
src/realm-settings/keys/key-providers/aes-generated/View.tsx
Normal file
64
src/realm-settings/keys/key-providers/aes-generated/View.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
109
src/realm-settings/keys/key-providers/hmac-generated/View.tsx
Normal file
109
src/realm-settings/keys/key-providers/hmac-generated/View.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
167
src/realm-settings/keys/key-providers/java-keystore/View.tsx
Normal file
167
src/realm-settings/keys/key-providers/java-keystore/View.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
123
src/realm-settings/keys/key-providers/rsa-generated/View.tsx
Normal file
123
src/realm-settings/keys/key-providers/rsa-generated/View.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
127
src/realm-settings/keys/key-providers/rsa/View.tsx
Normal file
127
src/realm-settings/keys/key-providers/rsa/View.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
|
||||||
});
|
|
|
@ -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),
|
|
||||||
});
|
|
|
@ -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),
|
|
||||||
});
|
|
|
@ -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),
|
|
||||||
});
|
|
32
src/realm-settings/routes/KeyProvider.ts
Normal file
32
src/realm-settings/routes/KeyProvider.ts
Normal 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),
|
||||||
|
});
|
|
@ -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),
|
|
||||||
});
|
|
|
@ -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),
|
|
||||||
});
|
|
|
@ -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),
|
|
||||||
});
|
|
Loading…
Reference in a new issue