changed to use ui-shared (#27705)

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-03-11 15:48:49 +01:00 committed by GitHub
parent 6967f57f39
commit 131aeedb98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 395 additions and 874 deletions

View file

@ -1,3 +1,4 @@
import Select from "../../../../forms/Select";
import LegacyKeyValueInput from "../LegacyKeyValueInput";
export default class AddMapperPage {
@ -7,7 +8,7 @@ export default class AddMapperPage {
#idpMapperSelect = "idp-mapper-select";
#addMapperButton = "#add-mapper-button";
#mapperNameInput = "#kc-name";
#mapperNameInput = "name";
#attribute = "config.user🍺attribute";
#attributeName = "attribute.name";
#attributeFriendlyName = "attribute.friendly.name";
@ -64,15 +65,15 @@ export default class AddMapperPage {
return this;
}
typeName(name: string) {
cy.findByTestId(this.#mapperNameInput).clear();
cy.findByTestId(this.#mapperNameInput).type(name);
}
fillSocialMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("legacy").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Legacy");
cy.get(this.#idpMapperSelectToggle).click();
cy.findByTestId(this.#idpMapperSelect)
@ -101,13 +102,8 @@ export default class AddMapperPage {
}
addAdvancedAttrToRoleMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
@ -132,13 +128,8 @@ export default class AddMapperPage {
}
addUsernameTemplateImporterMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
@ -157,13 +148,8 @@ export default class AddMapperPage {
}
addHardcodedUserSessionAttrMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
@ -185,13 +171,9 @@ export default class AddMapperPage {
}
addSAMLAttrImporterMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
this.typeName(name);
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
@ -215,14 +197,9 @@ export default class AddMapperPage {
}
addOIDCAttrImporterMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
cy.findByTestId(this.#idpMapperSelect)
@ -238,13 +215,8 @@ export default class AddMapperPage {
}
addHardcodedRoleMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
@ -257,13 +229,8 @@ export default class AddMapperPage {
}
addHardcodedAttrMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
@ -283,13 +250,9 @@ export default class AddMapperPage {
}
addSAMLAttributeToRoleMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
this.typeName(name);
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
@ -305,9 +268,7 @@ export default class AddMapperPage {
}
editUsernameTemplateImporterMapper() {
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("legacy").click();
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Legacy");
cy.findByTestId(this.#template).type("_edited");
@ -319,9 +280,7 @@ export default class AddMapperPage {
}
editSocialMapper() {
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.findByTestId(this.#socialProfileJSONfieldPath).clear();
@ -339,9 +298,7 @@ export default class AddMapperPage {
}
editSAMLorOIDCMapper() {
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("legacy").click();
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#attributesKeyInput).clear();
cy.get(this.#attributesKeyInput).type("key_edited");
@ -357,14 +314,9 @@ export default class AddMapperPage {
}
addOIDCAttributeImporterMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
this.typeName(name);
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();
cy.findByTestId(this.#idpMapperSelect)
@ -383,13 +335,9 @@ export default class AddMapperPage {
}
addOIDCClaimToRoleMapper(name: string) {
cy.get(this.#mapperNameInput).clear();
this.typeName(name);
cy.get(this.#mapperNameInput).clear().type(name);
cy.get(this.#syncmodeSelectToggle).click();
cy.findByTestId("inherit").click();
Select.selectItem(cy.get(this.#syncmodeSelectToggle), "Inherit");
cy.get(this.#idpMapperSelectToggle).click();

View file

@ -1,3 +1,4 @@
import Select from "../../../../../forms/Select";
import PageObject from "../../../components/PageObject";
import Masthead from "../../../Masthead";
@ -6,44 +7,32 @@ const masthead = new Masthead();
export default class ProviderSAMLSettings extends PageObject {
#samlSwitch = "Saml-switch";
#modalConfirm = "#modal-confirm";
#serviceProviderEntityID = "serviceProviderEntityId";
#serviceProviderEntityID = "config.entityId";
#identityProviderEntityId = "identityProviderEntityId";
#ssoServiceUrl = "sso-service-url";
#singleLogoutServiceUrl = "single-logout-service-url";
#nameIdPolicyFormat = "#kc-nameIdPolicyFormat";
#principalType = "#kc-principalType";
#principalAttribute = "principalAttribute";
#principalSubjectNameId = "subjectNameId-option";
#principalAttributeName = "attributeName-option";
#principalFriendlyAttribute = "attributeFriendlyName-option";
#ssoServiceUrl = "config.singleSignOnServiceUrl";
#singleLogoutServiceUrl = "config.singleLogoutServiceUrl";
#nameIdPolicyFormat = "#nameIDPolicyFormat";
#principalType = "#principalType";
#transientPolicy = "transient-option";
#emailPolicy = "email-option";
#kerberosPolicy = "kerberos-option";
#x509Policy = "x509-option";
#windowsDomainQNPolicy = "windowsDomainQN-option";
#unspecifiedPolicy = "unspecified-option";
#persistentPolicy = "persistent-option";
#allowCreate = "config.allowCreate";
#httpPostBindingResponse = "config.postBindingResponse";
#httpPostBindingAuthnRequest = "config.postBindingAuthnRequest";
#httpPostBindingLogout = "config.postBindingLogout";
#wantAuthnRequestsSigned = "config.wantAuthnRequestsSigned";
#allowCreate = "#allowCreate";
#httpPostBindingResponse = "#httpPostBindingResponse";
#httpPostBindingAuthnRequest = "#httpPostBindingAuthnRequest";
#httpPostBindingLogout = "#httpPostBindingLogout";
#wantAuthnRequestsSigned = "#wantAuthnRequestsSigned";
#signatureAlgorithm = "#signatureAlgorithm";
#samlSignatureKeyName = "#xmlSigKeyInfoKeyNameTransformer";
#signatureAlgorithm = "#kc-signatureAlgorithm";
#samlSignatureKeyName = "#kc-samlSignatureKeyName";
#wantAssertionsSigned = "#wantAssertionsSigned";
#wantAssertionsEncrypted = "#wantAssertionsEncrypted";
#forceAuthentication = "#forceAuthentication";
#validateSignature = "#validateSignature";
#validatingX509Certs = "validatingX509Certs";
#signServiceProviderMetadata = "#signServiceProviderMetadata";
#passSubject = "#passSubject";
#allowedClockSkew = "allowedClockSkew";
#attributeConsumingServiceIndex = "attributeConsumingServiceIndex";
#attributeConsumingServiceName = "attributeConsumingServiceName";
#wantAssertionsSigned = "config.wantAssertionsSigned";
#wantAssertionsEncrypted = "config.wantAssertionsEncrypted";
#forceAuthentication = "config.forceAuthn";
#validateSignature = "config.validateSignature";
#validatingX509Certs = "config.signingCertificate";
#signServiceProviderMetadata = "config.signSpMetadata";
#passSubject = "config.loginHint";
#allowedClockSkew = "#config\\.allowedClockSkew";
#attributeConsumingServiceIndex = "#config\\.attributeConsumingServiceIndex";
#attributeConsumingServiceName = "config.attributeConsumingServiceName";
#comparison = "#comparison";
#saveBtn = "idp-details-save";
@ -95,26 +84,33 @@ export default class ProviderSAMLSettings extends PageObject {
}
public typeX509Certs(cert: string) {
cy.findByTestId(this.#validatingX509Certs).clear().type(cert);
cy.findByTestId(this.#validatingX509Certs).clear();
cy.findByTestId(this.#validatingX509Certs).type(cert);
return this;
}
public selectNamePolicyIdFormat() {
cy.get(this.#nameIdPolicyFormat).scrollIntoView().click();
public selectNamePolicyIdFormat(option: string) {
cy.get(this.#nameIdPolicyFormat).scrollIntoView();
Select.selectItem(cy.get(this.#nameIdPolicyFormat), option);
Select.assertSelectedItem(cy.get(this.#nameIdPolicyFormat), option);
return this;
}
public selectPrincipalFormat() {
cy.get(this.#principalType).scrollIntoView().click();
public selectPrincipalFormat(option: string) {
cy.get(this.#principalType).scrollIntoView();
Select.selectItem(cy.get(this.#principalType), option);
Select.assertSelectedItem(cy.get(this.#principalType), option);
return this;
}
public selectSignatureAlgorithm(algorithm: string) {
cy.get(this.#signatureAlgorithm).scrollIntoView().click();
cy.findByText(algorithm).click();
cy.get(this.#signatureAlgorithm).scrollIntoView();
Select.selectItem(cy.get(this.#signatureAlgorithm), algorithm);
}
public selectSAMLSignature(key: string) {
cy.get(this.#samlSignatureKeyName).scrollIntoView().click();
cy.findByText(key).click();
cy.get(this.#samlSignatureKeyName).scrollIntoView();
Select.selectItem(cy.get(this.#samlSignatureKeyName), key);
}
public selectComparison(comparison: string) {
@ -143,25 +139,18 @@ export default class ProviderSAMLSettings extends PageObject {
}
public assertNameIdPolicyFormat() {
this.selectNamePolicyIdFormat();
cy.findByTestId(this.#transientPolicy).click();
this.selectNamePolicyIdFormat();
cy.findByTestId(this.#emailPolicy).click();
this.selectNamePolicyIdFormat();
cy.findByTestId(this.#kerberosPolicy).click();
this.selectNamePolicyIdFormat();
cy.findByTestId(this.#x509Policy).click();
this.selectNamePolicyIdFormat();
cy.findByTestId(this.#windowsDomainQNPolicy).click();
this.selectNamePolicyIdFormat();
cy.findByTestId(this.#unspecifiedPolicy).click();
this.selectNamePolicyIdFormat();
cy.findByTestId(this.#persistentPolicy).click();
this.selectNamePolicyIdFormat("Transient");
this.selectNamePolicyIdFormat("Email");
this.selectNamePolicyIdFormat("Kerberos");
this.selectNamePolicyIdFormat("X.509 Subject Name");
this.selectNamePolicyIdFormat("Windows Domain Qualified Name");
this.selectNamePolicyIdFormat("Unspecified");
this.selectNamePolicyIdFormat("Persistent");
return this;
}
public assertSignatureAlgorithm() {
cy.get(this.#wantAuthnRequestsSigned).parent().click();
cy.findByTestId(this.#wantAuthnRequestsSigned).parent().click();
cy.get(this.#signatureAlgorithm).should("not.exist");
cy.get(this.#samlSignatureKeyName).should("not.exist");
this.clickRevertBtn();
@ -183,38 +172,32 @@ export default class ProviderSAMLSettings extends PageObject {
}
public assertPrincipalType() {
this.selectPrincipalFormat();
cy.findByTestId(this.#principalAttributeName).click();
cy.findByTestId(this.#principalAttribute).should("exist").scrollIntoView();
this.selectPrincipalFormat();
cy.findByTestId(this.#principalFriendlyAttribute).click();
cy.findByTestId(this.#principalAttribute).should("exist");
this.selectPrincipalFormat();
cy.findByTestId(this.#principalSubjectNameId).click();
cy.findByTestId(this.#principalAttribute).should("not.exist");
this.selectPrincipalFormat("Subject NameID");
this.selectPrincipalFormat("Attribute [Name]");
this.selectPrincipalFormat("Attribute [Friendly Name]");
return this;
}
public assertSAMLSwitches() {
cy.get(this.#allowCreate).parent().click();
cy.get(this.#httpPostBindingResponse).parent().click();
cy.get(this.#httpPostBindingLogout).parent().click();
cy.get(this.#httpPostBindingAuthnRequest).parent().click();
cy.findByTestId(this.#allowCreate).parent().click();
cy.findByTestId(this.#httpPostBindingResponse).parent().click();
cy.findByTestId(this.#httpPostBindingLogout).parent().click();
cy.findByTestId(this.#httpPostBindingAuthnRequest).parent().click();
cy.get(this.#wantAssertionsSigned).parent().click();
cy.get(this.#wantAssertionsEncrypted).parent().click();
cy.get(this.#forceAuthentication).parent().click();
cy.findByTestId(this.#wantAssertionsSigned).parent().click();
cy.findByTestId(this.#wantAssertionsEncrypted).parent().click();
cy.findByTestId(this.#forceAuthentication).parent().click();
cy.get(this.#signServiceProviderMetadata).parent().click();
cy.get(this.#passSubject).parent().click();
cy.findByTestId(this.#signServiceProviderMetadata).parent().click();
cy.findByTestId(this.#passSubject).parent().click();
return this;
}
public assertValidateSignatures() {
cy.get(this.#validateSignature).parent().click();
cy.findByTestId(this.#validateSignature).parent().click();
cy.findByTestId(this.#validatingX509Certs).should("not.exist");
cy.get(this.#validateSignature).parent().click();
cy.findByTestId(this.#validateSignature).parent().click();
this.typeX509Certs("X509 Certificate");
this.clickRevertBtn();
cy.findByTestId(this.#validatingX509Certs);
@ -223,13 +206,13 @@ export default class ProviderSAMLSettings extends PageObject {
}
public assertTextFields() {
cy.findByTestId(this.#allowedClockSkew)
cy.get(this.#allowedClockSkew)
.find("input")
.should("have.value", 0)
.clear()
.type("111");
cy.findByTestId(this.#attributeConsumingServiceIndex)
cy.get(this.#attributeConsumingServiceIndex)
.find("input")
.should("have.value", 0)
.clear()

View file

@ -7,15 +7,14 @@ import {
Button,
ButtonVariant,
DropdownItem,
FormGroup,
PageSection,
ValidatedOptions,
} from "@patternfly/react-core";
import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom";
import { TextControl } from "ui-shared";
import { adminClient } from "../../admin-client";
import { useAlerts } from "../../components/alert/Alerts";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
@ -23,7 +22,6 @@ import { DynamicComponents } from "../../components/dynamic/DynamicComponents";
import { FormAccess } from "../../components/form/FormAccess";
import type { AttributeForm } from "../../components/key-value-form/AttributeForm";
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { ViewHeader } from "../../components/view-header/ViewHeader";
import { useRealm } from "../../context/realm-context/RealmContext";
import { convertFormValuesToObject, convertToFormValues } from "../../util";
@ -50,11 +48,7 @@ export default function AddMapper() {
const form = useForm<IdPMapperRepresentationWithAttributes>({
shouldUnregister: true,
});
const {
handleSubmit,
register,
formState: { errors },
} = form;
const { handleSubmit } = form;
const { addAlert, addError } = useAlerts();
const navigate = useNavigate();
const localeSort = useLocaleSort();
@ -202,41 +196,31 @@ export default function AddMapper() {
onSubmit={handleSubmit(save)}
className="pf-u-mt-lg"
>
{id && (
<FormGroup
label={t("id")}
fieldId="kc-name"
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
value={currentMapper.id}
id="kc-name"
isDisabled={!!id}
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
{...register("name")}
<FormProvider {...form}>
{id && (
<TextControl
name="id"
label={t("id")}
readOnly
rules={{
required: t("required"),
}}
/>
</FormGroup>
)}
{currentMapper.properties && (
<>
<AddMapperForm
form={form}
id={id}
mapperTypes={mapperTypes}
updateMapperType={setCurrentMapper}
mapperType={currentMapper}
/>
<FormProvider {...form}>
<DynamicComponents properties={currentMapper.properties!} />
</FormProvider>
</>
)}
)}
{currentMapper.properties && (
<>
<AddMapperForm
form={form}
id={id}
mapperTypes={mapperTypes}
updateMapperType={setCurrentMapper}
mapperType={currentMapper}
/>
<DynamicComponents properties={currentMapper.properties!} />
</>
)}
</FormProvider>
<ActionGroup>
<Button
data-testid="new-mapper-save-button"

View file

@ -5,14 +5,11 @@ import {
Select,
SelectOption,
SelectVariant,
ValidatedOptions,
} from "@patternfly/react-core";
import { useState } from "react";
import { Controller, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { HelpItem, SelectControl, TextControl } from "ui-shared";
import type { IdPMapperRepresentationWithAttributes } from "./AddMapper";
type AddMapperFormProps = {
@ -34,85 +31,33 @@ export const AddMapperForm = ({
}: AddMapperFormProps) => {
const { t } = useTranslation();
const {
control,
register,
formState: { errors },
} = form;
const { control } = form;
const [mapperTypeOpen, setMapperTypeOpen] = useState(false);
const syncModes = ["inherit", "import", "legacy", "force"];
const [syncModeOpen, setSyncModeOpen] = useState(false);
return (
<>
<FormGroup
<TextControl
name="name"
label={t("name")}
labelIcon={
<HelpItem helpText={t("addIdpMapperNameHelp")} fieldLabelId="name" />
}
fieldId="kc-name"
isRequired
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
id="kc-name"
isDisabled={!!id}
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
{...register("name", { required: true })}
/>
</FormGroup>
<FormGroup
labelIcon={t("addIdpMapperNameHelp")}
readOnly={!!id}
rules={{
required: t("required"),
}}
/>
<SelectControl
name="config.syncMode"
label={t("syncModeOverride")}
isRequired
labelIcon={
<HelpItem
helpText={t("syncModeOverrideHelp")}
fieldLabelId="syncModeOverride"
/>
}
fieldId="syncMode"
>
<Controller
name="config.syncMode"
defaultValue={syncModes[0].toUpperCase()}
control={control}
render={({ field }) => (
<Select
toggleId="syncMode"
datatest-id="syncmode-select"
required
direction="down"
onToggle={() => setSyncModeOpen(!syncModeOpen)}
onSelect={(_, value) => {
field.onChange(value.toString().toUpperCase());
setSyncModeOpen(false);
}}
selections={t(`syncModes.${field.value.toLowerCase()}`)}
variant={SelectVariant.single}
aria-label={t("syncMode")}
isOpen={syncModeOpen}
>
{syncModes.map((option) => (
<SelectOption
selected={option === field.value}
key={option}
data-testid={option}
value={option.toUpperCase()}
>
{t(`syncModes.${option}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
labelIcon={t("syncModeOverrideHelp")}
options={syncModes.map((option) => ({
key: option.toUpperCase(),
value: t(`syncModes.${option}`),
}))}
controller={{ defaultValue: syncModes[0].toUpperCase() }}
/>
<FormGroup
label={t("mapperType")}
labelIcon={

View file

@ -1,22 +1,10 @@
import IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
import {
ExpandableSection,
FormGroup,
NumberInput,
Select,
SelectOption,
SelectVariant,
ValidatedOptions,
} from "@patternfly/react-core";
import { ExpandableSection } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { FormProvider, useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { HelpItem } from "ui-shared";
import { KeycloakTextArea } from "../../components/keycloak-text-area/KeycloakTextArea";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { FormGroupField } from "../component/FormGroupField";
import { SwitchField } from "../component/SwitchField";
import { NumberControl, SelectControl, TextControl } from "ui-shared";
import { DefaultSwitchControl } from "../../components/SwitchControl";
import "./discovery-settings.css";
@ -27,22 +15,8 @@ type DescriptorSettingsProps = {
const Fields = ({ readOnly }: DescriptorSettingsProps) => {
const { t } = useTranslation();
const {
register,
control,
formState: { errors },
} = useFormContext<IdentityProviderRepresentation>();
const [namedPolicyDropdownOpen, setNamedPolicyDropdownOpen] = useState(false);
const [principalTypeDropdownOpen, setPrincipalTypeDropdownOpen] =
useState(false);
const [signatureAlgorithmDropdownOpen, setSignatureAlgorithmDropdownOpen] =
useState(false);
const [encryptionAlgorithmDropdownOpen, setEncryptionAlgorithmDropdownOpen] =
useState(false);
const [
samlSignatureKeyNameDropdownOpen,
setSamlSignatureKeyNameDropdownOpen,
] = useState(false);
const form = useFormContext<IdentityProviderRepresentation>();
const { control } = form;
const wantAuthnSigned = useWatch({
control,
@ -71,595 +45,282 @@ const Fields = ({ readOnly }: DescriptorSettingsProps) => {
return (
<div className="pf-c-form pf-m-horizontal">
<FormGroup
label={t("serviceProviderEntityId")}
fieldId="kc-saml-service-provider-entity-id"
labelIcon={
<HelpItem
helpText={t("serviceProviderEntityIdHelp")}
fieldLabelId="serviceProviderEntityId"
/>
}
>
<KeycloakTextInput
data-testid="serviceProviderEntityId"
id="kc-saml-service-provider-entity-id"
{...register("config.entityId")}
<FormProvider {...form}>
<TextControl
name="config.entityId"
label={t("serviceProviderEntityId")}
labelIcon={t("serviceProviderEntityIdHelp")}
/>
</FormGroup>
<FormGroup
label={t("identityProviderEntityId")}
fieldId="kc-identity-provider-entity-id"
labelIcon={
<HelpItem
helpText={t("identityProviderEntityIdHelp")}
fieldLabelId="identityProviderEntityId"
/>
}
>
<KeycloakTextInput
<TextControl
name="config.idpEntityId"
label={t("identityProviderEntityId")}
labelIcon={t("identityProviderEntityIdHelp")}
data-testid="identityProviderEntityId"
id="kc-identity-provider-entity-id"
{...register("config.idpEntityId")}
/>
</FormGroup>
<FormGroup
label={t("ssoServiceUrl")}
labelIcon={
<HelpItem
helpText={t("ssoServiceUrlHelp")}
fieldLabelId="ssoServiceUrl"
/>
}
fieldId="kc-sso-service-url"
isRequired
validated={
errors.config?.singleSignOnServiceUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
<TextControl
name="config.singleSignOnServiceUrl"
label={t("ssoServiceUrl")}
labelIcon={t("ssoServiceUrlHelp")}
type="url"
data-testid="sso-service-url"
id="kc-sso-service-url"
validated={
errors.config?.singleSignOnServiceUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
isReadOnly={readOnly}
{...register("config.singleSignOnServiceUrl", { required: true })}
readOnly={readOnly}
rules={{ required: t("required") }}
/>
</FormGroup>
<FormGroup
label={t("singleLogoutServiceUrl")}
labelIcon={
<HelpItem
helpText={t("singleLogoutServiceUrlHelp")}
fieldLabelId="singleLogoutServiceUrl"
/>
}
fieldId="single-logout-service-url"
data-testid="single-logout-service-url"
validated={
errors.config?.singleLogoutServiceUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
<TextControl
name="config.singleLogoutServiceUrl"
label={t("singleLogoutServiceUrl")}
labelIcon={t("singleLogoutServiceUrlHelp")}
type="url"
id="single-logout-service-url"
isReadOnly={readOnly}
{...register("config.singleLogoutServiceUrl")}
readOnly={readOnly}
rules={{ required: t("required") }}
/>
</FormGroup>
<SwitchField
field="config.backchannelSupported"
label="backchannelLogout"
data-testid="backchannelLogout"
isReadOnly={readOnly}
/>
<SwitchField
field="config.sendIdTokenOnLogout"
label="sendIdTokenOnLogout"
data-testid="sendIdTokenOnLogout"
defaultValue={"true"}
isReadOnly={readOnly}
/>
<SwitchField
field="config.sendClientIdOnLogout"
label="sendClientIdOnLogout"
data-testid="sendClientIdOnLogout"
isReadOnly={readOnly}
/>
<FormGroup
label={t("nameIdPolicyFormat")}
labelIcon={
<HelpItem
helpText={t("nameIdPolicyFormatHelp")}
fieldLabelId="nameIdPolicyFormat"
/>
}
fieldId="kc-nameIdPolicyFormat"
helperTextInvalid={t("required")}
>
<Controller
<DefaultSwitchControl
name="config.backchannelSupported"
label={t("backchannelLogout")}
isDisabled={readOnly}
stringify
/>
<DefaultSwitchControl
name="config.sendIdTokenOnLogout"
label={t("sendIdTokenOnLogout")}
defaultValue={"true"}
isDisabled={readOnly}
stringify
/>
<DefaultSwitchControl
name="config.sendClientIdOnLogout"
label={t("sendClientIdOnLogout")}
isDisabled={readOnly}
stringify
/>
<SelectControl
name="config.nameIDPolicyFormat"
defaultValue={"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"}
control={control}
render={({ field }) => (
<Select
toggleId="kc-nameIdPolicyFormat"
onToggle={(isExpanded) => setNamedPolicyDropdownOpen(isExpanded)}
isOpen={namedPolicyDropdownOpen}
onSelect={(_, value) => {
field.onChange(value as string);
setNamedPolicyDropdownOpen(false);
}}
selections={field.value}
variant={SelectVariant.single}
isDisabled={readOnly}
>
<SelectOption
data-testid="persistent-option"
value={"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"}
isPlaceholder
>
{t("persistent")}
</SelectOption>
<SelectOption
data-testid="transient-option"
value="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
>
{t("transient")}
</SelectOption>
<SelectOption
data-testid="email-option"
value="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
>
{t("email")}
</SelectOption>
<SelectOption
data-testid="kerberos-option"
value="urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos"
>
{t("kerberos")}
</SelectOption>
<SelectOption
data-testid="x509-option"
value="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"
>
{t("x509")}
</SelectOption>
<SelectOption
data-testid="windowsDomainQN-option"
value="urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"
>
{t("windowsDomainQN")}
</SelectOption>
<SelectOption
data-testid="unspecified-option"
value={"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"}
>
{t("unspecified")}
</SelectOption>
</Select>
)}
></Controller>
</FormGroup>
<FormGroup
label={t("principalType")}
labelIcon={
<HelpItem
helpText={t("principalTypeHelp")}
fieldLabelId="principalType"
/>
}
fieldId="kc-principalType"
helperTextInvalid={t("required")}
>
<Controller
label={t("nameIdPolicyFormat")}
labelIcon={t("nameIdPolicyFormatHelp")}
controller={{
defaultValue:
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
}}
isDisabled={readOnly}
options={[
{
key: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
value: t("persistent"),
},
{
key: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
value: t("transient"),
},
{
key: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
value: t("email"),
},
{
key: "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos",
value: t("kerberos"),
},
{
key: "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
value: t("x509"),
},
{
key: "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName",
value: t("windowsDomainQN"),
},
{
key: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
value: t("unspecified"),
},
]}
/>
<SelectControl
name="config.principalType"
defaultValue={t("subjectNameId")}
control={control}
render={({ field }) => (
<Select
toggleId="kc-principalType"
onToggle={(isExpanded) =>
setPrincipalTypeDropdownOpen(isExpanded)
}
isOpen={principalTypeDropdownOpen}
onSelect={(_, value) => {
field.onChange(value.toString());
setPrincipalTypeDropdownOpen(false);
}}
selections={field.value}
variant={SelectVariant.single}
isDisabled={readOnly}
>
<SelectOption
data-testid="subjectNameId-option"
value="SUBJECT"
isPlaceholder
>
{t("subjectNameId")}
</SelectOption>
<SelectOption
data-testid="attributeName-option"
value="ATTRIBUTE"
>
{t("attributeName")}
</SelectOption>
<SelectOption
data-testid="attributeFriendlyName-option"
value="FRIENDLY_ATTRIBUTE"
>
{t("attributeFriendlyName")}
</SelectOption>
</Select>
)}
></Controller>
</FormGroup>
label={t("principalType")}
labelIcon={t("principalTypeHelp")}
controller={{
defaultValue: "SUBJECT",
}}
isDisabled={readOnly}
options={[
{ key: "SUBJECT", value: t("subjectNameId") },
{ key: "ATTRIBUTE", value: t("attributeName") },
{ key: "FRIENDLY_ATTRIBUTE", value: t("attributeFriendlyName") },
]}
/>
{principalType?.includes("ATTRIBUTE") && (
<FormGroup
label={t("principalAttribute")}
labelIcon={
<HelpItem
helpText={t("principalAttributeHelp")}
fieldLabelId="principalAttribute"
/>
}
fieldId="principalAttribute"
>
<KeycloakTextInput
id="principalAttribute"
data-testid="principalAttribute"
isReadOnly={readOnly}
{...register("config.principalAttribute")}
{principalType?.includes("ATTRIBUTE") && (
<TextControl
name="config.principalAttribute"
label={t("principalAttribute")}
labelIcon={t("principalAttributeHelp")}
readOnly={readOnly}
/>
</FormGroup>
)}
<SwitchField
field="config.allowCreate"
label="allowCreate"
isReadOnly={readOnly}
/>
)}
<DefaultSwitchControl
name="config.allowCreate"
label={t("allowCreate")}
isDisabled={readOnly}
stringify
/>
<SwitchField
field="config.postBindingResponse"
label="httpPostBindingResponse"
isReadOnly={readOnly}
/>
<DefaultSwitchControl
name="config.postBindingResponse"
label={t("httpPostBindingResponse")}
isDisabled={readOnly}
stringify
/>
<SwitchField
field="config.postBindingAuthnRequest"
label="httpPostBindingAuthnRequest"
isReadOnly={readOnly}
/>
<DefaultSwitchControl
name="config.postBindingAuthnRequest"
label={t("httpPostBindingAuthnRequest")}
isDisabled={readOnly}
stringify
/>
<SwitchField
field="config.postBindingLogout"
label="httpPostBindingLogout"
isReadOnly={readOnly}
/>
<DefaultSwitchControl
name="config.postBindingLogout"
label={t("httpPostBindingLogout")}
isDisabled={readOnly}
stringify
/>
<SwitchField
field="config.wantAuthnRequestsSigned"
label="wantAuthnRequestsSigned"
isReadOnly={readOnly}
/>
<DefaultSwitchControl
name="config.wantAuthnRequestsSigned"
label={t("wantAuthnRequestsSigned")}
isDisabled={readOnly}
stringify
/>
{wantAuthnSigned === "true" && (
<>
<FormGroup
label={t("signatureAlgorithm")}
labelIcon={
<HelpItem
helpText={t("signatureAlgorithmHelp")}
fieldLabelId="signatureAlgorithm"
/>
}
fieldId="kc-signatureAlgorithm"
>
<Controller
{wantAuthnSigned === "true" && (
<>
<SelectControl
name="config.signatureAlgorithm"
defaultValue="RSA_SHA256"
control={control}
render={({ field }) => (
<Select
toggleId="kc-signatureAlgorithm"
onToggle={(isExpanded) =>
setSignatureAlgorithmDropdownOpen(isExpanded)
}
isOpen={signatureAlgorithmDropdownOpen}
onSelect={(_, value) => {
field.onChange(value.toString());
setSignatureAlgorithmDropdownOpen(false);
}}
selections={field.value}
variant={SelectVariant.single}
isDisabled={readOnly}
>
<SelectOption value="RSA_SHA1" />
<SelectOption value="RSA_SHA256" isPlaceholder />
<SelectOption value="RSA_SHA256_MGF1" />
<SelectOption value="RSA_SHA512" />
<SelectOption value="RSA_SHA512_MGF1" />
<SelectOption value="DSA_SHA1" />
</Select>
)}
></Controller>
</FormGroup>
<FormGroup
label={t("samlSignatureKeyName")}
labelIcon={
<HelpItem
helpText={t("samlSignatureKeyNameHelp")}
fieldLabelId="samlSignatureKeyName"
/>
}
fieldId="kc-samlSignatureKeyName"
>
<Controller
label={t("signatureAlgorithm")}
labelIcon={t("signatureAlgorithmHelp")}
isDisabled={readOnly}
controller={{
defaultValue: "RSA_SHA256",
}}
options={[
"RSA_SHA1",
"RSA_SHA256",
"RSA_SHA256_MGF1",
"RSA_SHA512",
"RSA_SHA512_MGF1",
"DSA_SHA1",
]}
/>
<SelectControl
name="config.xmlSigKeyInfoKeyNameTransformer"
defaultValue={t("keyID")}
control={control}
render={({ field }) => (
<Select
toggleId="kc-samlSignatureKeyName"
onToggle={(isExpanded) =>
setSamlSignatureKeyNameDropdownOpen(isExpanded)
}
isOpen={samlSignatureKeyNameDropdownOpen}
onSelect={(_, value) => {
field.onChange(value.toString());
setSamlSignatureKeyNameDropdownOpen(false);
}}
selections={field.value}
variant={SelectVariant.single}
isDisabled={readOnly}
>
<SelectOption value="NONE" />
<SelectOption value={t("keyID")} isPlaceholder />
<SelectOption value={t("certSubject")} />
</Select>
)}
></Controller>
</FormGroup>
</>
)}
<SwitchField
field="config.wantAssertionsSigned"
label="wantAssertionsSigned"
isReadOnly={readOnly}
/>
<SwitchField
field="config.wantAssertionsEncrypted"
label="wantAssertionsEncrypted"
isReadOnly={readOnly}
/>
{wantAssertionsEncrypted === "true" && (
<FormGroup
label={t("encryptionAlgorithm")}
labelIcon={
<HelpItem
helpText={t("encryptionAlgorithmHelp")}
fieldLabelId="encryptionAlgorithm"
label={t("samlSignatureKeyName")}
labelIcon={t("samlSignatureKeyNameHelp")}
isDisabled={readOnly}
controller={{
defaultValue: t("keyID"),
}}
options={["NONE", t("keyID"), t("certSubject")]}
/>
}
fieldId="kc-encryptionAlgorithm"
>
<Controller
</>
)}
<DefaultSwitchControl
name="config.wantAssertionsSigned"
label={t("wantAssertionsSigned")}
isDisabled={readOnly}
stringify
/>
<DefaultSwitchControl
name="config.wantAssertionsEncrypted"
label={t("wantAssertionsEncrypted")}
isDisabled={readOnly}
stringify
/>
{wantAssertionsEncrypted === "true" && (
<SelectControl
name="config.encryptionAlgorithm"
defaultValue="RSA-OAEP"
control={control}
render={({ field }) => (
<Select
toggleId="kc-encryptionAlgorithm"
onToggle={(isExpanded) =>
setEncryptionAlgorithmDropdownOpen(isExpanded)
}
isOpen={encryptionAlgorithmDropdownOpen}
onSelect={(_, value) => {
field.onChange(value.toString());
setEncryptionAlgorithmDropdownOpen(false);
}}
selections={field.value}
variant={SelectVariant.single}
isDisabled={readOnly}
>
<SelectOption value="RSA-OAEP" />
<SelectOption value="RSA1_5" />
</Select>
)}
></Controller>
</FormGroup>
)}
label={t("encryptionAlgorithm")}
labelIcon={t("encryptionAlgorithmHelp")}
isDisabled={readOnly}
controller={{
defaultValue: "RSA-OAEP",
}}
options={["RSA-OAEP", "RSA1_5"]}
/>
)}
<SwitchField
field="config.forceAuthn"
label="forceAuthentication"
isReadOnly={readOnly}
/>
<SwitchField
field="config.validateSignature"
label="validateSignature"
isReadOnly={readOnly}
/>
{validateSignature === "true" && (
<>
<FormGroup
label={t("metadataDescriptorUrl")}
labelIcon={
<HelpItem
helpText={t("metadataDescriptorUrlHelp")}
fieldLabelId="metadataDescriptorUrl"
/>
}
isRequired={useMetadataDescriptorUrl === "true"}
validated={
errors.config?.metadataDescriptorUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
fieldId="metadataDescriptorUrl"
helperTextInvalid={t("required")}
>
<KeycloakTextInput
<DefaultSwitchControl
name="config.forceAuthn"
label={t("forceAuthentication")}
isDisabled={readOnly}
stringify
/>
<DefaultSwitchControl
name="config.validateSignature"
label={t("validateSignature")}
isDisabled={readOnly}
stringify
/>
{validateSignature === "true" && (
<>
<TextControl
name="config.metadataDescriptorUrl"
label={t("metadataDescriptorUrl")}
labelIcon={t("metadataDescriptorUrlHelp")}
type="url"
id="metadataDescriptorUrl"
data-testid="metadataDescriptorUrl"
isReadOnly={readOnly}
validated={
errors.config?.metadataDescriptorUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
{...register("config.metadataDescriptorUrl", {
required: useMetadataDescriptorUrl === "true",
})}
readOnly={readOnly}
rules={{
required: {
value: useMetadataDescriptorUrl === "true",
message: t("required"),
},
}}
/>
</FormGroup>
<SwitchField
field="config.useMetadataDescriptorUrl"
label="useMetadataDescriptorUrl"
data-testid="useMetadataDescriptorUrl"
isReadOnly={readOnly}
/>
{useMetadataDescriptorUrl !== "true" && (
<FormGroupField label="validatingX509Certs">
<KeycloakTextArea
id="validatingX509Certs"
data-testid="validatingX509Certs"
isReadOnly={readOnly}
{...register("config.signingCertificate")}
></KeycloakTextArea>
</FormGroupField>
)}
</>
)}
<SwitchField
field="config.signSpMetadata"
label="signServiceProviderMetadata"
data-testid="signServiceProviderMetadata"
isReadOnly={readOnly}
/>
<SwitchField
field="config.loginHint"
label="passSubject"
data-testid="passSubject"
isReadOnly={readOnly}
/>
<FormGroup
label={t("allowedClockSkew")}
labelIcon={
<HelpItem
helpText={t("allowedClockSkewHelp")}
fieldLabelId="allowedClockSkew"
/>
}
fieldId="allowedClockSkew"
helperTextInvalid={t("required")}
>
<Controller
<DefaultSwitchControl
name="config.useMetadataDescriptorUrl"
label={t("useMetadataDescriptorUrl")}
isDisabled={readOnly}
/>
{useMetadataDescriptorUrl !== "true" && (
<TextControl
name="config.signingCertificate"
label="validatingX509Certs"
readOnly={readOnly}
/>
)}
</>
)}
<DefaultSwitchControl
name="config.signSpMetadata"
label={t("signServiceProviderMetadata")}
isDisabled={readOnly}
stringify
/>
<DefaultSwitchControl
name="config.loginHint"
label={t("passSubject")}
isDisabled={readOnly}
stringify
/>
<NumberControl
name="config.allowedClockSkew"
defaultValue={0}
control={control}
render={({ field }) => {
const v = Number(field.value);
return (
<NumberInput
data-testid="allowedClockSkew"
inputName="allowedClockSkew"
min={0}
max={2147483}
value={v}
readOnly
onPlus={() => field.onChange(v + 1)}
onMinus={() => field.onChange(v - 1)}
onChange={(event) => {
const value = Number(
(event.target as HTMLInputElement).value,
);
field.onChange(value < 0 ? 0 : value);
}}
/>
);
}}
label={t("allowedClockSkew")}
labelIcon={t("allowedClockSkewHelp")}
controller={{ defaultValue: 0, rules: { min: 0, max: 2147483 } }}
isDisabled={readOnly}
/>
</FormGroup>
<FormGroup
label={t("attributeConsumingServiceIndex")}
labelIcon={
<HelpItem
helpText={t("attributeConsumingServiceIndexHelp")}
fieldLabelId="attributeConsumingServiceIndex"
/>
}
fieldId="attributeConsumingServiceIndex"
helperTextInvalid={t("required")}
>
<Controller
<NumberControl
name="config.attributeConsumingServiceIndex"
defaultValue={0}
control={control}
render={({ field }) => {
const v = Number(field.value);
return (
<NumberInput
data-testid="attributeConsumingServiceIndex"
inputName="attributeConsumingServiceIndex"
min={0}
max={2147483}
value={v}
readOnly
onPlus={() => field.onChange(v + 1)}
onMinus={() => field.onChange(v - 1)}
onChange={(event) => {
const value = Number(
(event.target as HTMLInputElement).value,
);
field.onChange(value < 0 ? 0 : value);
}}
/>
);
}}
label={t("attributeConsumingServiceIndex")}
labelIcon={t("attributeConsumingServiceIndexHelp")}
controller={{ defaultValue: 0, rules: { min: 0, max: 2147483 } }}
isDisabled={readOnly}
/>
</FormGroup>
<FormGroup
label={t("attributeConsumingServiceName")}
labelIcon={
<HelpItem
helpText={t("attributeConsumingServiceNameHelp")}
fieldLabelId="attributeConsumingServiceName"
/>
}
fieldId="attributeConsumingServiceName"
helperTextInvalid={t("required")}
>
<KeycloakTextInput
id="attributeConsumingServiceName"
data-testid="attributeConsumingServiceName"
isReadOnly={readOnly}
{...register("config.attributeConsumingServiceName")}
<TextControl
name="config.attributeConsumingServiceName"
label={t("attributeConsumingServiceName")}
labelIcon={t("attributeConsumingServiceNameHelp")}
readOnly={readOnly}
/>
</FormGroup>
</FormProvider>
</div>
);
};

View file

@ -73,7 +73,7 @@ export const SelectControl = <
selections={
typeof options[0] !== "string"
? (options as SelectControlOption[])
.filter((o) => value.includes(o.key))
.filter((o) => value === o.key)
.map((o) => o.value)
: value
}

View file

@ -12,7 +12,7 @@ import { FormLabel } from "./FormLabel";
export type SwitchControlProps<
T extends FieldValues,
P extends FieldPath<T> = FieldPath<T>,
> = Omit<SwitchProps, "name" | "defaultValue"> &
> = Omit<SwitchProps, "name" | "defaultValue" | "ref"> &
UseControllerProps<T, P> & {
name: string;
label?: string;
@ -45,10 +45,10 @@ export const SwitchControl = <
defaultValue={defaultValue}
render={({ field: { onChange, value } }) => (
<Switch
{...props}
id={props.name}
data-testid={props.name}
label={props.labelOn}
labelOff={props.labelOff}
isChecked={props.stringify ? value === "true" : value}
onChange={(checked, e) => {
const value = props.stringify ? checked.toString() : checked;