diff --git a/src/user-federation/UserFederationLdapSettings.tsx b/src/user-federation/UserFederationLdapSettings.tsx index 337002f9ad..a6d4e2b369 100644 --- a/src/user-federation/UserFederationLdapSettings.tsx +++ b/src/user-federation/UserFederationLdapSettings.tsx @@ -1,7 +1,13 @@ -import { PageSection } from "@patternfly/react-core"; +import { + ActionGroup, + AlertVariant, + Button, + Form, + PageSection, +} from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import React from "react"; -import { ScrollForm } from "../components/scroll-form/ScrollForm"; +import React, { useEffect } from "react"; + import { LdapSettingsAdvanced } from "./ldap/LdapSettingsAdvanced"; import { LdapSettingsKerberosIntegration } from "./ldap/LdapSettingsKerberosIntegration"; import { LdapSettingsCache } from "./ldap/LdapSettingsCache"; @@ -9,9 +15,55 @@ import { LdapSettingsSynchronization } from "./ldap/LdapSettingsSynchronization" import { LdapSettingsGeneral } from "./ldap/LdapSettingsGeneral"; import { LdapSettingsConnection } from "./ldap/LdapSettingsConnection"; import { LdapSettingsSearching } from "./ldap/LdapSettingsSearching"; +import { ScrollForm } from "../components/scroll-form/ScrollForm"; + +import { useHistory, useParams } from "react-router-dom"; +import { useRealm } from "../context/realm-context/RealmContext"; +import { convertToFormValues } from "../util"; +import { useAlerts } from "../components/alert/Alerts"; +import { useAdminClient } from "../context/auth/AdminClient"; +import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; + +import { useForm } from "react-hook-form"; export const UserFederationLdapSettings = () => { const { t } = useTranslation("user-federation"); + const form = useForm(); + const history = useHistory(); + const adminClient = useAdminClient(); + const { realm } = useRealm(); + + const { id } = useParams<{ id: string }>(); + const { addAlert } = useAlerts(); + + useEffect(() => { + (async () => { + const fetchedComponent = await adminClient.components.findOne({ id }); + if (fetchedComponent) { + setupForm(fetchedComponent); + } + })(); + }, []); + + const setupForm = (component: ComponentRepresentation) => { + Object.entries(component).map((entry) => { + if (entry[0] === "config") { + convertToFormValues(entry[1], "config", form.setValue); + } else { + form.setValue(entry[0], entry[1]); + } + }); + }; + + const save = async (component: ComponentRepresentation) => { + try { + await adminClient.components.update({ id }, component); + setupForm(component as ComponentRepresentation); + addAlert(t("saveSuccess"), AlertVariant.success); + } catch (error) { + addAlert(`${t("saveError")} '${error}'`, AlertVariant.danger); + } + }; return ( <> @@ -27,27 +79,27 @@ export const UserFederationLdapSettings = () => { t("advancedSettings"), ]} > - {/* General settings */} - - - {/* Connection settings */} - - - {/* Searching and updating settings */} - - - {/* Synchronization settings */} - - - {/* Kerberos integration */} - - - {/* Cache settings */} - - - {/* Advanced settings */} - + + + + + + + +
+ + + + +
); diff --git a/src/user-federation/UserFederationLdapWizard.tsx b/src/user-federation/UserFederationLdapWizard.tsx index ad0976ca73..fca56d37c0 100644 --- a/src/user-federation/UserFederationLdapWizard.tsx +++ b/src/user-federation/UserFederationLdapWizard.tsx @@ -13,8 +13,12 @@ import { LdapSettingsKerberosIntegration } from "./ldap/LdapSettingsKerberosInte import { LdapSettingsCache } from "./ldap/LdapSettingsCache"; import { LdapSettingsAdvanced } from "./ldap/LdapSettingsAdvanced"; import { useTranslation } from "react-i18next"; +import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; + +import { useForm } from "react-hook-form"; export const UserFederationLdapWizard = () => { + const form = useForm(); const { t } = useTranslation("user-federation"); const steps = [ @@ -22,21 +26,33 @@ export const UserFederationLdapWizard = () => { name: t("requiredSettings"), id: "ldapRequiredSettingsStep", component: ( - + ), }, { name: t("connectionAndAuthenticationSettings"), id: "ldapConnectionSettingsStep", component: ( - + ), }, { name: t("ldapSearchingAndUpdatingSettings"), id: "ldapSearchingSettingsStep", component: ( - + ), }, { @@ -44,6 +60,7 @@ export const UserFederationLdapWizard = () => { id: "ldapSynchronizationSettingsStep", component: ( @@ -54,6 +71,7 @@ export const UserFederationLdapWizard = () => { id: "ldapKerberosIntegrationSettingsStep", component: ( @@ -63,14 +81,22 @@ export const UserFederationLdapWizard = () => { name: t("cacheSettings"), id: "ldapCacheSettingsStep", component: ( - + ), }, { name: t("advancedSettings"), id: "ldapAdvancedSettingsStep", component: ( - + ), }, ]; diff --git a/src/user-federation/help.json b/src/user-federation/help.json index 81f242ba5d..74a7ef39dc 100644 --- a/src/user-federation/help.json +++ b/src/user-federation/help.json @@ -9,7 +9,7 @@ "ldapConnectionAndAuthorizationSettingsDescription": "This section contains options related to the configuration of the connection to the LDAP server. It also contains options related to authentication of the LDAP connection to the LDAP server.", "consoleDisplayConnectionUrlHelp": "Connection URL to your LDAP server", - "enableStarttlsHelp": "Encrypts the connection to LDAP using STARTTLS, which will disable connection pooling", + "enableStartTlsHelp": "Encrypts the connection to LDAP using STARTTLS, which will disable connection pooling", "useTruststoreSpiHelp": "Specifies whether LDAP connection will use the Truststore SPI with the truststore configured in standalone.xml/domain.sml. 'Always' means that it will always use it. 'Never' means that it will not use it. 'Only for ldaps' means that it will use it if your connection URL use ldaps. Note that even if standalone.xml/domain.xml is not configured, the default java cacerts or certificate specified by 'javax.net.ssl.trustStore' property will be used.", "connectionPoolingHelp": "Determines if Keycloak should use connection pooling for accessing LDAP server.", "connectionTimeoutHelp": "LDAP connection timeout in milliseconds", @@ -30,10 +30,12 @@ "paginationHelp": "Whether the LDAP server supports pagination", "ldapSynchronizationSettingsDescription": "This section contains options related to synchronization of users from LDAP to the Keycloak database.", - "importUsersHelp": "Import users", + "importUsersHelp": "If true, LDAP users will be imported into the Keycloak DB and synced by the configured sync policies.", "batchSizeHelp": "Count of LDAP users to be imported from LDAP to Keycloak within a single transaction", "periodicFullSyncHelp": "Whether periodic full synchronization of LDAP users to Keycloak should be enabled or not", + "fullSyncPeriodHelp": "Period for full synchronization in seconds", "periodicChangedUsersSyncHelp": "Whether periodic synchronization of changed or newly created LDAP users to Keycloak should be enabled or not", + "changedUsersSyncHelp": "Period for synchronization of changed or newly created LDAP users in seconds", "ldapKerberosSettingsDescription": "This section contains options useful for the Kerberos integration. This is used only when the LDAP server is used together with Kerberos/SPNEGO for user authentication.", "allowKerberosAuthenticationHelp": "Enable/disable HTTP authentication of users with SPNEGO/Kerberos tokens. The data about authenticated users will be provisioned from this LDAP server.", diff --git a/src/user-federation/ldap/LdapSettingsAdvanced.tsx b/src/user-federation/ldap/LdapSettingsAdvanced.tsx index f18d0e7cfb..139d174e3a 100644 --- a/src/user-federation/ldap/LdapSettingsAdvanced.tsx +++ b/src/user-federation/ldap/LdapSettingsAdvanced.tsx @@ -1,49 +1,24 @@ import { FormGroup, Switch } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import React, { useEffect } from "react"; +import React from "react"; import { HelpItem } from "../../components/help-enabler/HelpItem"; -import { useForm, Controller } from "react-hook-form"; -import { convertToFormValues } from "../../util"; -import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; +import { UseFormMethods, Controller } from "react-hook-form"; import { FormAccess } from "../../components/form-access/FormAccess"; -import { - useAdminClient, - asyncStateFetch, -} from "../../context/auth/AdminClient"; -import { useParams } from "react-router-dom"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; export type LdapSettingsAdvancedProps = { + form: UseFormMethods; showSectionHeading?: boolean; showSectionDescription?: boolean; }; export const LdapSettingsAdvanced = ({ + form, showSectionHeading = false, showSectionDescription = false, }: LdapSettingsAdvancedProps) => { const { t } = useTranslation("user-federation"); const helpText = useTranslation("user-federation-help").t; - const adminClient = useAdminClient(); - const { control, setValue } = useForm(); - const { id } = useParams<{ id: string }>(); - - const setupForm = (component: ComponentRepresentation) => { - Object.entries(component).map((entry) => { - if (entry[0] === "config") { - convertToFormValues(entry[1], "config", setValue); - } else { - setValue(entry[0], entry[1]); - } - }); - }; - - useEffect(() => { - return asyncStateFetch( - () => adminClient.components.findOne({ id }), - (fetchedComponent) => setupForm(fetchedComponent) - ); - }, []); return ( <> @@ -70,14 +45,14 @@ export const LdapSettingsAdvanced = ({ > ( onChange([`${value}`])} + isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} /> @@ -99,14 +74,14 @@ export const LdapSettingsAdvanced = ({ > ( onChange([`${value}`])} + isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} /> @@ -128,14 +103,14 @@ export const LdapSettingsAdvanced = ({ > ( onChange([`${value}`])} + isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} /> diff --git a/src/user-federation/ldap/LdapSettingsCache.tsx b/src/user-federation/ldap/LdapSettingsCache.tsx index e85cf52538..77820de2aa 100644 --- a/src/user-federation/ldap/LdapSettingsCache.tsx +++ b/src/user-federation/ldap/LdapSettingsCache.tsx @@ -3,84 +3,30 @@ import { Select, SelectOption, SelectVariant, - Text, - TextContent, TextInput, - Title, } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { HelpItem } from "../../components/help-enabler/HelpItem"; -import { useForm, Controller } from "react-hook-form"; -import { convertToFormValues } from "../../util"; -import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; +import { UseFormMethods, useWatch, Controller } from "react-hook-form"; import { FormAccess } from "../../components/form-access/FormAccess"; -import { useAdminClient } from "../../context/auth/AdminClient"; -import { useParams } from "react-router-dom"; +import _ from "lodash"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; export type LdapSettingsCacheProps = { + form: UseFormMethods; showSectionHeading?: boolean; showSectionDescription?: boolean; }; export const LdapSettingsCache = ({ + form, showSectionHeading = false, showSectionDescription = false, }: LdapSettingsCacheProps) => { const { t } = useTranslation("user-federation"); const helpText = useTranslation("user-federation-help").t; - const adminClient = useAdminClient(); - const { control, setValue, register } = useForm(); - const { id } = useParams<{ id: string }>(); - - const convertToDays = (num: string) => { - switch (num) { - case "1": - return t("common:Sunday"); - case "2": - return t("common:Monday"); - case "3": - return t("common:Tuesday"); - case "4": - return t("common:Wednesday"); - case "5": - return t("common:Thursday"); - case "6": - return t("common:Friday"); - case "7": - return t("common:Saturday"); - default: - return t("common:selectOne"); - } - }; - - const setupForm = (component: ComponentRepresentation) => { - Object.entries(component).map((entry) => { - if (entry[0] === "config") { - convertToFormValues(entry[1], "config", setValue); - if (entry[1].evictionDay) { - setValue( - "config.evictionDay", - convertToDays(entry[1].evictionDay[0]) - ); - } - } else { - setValue(entry[0], entry[1]); - } - }); - }; - - useEffect(() => { - (async () => { - const fetchedComponent = await adminClient.components.findOne({ id }); - if (fetchedComponent) { - setupForm(fetchedComponent); - } - })(); - }, []); - const [isCachePolicyDropdownOpen, setIsCachePolicyDropdownOpen] = useState( false ); @@ -89,6 +35,11 @@ export const LdapSettingsCache = ({ false ); + const cachePolicyType = useWatch({ + control: form.control, + name: "config.cachePolicy", + }); + const [ isEvictionMinuteDropdownOpen, setIsEvictionMinuteDropdownOpen, @@ -98,18 +49,16 @@ export const LdapSettingsCache = ({ false ); - const hourOptions = [ - , - ]; - for (let index = 1; index <= 24; index++) { - hourOptions.push(); + const hourOptions = []; + for (let index = 2; index <= 24; index++) { + hourOptions.push(); } const minuteOptions = [ - , + , ]; - for (let index = 1; index <= 60; index++) { - minuteOptions.push(); + for (let index = 2; index <= 60; index++) { + minuteOptions.push(); } return ( @@ -121,8 +70,6 @@ export const LdapSettingsCache = ({ showDescription={showSectionDescription} /> )} - - {/* Cache settings */} ( )} > - - {/* TODO: Field shows only if cache policy is EVICT_WEEKLY */} - - } - fieldId="kc-eviction-day" - > - ( - - )} - > - - - {/* TODO: Field shows only if cache policy is EVICT_WEEKLY or EVICT_DAILY */} - {/* TODO: Investigate whether this should be a number field instead of a dropdown/text field */} - - } - fieldId="kc-eviction-hour" - > - ( - - )} - > - - - {/* TODO: Field shows only if cache policy is EVICT_WEEKLY or EVICT_DAILY */} - {/* TODO: Investigate whether this should be a number field instead of a dropdown/text field */} - - } - fieldId="kc-eviction-minute" - > - ( - - )} - > - - - {/* TODO: Field shows only if cache policy is MAX_LIFESPAN */} - - } - fieldId="kc-max-lifespan" - > - + } isRequired - type="text" - id="kc-max-lifespan" - name="config.maxLifespan" - ref={register} - /> - + fieldId="kc-eviction-day" + > + ( + + )} + > + + ) : ( + <> + )} + {_.isEqual(cachePolicyType, ["EVICT_DAILY"]) || + _.isEqual(cachePolicyType, ["EVICT_WEEKLY"]) ? ( + <> + + } + isRequired + fieldId="kc-eviction-hour" + > + ( + + )} + > + + + } + isRequired + fieldId="kc-eviction-minute" + > + ( + + )} + > + + + ) : ( + <> + )} + {_.isEqual(cachePolicyType, ["MAX_LIFESPAN"]) ? ( + + } + fieldId="kc-max-lifespan" + > + + + ) : ( + <> + )} ); diff --git a/src/user-federation/ldap/LdapSettingsConnection.tsx b/src/user-federation/ldap/LdapSettingsConnection.tsx index 36b368621d..a3cb1ddb3d 100644 --- a/src/user-federation/ldap/LdapSettingsConnection.tsx +++ b/src/user-federation/ldap/LdapSettingsConnection.tsx @@ -9,46 +9,26 @@ import { TextInput, } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { HelpItem } from "../../components/help-enabler/HelpItem"; -import { Controller, useForm } from "react-hook-form"; -import { convertToFormValues } from "../../util"; -import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; +import { Controller, UseFormMethods } from "react-hook-form"; import { EyeIcon } from "@patternfly/react-icons"; import { FormAccess } from "../../components/form-access/FormAccess"; -import { - useAdminClient, - asyncStateFetch, -} from "../../context/auth/AdminClient"; -import { useParams } from "react-router-dom"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; export type LdapSettingsConnectionProps = { + form: UseFormMethods; showSectionHeading?: boolean; showSectionDescription?: boolean; }; export const LdapSettingsConnection = ({ + form, showSectionHeading = false, showSectionDescription = false, }: LdapSettingsConnectionProps) => { const { t } = useTranslation("user-federation"); const helpText = useTranslation("user-federation-help").t; - const adminClient = useAdminClient(); - const { register, control, setValue } = useForm(); - const { id } = useParams<{ id: string }>(); - - const convertTruststoreSpiValues = (truststoreValue: string) => { - switch (truststoreValue) { - case "always": - return `${t("always")}`; - case "never": - return `${t("never")}`; - case "ldapsOnly": - default: - return `${t("onlyLdaps")}`; - } - }; const [ isTruststoreSpiDropdownOpen, @@ -57,29 +37,6 @@ export const LdapSettingsConnection = ({ const [isBindTypeDropdownOpen, setIsBindTypeDropdownOpen] = useState(false); - const setupForm = (component: ComponentRepresentation) => { - Object.entries(component).map((entry) => { - if (entry[0] === "config") { - convertToFormValues(entry[1], "config", setValue); - if (entry[1].useTruststoreSpi) { - setValue( - "config.useTruststoreSpi", - convertTruststoreSpiValues(entry[1].useTruststoreSpi[0]) - ); - } - } else { - setValue(entry[0], entry[1]); - } - }); - }; - - useEffect(() => { - return asyncStateFetch( - () => adminClient.components.findOne({ id }), - (fetchedComponent) => setupForm(fetchedComponent) - ); - }, []); - return ( <> {showSectionHeading && ( @@ -91,7 +48,6 @@ export const LdapSettingsConnection = ({ showDescription={showSectionDescription} /> )} - + {form.errors.config && + form.errors.config.connectionUrl && + form.errors.config.connectionUrl[0] && ( +
+ {form.errors.config.connectionUrl[0].message} +
+ )}
( onChange([`${value}`])} + isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} /> @@ -154,9 +122,9 @@ export const LdapSettingsConnection = ({ fieldId="kc-use-truststore-spi" > ( )} > @@ -193,12 +167,12 @@ export const LdapSettingsConnection = ({ ( onChange([`${value}`])} isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} @@ -220,8 +194,8 @@ export const LdapSettingsConnection = ({ ( - - - - - - + + Active Directory + + + Red Hat Directory Server + + + Tivoli + + + Novell eDirectory + + + Other + )} > diff --git a/src/user-federation/ldap/LdapSettingsKerberosIntegration.tsx b/src/user-federation/ldap/LdapSettingsKerberosIntegration.tsx index d4bc5116a5..0595a96f51 100644 --- a/src/user-federation/ldap/LdapSettingsKerberosIntegration.tsx +++ b/src/user-federation/ldap/LdapSettingsKerberosIntegration.tsx @@ -1,51 +1,25 @@ import { FormGroup, Switch } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import React, { useEffect } from "react"; +import React from "react"; import { HelpItem } from "../../components/help-enabler/HelpItem"; -import { useForm, Controller } from "react-hook-form"; -import { convertToFormValues } from "../../util"; -import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; +import { UseFormMethods, Controller } from "react-hook-form"; import { FormAccess } from "../../components/form-access/FormAccess"; -import { - useAdminClient, - asyncStateFetch, -} from "../../context/auth/AdminClient"; -import { useParams } from "react-router-dom"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; export type LdapSettingsKerberosIntegrationProps = { + form: UseFormMethods; showSectionHeading?: boolean; showSectionDescription?: boolean; }; export const LdapSettingsKerberosIntegration = ({ + form, showSectionHeading = false, showSectionDescription = false, }: LdapSettingsKerberosIntegrationProps) => { const { t } = useTranslation("user-federation"); const helpText = useTranslation("user-federation-help").t; - const adminClient = useAdminClient(); - const { control, setValue } = useForm(); - const { id } = useParams<{ id: string }>(); - - const setupForm = (component: ComponentRepresentation) => { - Object.entries(component).map((entry) => { - if (entry[0] === "config") { - convertToFormValues(entry[1], "config", setValue); - } else { - setValue(entry[0], entry[1]); - } - }); - }; - - useEffect(() => { - return asyncStateFetch( - () => adminClient.components.findOne({ id }), - (fetchedComponent) => setupForm(fetchedComponent) - ); - }, []); - return ( <> {showSectionHeading && ( @@ -72,12 +46,12 @@ export const LdapSettingsKerberosIntegration = ({ ( onChange([`${value}`])} isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} @@ -100,12 +74,12 @@ export const LdapSettingsKerberosIntegration = ({ ( onChange([`${value}`])} isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} diff --git a/src/user-federation/ldap/LdapSettingsSearching.tsx b/src/user-federation/ldap/LdapSettingsSearching.tsx index 28c3c7a33c..acbc51ed7f 100644 --- a/src/user-federation/ldap/LdapSettingsSearching.tsx +++ b/src/user-federation/ldap/LdapSettingsSearching.tsx @@ -7,70 +7,30 @@ import { TextInput, } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { HelpItem } from "../../components/help-enabler/HelpItem"; -import { useForm, Controller } from "react-hook-form"; -import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; +import { UseFormMethods, Controller } from "react-hook-form"; import { FormAccess } from "../../components/form-access/FormAccess"; -import { - useAdminClient, - asyncStateFetch, -} from "../../context/auth/AdminClient"; -import { useParams } from "react-router-dom"; -import { convertToFormValues } from "../../util"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; export type LdapSettingsSearchingProps = { + form: UseFormMethods; showSectionHeading?: boolean; showSectionDescription?: boolean; }; export const LdapSettingsSearching = ({ + form, showSectionHeading = false, showSectionDescription = false, }: LdapSettingsSearchingProps) => { const { t } = useTranslation("user-federation"); - const adminClient = useAdminClient(); const helpText = useTranslation("user-federation-help").t; - const [isEditModeDropdownOpen, setIsEditModeDropdownOpen] = useState(false); - const { id } = useParams<{ id: string }>(); + const [isSearchScopeDropdownOpen, setIsSearchScopeDropdownOpen] = useState( false ); - const { register, control, setValue } = useForm(); - - const convertSearchScopes = (scope: string) => { - switch (scope) { - case "1": - default: - return `${t("oneLevel")}`; - case "2": - return `${t("subtree")}`; - } - }; - - const setupForm = (component: ComponentRepresentation) => { - Object.entries(component).map((entry) => { - if (entry[0] === "config") { - convertToFormValues(entry[1], "config", setValue); - if (entry[1].searchScope) { - setValue( - "config.searchScope", - convertSearchScopes(entry[1].searchScope[0]) - ); - } - } else { - setValue(entry[0], entry[1]); - } - }); - }; - - useEffect(() => { - return asyncStateFetch( - () => adminClient.components.findOne({ id }), - (fetchedComponent) => setupForm(fetchedComponent) - ); - }, []); + const [isEditModeDropdownOpen, setIsEditModeDropdownOpen] = useState(false); return ( <> @@ -95,9 +55,9 @@ export const LdapSettingsSearching = ({ fieldId="kc-edit-mode" > ( @@ -141,9 +97,21 @@ export const LdapSettingsSearching = ({ isRequired type="text" id="kc-console-users-dn" - name="config.usersDn" - ref={register} + name="config.usersDn[0]" + ref={form.register({ + required: { + value: true, + message: `${t("validateUsersDn")}`, + }, + })} /> + {form.errors.config && + form.errors.config.usersDn && + form.errors.config.usersDn[0] && ( +
+ {form.errors.config.usersDn[0].message} +
+ )}
+ {form.errors.config && + form.errors.config.usernameLDAPAttribute && + form.errors.config.usernameLDAPAttribute[0] && ( +
+ {form.errors.config.usernameLDAPAttribute[0].message} +
+ )}
+ {form.errors.config && + form.errors.config.rdnLDAPAttribute && + form.errors.config.rdnLDAPAttribute[0] && ( +
+ {form.errors.config.rdnLDAPAttribute[0].message} +
+ )}
+ {form.errors.config && + form.errors.config.uuidLDAPAttribute && + form.errors.config.uuidLDAPAttribute[0] && ( +
+ {form.errors.config.uuidLDAPAttribute[0].message} +
+ )}
+ {form.errors.config && + form.errors.config.userObjectClasses && + form.errors.config.userObjectClasses[0] && ( +
+ {form.errors.config.userObjectClasses[0].message} +
+ )}
+ ( )} > @@ -298,8 +314,8 @@ export const LdapSettingsSearching = ({ ( onChange([`${value}`])} + isChecked={value[0] === "true"} label={t("common:on")} labelOff={t("common:off")} /> diff --git a/src/user-federation/ldap/LdapSettingsSynchronization.tsx b/src/user-federation/ldap/LdapSettingsSynchronization.tsx index 7797a1e4cf..7b408f8035 100644 --- a/src/user-federation/ldap/LdapSettingsSynchronization.tsx +++ b/src/user-federation/ldap/LdapSettingsSynchronization.tsx @@ -1,49 +1,24 @@ import { FormGroup, Switch, TextInput } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import React, { useEffect } from "react"; +import React from "react"; import { HelpItem } from "../../components/help-enabler/HelpItem"; -import { useForm, Controller } from "react-hook-form"; -import { convertToFormValues } from "../../util"; -import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; +import { UseFormMethods, Controller } from "react-hook-form"; import { FormAccess } from "../../components/form-access/FormAccess"; -import { - useAdminClient, - asyncStateFetch, -} from "../../context/auth/AdminClient"; -import { useParams } from "react-router-dom"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; export type LdapSettingsSynchronizationProps = { + form: UseFormMethods; showSectionHeading?: boolean; showSectionDescription?: boolean; }; export const LdapSettingsSynchronization = ({ + form, showSectionHeading = false, showSectionDescription = false, }: LdapSettingsSynchronizationProps) => { const { t } = useTranslation("user-federation"); const helpText = useTranslation("user-federation-help").t; - const adminClient = useAdminClient(); - const { register, control, setValue } = useForm(); - const { id } = useParams<{ id: string }>(); - - const setupForm = (component: ComponentRepresentation) => { - Object.entries(component).map((entry) => { - if (entry[0] === "config") { - convertToFormValues(entry[1], "config", setValue); - } else { - setValue(entry[0], entry[1]); - } - }); - }; - - useEffect(() => { - return asyncStateFetch( - () => adminClient.components.findOne({ id }), - (fetchedComponent) => setupForm(fetchedComponent) - ); - }, []); return ( <> @@ -54,7 +29,6 @@ export const LdapSettingsSynchronization = ({ showDescription={showSectionDescription} /> )} - ( onChange([`${value}`])} isChecked={value[0] === "true"} isDisabled={false} - onChange={onChange} /> )} >
@@ -99,46 +73,50 @@ export const LdapSettingsSynchronization = ({ + + {/* Enter -1 to switch off, otherwise enter value */} } - fieldId="kc-periodic-full-sync" + fieldId="kc-full-sync-period" > + + {/* Enter -1 to switch off, otherwise enter value */} } - fieldId="kc-periodic-changed-users-sync" + fieldId="kc-changed-users-sync-period" hasNoPaddingTop > diff --git a/src/user-federation/messages.json b/src/user-federation/messages.json index 8d01978db2..aad0e6ef8e 100644 --- a/src/user-federation/messages.json +++ b/src/user-federation/messages.json @@ -46,7 +46,9 @@ "importUsers": "Import users", "batchSize": "Batch size", "periodicFullSync": "Periodic full sync", + "fullSyncPeriod": "Full sync period", "periodicChangedUsersSync": "Periodic changed users sync", + "changedUsersSyncPeriod": "Changed users sync period", "kerberosIntegration": "Kerberos integration", "allowKerberosAuthentication": "Allow Kerberos authentication", @@ -92,6 +94,13 @@ "validateRealm":"You must enter a realm", "validateServerPrincipal":"You must enter a server principal", "validateKeyTab": "You must enter a key tab", + "validateConnectionUrl": "You must enter a connection URL", + "validateBindCredentials": "You must enter bind credentials", + "validateUuidLDAPAttribute": "You must enter a UUID LDAP attribute", + "validateUserObjectClasses": "You must enter one or more user object classes", + "validateUsersDn": "You must enter users DN", + "validateUsernameLDAPAttribute": "You must enter a username LDAP attribute", + "validateRdnLdapAttribute": "You must enter an RDN LDAP attribute", "id": "ID", "mapperType": "Mapper type",