import { ActionGroup, AlertVariant, Button, Checkbox, FormGroup, PageSection, Switch, TextInput, } from "@patternfly/react-core"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import React, { useState } from "react"; import { Controller, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useAlerts } from "../components/alert/Alerts"; import { FormAccess } from "../components/form-access/FormAccess"; import { HelpItem } from "../components/help-enabler/HelpItem"; import { FormPanel } from "../components/scroll-form/FormPanel"; import { useAdminClient } from "../context/auth/AdminClient"; import { useRealm } from "../context/realm-context/RealmContext"; import { useWhoAmI } from "../context/whoami/WhoAmI"; import { emailRegexPattern } from "../util"; import { AddUserEmailModal } from "./AddUserEmailModal"; import "./realm-settings-section.css"; type RealmSettingsEmailTabProps = { realm: RealmRepresentation; }; export type EmailRegistrationCallback = (registered: boolean) => void; export const RealmSettingsEmailTab = ({ realm: initialRealm, }: RealmSettingsEmailTabProps) => { const { t } = useTranslation("realm-settings"); const adminClient = useAdminClient(); const { realm: realmName } = useRealm(); const { addAlert, addError } = useAlerts(); const { whoAmI } = useWhoAmI(); const [realm, setRealm] = useState(initialRealm); const [callback, setCallback] = useState(); const { register, control, handleSubmit, errors, watch, reset: resetForm, getValues, } = useForm({ defaultValues: realm }); const reset = () => resetForm(realm); const watchFromValue = watch("smtpServer.from", ""); const watchHostValue = watch("smtpServer.host", ""); const authenticationEnabled = useWatch({ control, name: "smtpServer.authentication", defaultValue: "", }); const save = async (form: RealmRepresentation) => { try { const registered = await registerEmailIfNeeded(); if (!registered) { return; } const savedRealm = { ...realm, ...form }; await adminClient.realms.update({ realm: realmName }, savedRealm); setRealm(savedRealm); addAlert(t("saveSuccess"), AlertVariant.success); } catch (error) { addError("realm-settings:saveError", error); } }; const testConnection = async () => { const serverSettings = { ...getValues()["smtpServer"] }; // Code below uses defensive coding as the server configuration uses an ambiguous record type. if (typeof serverSettings.port === "string") { serverSettings.port = Number(serverSettings.port); } if (typeof serverSettings.ssl === "string") { serverSettings.ssl = serverSettings.ssl === true.toString(); } if (typeof serverSettings.starttls === "string") { serverSettings.starttls = serverSettings.starttls === true.toString(); } // For some reason the API wants a duplicate field for the authentication status. // Somebody thought this was a good idea, so here we are. if (serverSettings.authentication === true.toString()) { serverSettings.auth = true; } try { const registered = await registerEmailIfNeeded(); if (!registered) { return; } await adminClient.realms.testSMTPConnection( { realm: realm.realm! }, serverSettings ); addAlert(t("testConnectionSuccess"), AlertVariant.success); } catch (error) { addError("realm-settings:testConnectionError", error); } }; /** * Triggers the flow to register the user's email if the user does not yet have one configured, if successful resolves true, otherwise false. */ const registerEmailIfNeeded = async () => { const user = await adminClient.users.findOne({ id: whoAmI.getUserId() }); // A user should always be found, throw if it is not. if (!user) { throw new Error("Unable to find user."); } // User already has an e-mail associated with it, no need to register. if (user.email) { return true; } // User needs to register, show modal to do so. return new Promise((resolve) => { const callback: EmailRegistrationCallback = (registered) => { setCallback(undefined); resolve(registered); }; setCallback(() => callback); }); }; return ( <> {callback && } } > } > } > ( onChange("" + value)} /> )} /> ( onChange("" + value)} /> )} /> ( { onChange("" + value); }} /> )} /> {authenticationEnabled === "true" && ( <> } > )} ); };