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:
parent
04140f1737
commit
2746109e78
13 changed files with 2201 additions and 57 deletions
|
@ -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();
|
||||
});
|
||||
});
|
||||
// });
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
252
src/realm-settings/AESGeneratedModal.tsx
Normal file
252
src/realm-settings/AESGeneratedModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
367
src/realm-settings/ECDSAGeneratedModal.tsx
Normal file
367
src/realm-settings/ECDSAGeneratedModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
305
src/realm-settings/HMACGeneratedModal.tsx
Normal file
305
src/realm-settings/HMACGeneratedModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
366
src/realm-settings/JavaKeystoreModal.tsx
Normal file
366
src/realm-settings/JavaKeystoreModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
|
|
297
src/realm-settings/RSAGeneratedModal.tsx
Normal file
297
src/realm-settings/RSAGeneratedModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
317
src/realm-settings/RSAModal.tsx
Normal file
317
src/realm-settings/RSAModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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"
|
||||
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue