Realm settings(keys): toggle provider modal for all provider types (#642)

* changes after rebase

* add cypress tests

* addProviderModal done

* fix lint

* clean up ids/fieldids

* divide modals into separate components

* format

* cypress

* fix realm settings test

* clean up cypress tests

* try remove after all hook
This commit is contained in:
Jenny 2021-06-03 14:31:47 -04:00 committed by GitHub
parent 04140f1737
commit 2746109e78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 2201 additions and 57 deletions

View file

@ -4,68 +4,115 @@ import RealmSettingsPage from "../support/pages/admin_console/manage/realm_setti
import { keycloakBefore } from "../support/util/keycloak_before";
import AdminClient from "../support/util/AdminClient";
describe("Realm settings test", () => {
const loginPage = new LoginPage();
const sidebarPage = new SidebarPage();
const realmSettingsPage = new RealmSettingsPage();
// describe("Realm settings test", () => {
const loginPage = new LoginPage();
const sidebarPage = new SidebarPage();
const realmSettingsPage = new RealmSettingsPage();
describe("Realm settings", () => {
const realmName = "Realm_" + (Math.random() + 1).toString(36).substring(7);
describe("Realm settings", () => {
const realmName = "Realm_" + (Math.random() + 1).toString(36).substring(7);
beforeEach(() => {
keycloakBefore();
loginPage.logIn();
sidebarPage.goToRealm(realmName);
});
beforeEach(() => {
keycloakBefore();
loginPage.logIn();
sidebarPage.goToRealm(realmName);
});
before(async () => {
await new AdminClient().createRealm(realmName);
});
before(async () => {
await new AdminClient().createRealm(realmName);
});
after(async () => {
await new AdminClient().deleteRealm(realmName);
});
// after(async () => {
// await new AdminClient().deleteRealm(realmName);
// });
it("Go to general tab", function () {
sidebarPage.goToRealmSettings();
realmSettingsPage.toggleSwitch(realmSettingsPage.managedAccessSwitch);
realmSettingsPage.save(realmSettingsPage.generalSaveBtn);
realmSettingsPage.toggleSwitch(realmSettingsPage.managedAccessSwitch);
realmSettingsPage.save(realmSettingsPage.generalSaveBtn);
});
it("Go to general tab", function () {
sidebarPage.goToRealmSettings();
realmSettingsPage.toggleSwitch(realmSettingsPage.managedAccessSwitch);
realmSettingsPage.save(realmSettingsPage.generalSaveBtn);
realmSettingsPage.toggleSwitch(realmSettingsPage.managedAccessSwitch);
realmSettingsPage.save(realmSettingsPage.generalSaveBtn);
});
it("Go to login tab", function () {
sidebarPage.goToRealmSettings();
cy.getId("rs-login-tab").click();
realmSettingsPage.toggleSwitch(realmSettingsPage.userRegSwitch);
realmSettingsPage.toggleSwitch(realmSettingsPage.forgotPwdSwitch);
realmSettingsPage.toggleSwitch(realmSettingsPage.rememberMeSwitch);
realmSettingsPage.toggleSwitch(realmSettingsPage.verifyEmailSwitch);
});
it("Go to login tab", function () {
sidebarPage.goToRealmSettings();
cy.getId("rs-login-tab").click();
realmSettingsPage.toggleSwitch(realmSettingsPage.userRegSwitch);
realmSettingsPage.toggleSwitch(realmSettingsPage.forgotPwdSwitch);
realmSettingsPage.toggleSwitch(realmSettingsPage.rememberMeSwitch);
realmSettingsPage.toggleSwitch(realmSettingsPage.verifyEmailSwitch);
});
it("Go to email tab", function () {
sidebarPage.goToRealmSettings();
cy.getId("rs-email-tab").click();
it("Go to email tab", function () {
sidebarPage.goToRealmSettings();
cy.getId("rs-email-tab").click();
realmSettingsPage.addSenderEmail("example@example.com");
cy.wait(1000);
cy.wait(100);
realmSettingsPage.addSenderEmail("example@example.com");
realmSettingsPage.toggleCheck(realmSettingsPage.enableSslCheck);
realmSettingsPage.toggleCheck(realmSettingsPage.enableStartTlsCheck);
realmSettingsPage.toggleCheck(realmSettingsPage.enableSslCheck);
realmSettingsPage.toggleCheck(realmSettingsPage.enableStartTlsCheck);
realmSettingsPage.save(realmSettingsPage.emailSaveBtn);
});
realmSettingsPage.save(realmSettingsPage.emailSaveBtn);
});
it("Go to themes tab", function () {
sidebarPage.goToRealmSettings();
cy.getId("rs-themes-tab").click();
realmSettingsPage.selectLoginThemeType("keycloak");
realmSettingsPage.selectAccountThemeType("keycloak");
realmSettingsPage.selectAdminThemeType("base");
realmSettingsPage.selectEmailThemeType("base");
it("Go to themes tab", function () {
cy.wait(5000);
sidebarPage.goToRealmSettings();
cy.getId("rs-themes-tab").click();
realmSettingsPage.selectLoginThemeType("keycloak");
realmSettingsPage.selectAccountThemeType("keycloak");
realmSettingsPage.selectAdminThemeType("base");
realmSettingsPage.selectEmailThemeType("base");
realmSettingsPage.saveThemes();
});
realmSettingsPage.saveThemes();
});
it("Go to keys tab", function () {
cy.wait(5000);
sidebarPage.goToRealmSettings();
cy.getId("rs-keys-tab").click();
});
it("add Providers", function () {
cy.wait(5000);
sidebarPage.goToRealmSettings();
cy.getId("rs-keys-tab").click();
cy.wait(10000);
cy.getId("rs-providers-tab").click();
realmSettingsPage.toggleAddProviderDropdown();
cy.getId("option-aes-generated").click();
realmSettingsPage.enterConsoleDisplayName("test_aes-generated");
cy.wait(200);
realmSettingsPage.addProvider();
realmSettingsPage.toggleAddProviderDropdown();
cy.getId("option-ecdsa-generated").click();
realmSettingsPage.enterConsoleDisplayName("test_ecdsa-generated");
cy.wait(200);
realmSettingsPage.addProvider();
realmSettingsPage.toggleAddProviderDropdown();
cy.getId("option-hmac-generated").click();
realmSettingsPage.enterConsoleDisplayName("test_hmac-generated");
cy.wait(200);
realmSettingsPage.addProvider();
realmSettingsPage.toggleAddProviderDropdown();
cy.getId("option-rsa-generated").click();
realmSettingsPage.enterConsoleDisplayName("test_rsa-generated");
realmSettingsPage.addProvider();
});
});
// });

View file

@ -25,10 +25,17 @@ export default class RealmSettingsPage {
fromInput = "sender-email-address";
enableSslCheck = "enable-ssl";
enableStartTlsCheck = "enable-start-tls";
addProviderDropdown = "addProviderDropdown";
addProviderButton = "add-provider-button";
displayName = "display-name-input";
selectLoginThemeType(themeType: string) {
const themesUrl = "/auth/admin/realms/master/themes";
cy.intercept(themesUrl).as("themesFetch");
cy.get(this.selectLoginTheme).click();
cy.get(this.loginThemeList).contains(themeType).click();
return this;
}
@ -90,6 +97,24 @@ export default class RealmSettingsPage {
return this;
}
toggleAddProviderDropdown() {
const keysUrl = "/auth/admin/realms/master/keys";
cy.intercept(keysUrl).as("keysFetch");
cy.getId(this.addProviderDropdown).click();
return this;
}
addProvider() {
cy.getId(this.addProviderButton).click();
return this;
}
enterConsoleDisplayName(name: string) {
cy.getId(this.displayName).clear().type(name);
}
save(saveBtn: string) {
cy.getId(saveBtn).click();

View file

@ -0,0 +1,252 @@
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-admin/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useRealm } from "../context/realm-context/RealmContext";
type AESGeneratedModalProps = {
providerType?: string;
handleModalToggle?: () => void;
refresh?: () => void;
open: boolean;
};
export const AESGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: // save,
AESGeneratedModalProps) => {
const { t } = useTranslation("groups");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert } = useAlerts();
const { handleSubmit, control } = useForm({});
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const [displayName, setDisplayName] = useState("");
const realm = useRealm();
const allComponentTypes = serverInfo.componentTypes![
"org.keycloak.keys.KeyProvider"
];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
parentId: realm.realm,
name: displayName !== "" ? displayName : providerType,
providerId: providerType,
providerType: "org.keycloak.keys.KeyProvider",
...component,
});
refresh!();
addAlert(t("realm-settings:saveProviderSuccess"), AlertVariant.success);
handleModalToggle!();
} catch (error) {
addAlert(
t("realm-settings:saveProviderError") +
error.response?.data?.errorMessage || error,
AlertVariant.danger
);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("realm-settings: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"
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("realm-settings:consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
forLabel={t("loginTheme")}
forID="kc-console-display-name"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
setDisplayName(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
forLabel={t("enabled")}
forID="kc-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("realm-settings:active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
forLabel={t("active")}
forID="kc-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 === "aes-generated" && (
<FormGroup
label={t("realm-settings:AESKeySize")}
fieldId="kc-aes-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:AESKeySize"
forLabel={t("AESKeySize")}
forID="kc-aes-key-size"
/>
}
>
<Controller
name="config.secretSize"
control={control}
defaultValue={["16"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-aes-keysize"
onToggle={() =>
setIsKeySizeDropdownOpen(!isKeySizeDropdownOpen)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsKeySizeDropdownOpen(false);
}}
selections={[value + ""]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("aesKeySize")}
data-testid="select-secret-size"
>
{allComponentTypes[0].properties[3].options!.map(
(item, idx) => (
<SelectOption
selected={item === value}
key={`email-theme-${idx}`}
value={item}
/>
)
)}
</Select>
)}
/>
</FormGroup>
)}
</Form>
</Modal>
);
};

View file

@ -0,0 +1,367 @@
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-admin/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useRealm } from "../context/realm-context/RealmContext";
type ECDSAGeneratedModalProps = {
providerType?: string;
handleModalToggle?: () => void;
refresh?: () => void;
open: boolean;
};
export const ECDSAGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: // save,
ECDSAGeneratedModalProps) => {
const { t } = useTranslation("groups");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert } = useAlerts();
const { handleSubmit, control } = useForm({});
const [
isEllipticCurveDropdownOpen,
setIsEllipticCurveDropdownOpen,
] = useState(false);
const [isRSAalgDropdownOpen, setIsRSAalgDropdownOpen] = useState(false);
const [displayName, setDisplayName] = useState("");
const realm = useRealm();
const [keyFileName, setKeyFileName] = useState("");
const [certificateFileName, setCertificateFileName] = useState("");
const allComponentTypes = serverInfo.componentTypes![
"org.keycloak.keys.KeyProvider"
];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
parentId: realm.realm,
name: displayName !== "" ? displayName : providerType,
providerId: providerType,
providerType: "org.keycloak.keys.KeyProvider",
...component,
});
refresh!();
addAlert(t("realm-settings:saveProviderSuccess"), AlertVariant.success);
handleModalToggle!();
} catch (error) {
addAlert(
t("realm-settings:saveProviderError") +
error.response?.data?.errorMessage || error,
AlertVariant.danger
);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("realm-settings: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"
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("realm-settings:consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
forLabel={t("loginTheme")}
forID="kc-console-display-name"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
setDisplayName(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
forLabel={t("enabled")}
forID="kc-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("realm-settings:active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
forLabel={t("active")}
forID="kc-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("realm-settings:algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
forLabel={t("algorithm")}
forID="kc-algorithm"
/>
}
>
<Controller
name="algorithm"
control={control}
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("realm-settings:privateRSAKey")}
fieldId="kc-private-rsa-key"
labelIcon={
<HelpItem
helpText="realm-settings-help:privateRSAKey"
forLabel={t("privateRSAKey")}
forID="kc-rsa-key"
/>
}
>
<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("realm-settings:x509Certificate")}
fieldId="kc-aes-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:x509Certificate"
forLabel={t("x509Certificate")}
forID="kc-x509-certificatw"
/>
}
>
<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>
</>
)}
{providerType === "ecdsa-generated" && (
<FormGroup
label={t("realm-settings:ellipticCurve")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:ellipticCurve"
forLabel={t("emailTheme")}
forID="kc-email-theme"
/>
}
>
<Controller
name="config.ecdsaEllipticCurveKey"
control={control}
defaultValue={["P-256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={() =>
setIsEllipticCurveDropdownOpen(!isEllipticCurveDropdownOpen)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsEllipticCurveDropdownOpen(false);
}}
selections={[value + ""]}
variant={SelectVariant.single}
aria-label={t("emailTheme")}
isOpen={isEllipticCurveDropdownOpen}
placeholderText="Select one..."
data-testid="select-email-theme"
>
{allComponentTypes[1].properties[3].options!.map((p, idx) => (
<SelectOption
selected={p === value}
key={`email-theme-${idx}`}
value={p}
></SelectOption>
))}
</Select>
)}
/>
</FormGroup>
)}
</Form>
</Modal>
);
};

View file

@ -0,0 +1,305 @@
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-admin/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useRealm } from "../context/realm-context/RealmContext";
type HMACGeneratedModalProps = {
providerType?: string;
handleModalToggle?: () => void;
refresh?: () => void;
open: boolean;
};
export const HMACGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: // save,
HMACGeneratedModalProps) => {
const { t } = useTranslation("groups");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert } = useAlerts();
const { handleSubmit, control } = useForm({});
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const [
isEllipticCurveDropdownOpen,
setIsEllipticCurveDropdownOpen,
] = useState(false);
const [displayName, setDisplayName] = useState("");
const realm = useRealm();
const allComponentTypes = serverInfo.componentTypes![
"org.keycloak.keys.KeyProvider"
];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
parentId: realm.realm,
name: displayName !== "" ? displayName : providerType,
providerId: providerType,
providerType: "org.keycloak.keys.KeyProvider",
...component,
});
refresh!();
addAlert(t("realm-settings:saveProviderSuccess"), AlertVariant.success);
handleModalToggle!();
} catch (error) {
addAlert(
t("realm-settings:saveProviderError") +
error.response?.data?.errorMessage || error,
AlertVariant.danger
);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("realm-settings: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"
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("realm-settings:consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
forLabel={t("loginTheme")}
forID="kc-console-display-name"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
setDisplayName(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
forLabel={t("enabled")}
forID="kc-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("realm-settings:active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
forLabel={t("active")}
forID="kc-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 === "hmac-generated" && (
<>
<FormGroup
label={t("realm-settings:secretSize")}
fieldId="kc-aes-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:secretSize"
forLabel={t("emailTheme")}
forID="kc-email-theme"
/>
}
>
<Controller
name="config.secretSize"
control={control}
defaultValue={["64"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-aes-keysize"
onToggle={() =>
setIsKeySizeDropdownOpen(!isKeySizeDropdownOpen)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsKeySizeDropdownOpen(false);
}}
selections={[value + ""]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("aesKeySize")}
data-testid="select-secret-size"
>
{allComponentTypes[2].properties[3].options!.map(
(item, idx) => (
<SelectOption
selected={item === value}
key={`email-theme-${idx}`}
value={item}
/>
)
)}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("realm-settings:algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
forLabel={t("algorithm")}
forID="kc-algorithm"
/>
}
>
<Controller
name="config.algorithm"
control={control}
defaultValue={["HS-256"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-elliptic"
onToggle={() =>
setIsEllipticCurveDropdownOpen(
!isEllipticCurveDropdownOpen
)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsEllipticCurveDropdownOpen(false);
}}
selections={[value + ""]}
variant={SelectVariant.single}
aria-label={t("emailTheme")}
isOpen={isEllipticCurveDropdownOpen}
placeholderText="Select one..."
data-testid="select-email-theme"
>
{allComponentTypes[2].properties[4].options!.map(
(p, idx) => (
<SelectOption
selected={p === value}
key={`email-theme-${idx}`}
value={p}
></SelectOption>
)
)}
</Select>
)}
/>
</FormGroup>
</>
)}
</Form>
</Modal>
);
};

View file

@ -0,0 +1,366 @@
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-admin/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useRealm } from "../context/realm-context/RealmContext";
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 } = useAlerts();
const { handleSubmit, control } = useForm({});
const [
isEllipticCurveDropdownOpen,
setIsEllipticCurveDropdownOpen,
] = useState(false);
const [displayName, setDisplayName] = useState("");
const realm = useRealm();
const allComponentTypes = serverInfo.componentTypes![
"org.keycloak.keys.KeyProvider"
];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
parentId: realm.realm,
name: displayName !== "" ? displayName : providerType,
providerId: providerType,
providerType: "org.keycloak.keys.KeyProvider",
...component,
});
refresh!();
addAlert(t("realm-settings:saveProviderSuccess"), AlertVariant.success);
handleModalToggle!();
} catch (error) {
addAlert(
t("realm-settings:saveProviderError") +
error.response?.data?.errorMessage || error,
AlertVariant.danger
);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("realm-settings: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"
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("realm-settings:consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
forLabel={t("loginTheme")}
forID="kc-console-display-name"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
setDisplayName(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
forLabel={t("enabled")}
forID="kc-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("realm-settings:active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
forLabel={t("active")}
forID="kc-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("realm-settings:algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
forLabel={t("algorithm")}
forID="kc-email-theme"
/>
}
>
<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("realm-settings:keystore")}
fieldId="kc-login-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:keystore"
forLabel={t("keystore")}
forID="kc-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("realm-settings:keystorePassword")}
fieldId="kc-login-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:keystorePassword"
forLabel={t("keystorePassword")}
forID="kc-keystore-password"
/>
}
>
<Controller
name="config.keystorePassword"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
onChange={(value) => {
onChange([value + ""]);
setDisplayName(value);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("realm-settings:keyAlias")}
fieldId="kc-login-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:keyAlias"
forLabel={t("keyAlias")}
forID="kc-key-alias"
/>
}
>
<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("realm-settings:keyPassword")}
fieldId="kc-login-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:keyPassword"
forLabel={t("keyPassword")}
forID="kc-key-password"
/>
}
>
<Controller
name="config.keyPassword"
control={control}
defaultValue={[]}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
onChange={(value) => {
onChange([value + ""]);
setDisplayName(value);
}}
data-testid="select-display-name"
></TextInput>
)}
/>
</FormGroup>
</>
)}
</Form>
</Modal>
);
};

View file

@ -11,6 +11,7 @@ import {
DataListItemCells,
DataListItemRow,
Dropdown,
DropdownItem,
DropdownToggle,
InputGroup,
PageSection,
@ -26,6 +27,13 @@ import type ComponentRepresentation from "keycloak-admin/lib/defs/componentRepre
import type ComponentTypeRepresentation from "keycloak-admin/lib/defs/componentTypeRepresentation";
import "./RealmSettingsSection.css";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { AESGeneratedModal } from "./AESGeneratedModal";
import { ECDSAGeneratedModal } from "./ECDSAGeneratedModal";
import { HMACGeneratedModal } from "./HMACGeneratedModal";
import { JavaKeystoreModal } from "./JavaKeystoreModal";
import { RSAModal } from "./RSAModal";
import { RSAGeneratedModal } from "./RSAGeneratedModal";
type ComponentData = KeyMetadataRepresentation & {
providerDescription?: string;
@ -34,9 +42,12 @@ type ComponentData = KeyMetadataRepresentation & {
type KeysTabInnerProps = {
components: ComponentData[];
realmComponents: ComponentRepresentation[];
keyProviderComponentTypes: ComponentTypeRepresentation[];
refresh: () => void;
};
export const KeysTabInner = ({ components }: KeysTabInnerProps) => {
export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => {
const { t } = useTranslation("roles");
const [id, setId] = useState("");
@ -44,10 +55,21 @@ export const KeysTabInner = ({ components }: KeysTabInnerProps) => {
const [filteredComponents, setFilteredComponents] = useState<ComponentData[]>(
[]
);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const serverInfo = useServerInfo();
const providerTypes = serverInfo.componentTypes![
"org.keycloak.keys.KeyProvider"
].map((item) => item.id);
const itemIds = components.map((_, idx) => "data" + idx);
const [itemOrder, setItemOrder] = useState<string[]>([]);
const [providerDropdownOpen, setProviderDropdownOpen] = useState(false);
const [defaultConsoleDisplayName, setDefaultConsoleDisplayName] = useState(
""
);
const [liveText, setLiveText] = useState("");
@ -96,8 +118,68 @@ export const KeysTabInner = ({ components }: KeysTabInnerProps) => {
setSearchVal(value);
};
const handleModalToggle = () => {
setIsCreateModalOpen(!isCreateModalOpen);
};
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 === "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}
/>
)}
<PageSection variant="light" padding={{ default: "noPadding" }}>
<Toolbar>
<>
@ -125,12 +207,31 @@ export const KeysTabInner = ({ components }: KeysTabInnerProps) => {
<Dropdown
data-testid="addProviderDropdown"
className="add-provider-dropdown"
onSelect={() => {}}
isOpen={providerDropdownOpen}
toggle={
<DropdownToggle isPrimary>
<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>
@ -222,11 +323,13 @@ export const KeysTabInner = ({ components }: KeysTabInnerProps) => {
type KeysProps = {
components: ComponentRepresentation[];
keyProviderComponentTypes: ComponentTypeRepresentation[];
refresh: () => void;
};
export const KeysProviderTab = ({
components,
keyProviderComponentTypes,
refresh,
...props
}: KeysProps) => {
return (
@ -238,6 +341,9 @@ export const KeysProviderTab = ({
);
return { ...component, providerDescription: provider?.helpText };
})}
keyProviderComponentTypes={keyProviderComponentTypes}
refresh={refresh}
realmComponents={components}
{...props}
/>
);

View file

@ -0,0 +1,297 @@
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-admin/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useRealm } from "../context/realm-context/RealmContext";
type RSAGeneratedModalProps = {
providerType?: string;
handleModalToggle?: () => void;
refresh?: () => void;
open: boolean;
};
export const RSAGeneratedModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: RSAGeneratedModalProps) => {
const { t } = useTranslation("groups");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert } = useAlerts();
const { handleSubmit, control } = useForm({});
const [isKeySizeDropdownOpen, setIsKeySizeDropdownOpen] = useState(false);
const [isRSAalgDropdownOpen, setIsRSAalgDropdownOpen] = useState(false);
const [displayName, setDisplayName] = useState("");
const realm = useRealm();
const allComponentTypes = serverInfo.componentTypes![
"org.keycloak.keys.KeyProvider"
];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
parentId: realm.realm,
name: displayName !== "" ? displayName : providerType,
providerId: providerType,
providerType: "org.keycloak.keys.KeyProvider",
...component,
});
refresh!();
addAlert(t("realm-settings:saveProviderSuccess"), AlertVariant.success);
handleModalToggle!();
} catch (error) {
addAlert(
t("realm-settings:saveProviderError") +
error.response?.data?.errorMessage || error,
AlertVariant.danger
);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("realm-settings: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"
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("realm-settings:consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
forLabel={t("loginTheme")}
forID="kc-console-display-name"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
setDisplayName(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
forLabel={t("enabled")}
forID="kc-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("realm-settings:active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
forLabel={t("active")}
forID="kc-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("realm-settings:algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
forLabel={t("algorithm")}
forID="kc-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[5].properties[3].options!.map(
(p, idx) => (
<SelectOption
selected={p === value}
key={`rsa-algorithm-${idx}`}
value={p}
></SelectOption>
)
)}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("realm-settings:AESKeySize")}
fieldId="kc-aes-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:AESKeySize"
forLabel={t("AESKeySize")}
forID="kc-aes-key-size"
/>
}
>
<Controller
name="config.secretSize"
control={control}
defaultValue={["2048"]}
render={({ onChange, value }) => (
<Select
toggleId="kc-rsa-keysize"
onToggle={() =>
setIsKeySizeDropdownOpen(!isKeySizeDropdownOpen)
}
onSelect={(_, value) => {
onChange([value + ""]);
setIsKeySizeDropdownOpen(false);
}}
selections={[value + ""]}
isOpen={isKeySizeDropdownOpen}
variant={SelectVariant.single}
aria-label={t("keySize")}
data-testid="select-secret-size"
>
{allComponentTypes[5].properties[4].options!.map(
(item, idx) => (
<SelectOption
selected={item === value}
key={`rsa-generated-key-size-${idx}`}
value={item}
/>
)
)}
</Select>
)}
/>
</FormGroup>
</>
)}
</Form>
</Modal>
);
};

View file

@ -0,0 +1,317 @@
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-admin/lib/defs/componentRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useRealm } from "../context/realm-context/RealmContext";
type RSAModalProps = {
providerType?: string;
handleModalToggle?: () => void;
refresh?: () => void;
open: boolean;
};
export const RSAModal = ({
providerType,
handleModalToggle,
open,
refresh,
}: // save,
RSAModalProps) => {
const { t } = useTranslation("groups");
const serverInfo = useServerInfo();
const adminClient = useAdminClient();
const { addAlert } = useAlerts();
const { handleSubmit, control } = useForm({});
const [isRSAalgDropdownOpen, setIsRSAalgDropdownOpen] = useState(false);
const [displayName, setDisplayName] = useState("");
const realm = useRealm();
const [keyFileName, setKeyFileName] = useState("");
const [certificateFileName, setCertificateFileName] = useState("");
const allComponentTypes = serverInfo.componentTypes![
"org.keycloak.keys.KeyProvider"
];
const save = async (component: ComponentRepresentation) => {
try {
await adminClient.components.create({
parentId: realm.realm,
name: displayName !== "" ? displayName : providerType,
providerId: providerType,
providerType: "org.keycloak.keys.KeyProvider",
...component,
});
refresh!();
addAlert(t("realm-settings:saveProviderSuccess"), AlertVariant.success);
handleModalToggle!();
} catch (error) {
addAlert(
t("realm-settings:saveProviderError") +
error.response?.data?.errorMessage || error,
AlertVariant.danger
);
}
};
return (
<Modal
className="add-provider-modal"
variant={ModalVariant.medium}
title={t("realm-settings: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"
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("realm-settings:consoleDisplayName")}
fieldId="kc-console-display-name"
labelIcon={
<HelpItem
helpText="realm-settings-help:displayName"
forLabel={t("loginTheme")}
forID="kc-console-display-name"
/>
}
>
<Controller
name="name"
control={control}
defaultValue={providerType}
render={({ onChange }) => (
<TextInput
aria-label={t("consoleDisplayName")}
defaultValue={providerType}
onChange={(value) => {
onChange(value);
setDisplayName(value);
}}
data-testid="display-name-input"
></TextInput>
)}
/>
</FormGroup>
<FormGroup
label={t("common:enabled")}
fieldId="kc-enabled"
labelIcon={
<HelpItem
helpText={t("realm-settings-help:enabled")}
forLabel={t("enabled")}
forID="kc-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("realm-settings:active")}
fieldId="kc-active"
labelIcon={
<HelpItem
helpText="realm-settings-help:active"
forLabel={t("active")}
forID="kc-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("realm-settings:algorithm")}
fieldId="kc-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:algorithm"
forLabel={t("algorithm")}
forID="kc-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("realm-settings:privateRSAKey")}
fieldId="kc-private-rsa-key"
labelIcon={
<HelpItem
helpText="realm-settings-help:privateRSAKey"
forLabel={t("privateRSAKey")}
forID="kc-rsa-key"
/>
}
>
<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("realm-settings:x509Certificate")}
fieldId="kc-aes-keysize"
labelIcon={
<HelpItem
helpText="realm-settings-help:x509Certificate"
forLabel={t("x509Certificate")}
forID="kc-x509-certificatw"
/>
}
>
<Controller
name="config.certificate"
control={control}
defaultValue={[]}
render={({ onChange, value }) => (
<FileUpload
id="importCertificate"
type="text"
value={value[0]}
filenamePlaceholder="Upload a PEM file or paste key below"
filename={certificateFileName}
onChange={(value, fileName) => {
setCertificateFileName(fileName);
onChange([value]);
}}
/>
)}
/>
</FormGroup>
</>
)}
</Form>
</Modal>
);
};

View file

@ -56,6 +56,10 @@ button.pf-c-data-list__item-draggable-button.row-drag-button {
margin-right: 0px;
}
.pf-c-data-list__item-row.test {
font-weight: bold;
}
button.pf-c-button.pf-m-link.add-provider {
padding: 0px;
}
@ -68,3 +72,7 @@ button.pf-c-button.pf-m-link.add-provider {
display: flex;
align-items: start;
}
.add-provider-modal > div.pf-c-modal-box__body {
overflow: visible;
}

View file

@ -130,6 +130,8 @@ export const RealmSettingsSection = () => {
const { control, getValues, setValue, reset: resetForm } = form;
const [realm, setRealm] = useState<RealmRepresentation>();
const [activeTab, setActiveTab] = useState(0);
const [key, setKey] = useState(0);
// const [keys, setKeys] = useState<KeyMetadataRepresentation[]>([]);
const [realmComponents, setRealmComponents] = useState<
ComponentRepresentation[]
>();
@ -155,6 +157,21 @@ export const RealmSettingsSection = () => {
[]
);
const refresh = () => {
setKey(new Date().getTime());
};
useEffect(() => {
const update = async () => {
const realmComponents = await adminClient.components.find({
type: "org.keycloak.keys.KeyProvider",
realm: realmName,
});
setRealmComponents(realmComponents);
};
setTimeout(update, 100);
}, [key]);
useEffect(() => {
if (realm) setupForm(realm);
}, [realm]);
@ -240,18 +257,20 @@ export const RealmSettingsSection = () => {
onSelect={(_, key) => setActiveTab(key as number)}
>
<Tab
id="setup"
id="keysList"
eventKey={0}
title={<TabTitleText>{t("keysList")}</TabTitleText>}
>
<KeysListTab realmComponents={realmComponents} />
</Tab>
<Tab
id="evaluate"
id="providers"
data-testid="rs-providers-tab"
eventKey={1}
title={<TabTitleText>{t("providers")}</TabTitleText>}
>
<KeysProviderTab
refresh={refresh}
components={realmComponents}
keyProviderComponentTypes={kpComponentTypes}
/>

View file

@ -11,6 +11,21 @@
"loginTheme": "Select theme for login, OTP, grant, registration and forgot password pages.",
"accountTheme": "Select theme for user account management pages.",
"adminConsoleTheme": "Select theme for admin console.",
"emailTheme": "Select theme for emails that are sent by the server."
"emailTheme": "Select theme for emails that are sent by the server.",
"displayName": "Display name of provider when linked in admin console",
"priority": "Priority of the provider",
"enabled": "Set if the keys are enabled",
"active": "Set if the keys can be used for signing",
"AESKeySize": "Size in bytes for the generated AES key. Size 16 is for AES-128, Size 24 for AES-192, and Size 32 for AES-256. WARN: Bigger keys than 128 are not allowed on some JDK implementations.",
"ellipticCurve": "Elliptic curve used in ECDSA",
"secretSize": "Size in bytes for the generated secret",
"algorithm": "Intended algorithm for the key",
"keystore": "Path to keys file",
"keystorePassword": "Password for the keys",
"keyAlias": "Alias for the private key",
"keyPassword": "Password for the private key",
"privateRSAKey": "Private RSA Key encoded in PEM format",
"x509Certificate": "X509 Certificate encoded in PEM format"
}
}

View file

@ -1,3 +1,4 @@
{
"realm-settings": {
"partialImport": "Partial import",
@ -10,6 +11,8 @@
"disableConfirmTitle": "Disable realm?",
"disableConfirm": "User and clients can't access the realm if it's disabled. Are you sure you want to continue?",
"saveSuccess": "Realm successfully updated",
"saveProviderSuccess": "The provider has been saved successfully.",
"saveProviderError": "Error saving provider: ",
"saveError": "Realm could not be updated: {error}",
"general": "General",
"login": "Login",
@ -33,8 +36,25 @@
"keys": "Keys",
"keysList": "Keys list",
"searchKey":"Search key",
"keystore": "Keystore",
"keystorePassword": "Keystore password",
"keyAlias": "Key alias",
"keyPassword": "Key password",
"providers": "Providers",
"algorithm": "Algorithm",
"aesGenerated": "aes-generated",
"ecdsaGenerated": "ecdsca-generated",
"hmacGenerated": "hmac-generated",
"javaKeystore": "java-keystore",
"rsa": "rsa",
"rsaGenerated": "rsa-generated",
"consoleDisplayName": "Console Display Name",
"AESKeySize": "AES Key Size",
"active": "Active",
"privateRSAKey": "Private RSA Key",
"x509Certificate": "X509 Certificate",
"ellipticCurve": "Elliptic Curve",
"secretSize": "Secret size",
"type": "Type",
"name": "Name",
"kid": "Kid",