/* eslint-disable */ // @ts-nocheck import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import { UserProfileAttributeMetadata, UserProfileMetadata, } from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import { FormErrorText, HelpItem, SwitchControl, TextControl, UserProfileFields, ContinueCancelModal, } from "../../shared/keycloak-ui-shared"; import { Alert, AlertVariant, Button, Chip, ChipGroup, FormGroup, InputGroup, InputGroupItem, Switch, TextInput, } from "../../shared/@patternfly/react-core"; import { TFunction } from "i18next"; import { useEffect, useState } from "react"; import { Controller, FormProvider, UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useAdminClient } from "../admin-client"; import { DefaultSwitchControl } from "../components/SwitchControl"; import { useAlerts } from "../../shared/keycloak-ui-shared"; import { FormAccess } from "../components/form/FormAccess"; import { GroupPickerDialog } from "../components/group/GroupPickerDialog"; import { useAccess } from "../context/access/Access"; import { useWhoAmI } from "../context/whoami/WhoAmI"; import { emailRegexPattern } from "../util"; import useFormatDate from "../utils/useFormatDate"; import { FederatedUserLink } from "./FederatedUserLink"; import { UserFormFields, toUserFormFields } from "./form-state"; import { toUsers } from "./routes/Users"; import { FixedButtonsGroup } from "../components/form/FixedButtonGroup"; import { RequiredActionMultiSelect } from "./user-credentials/RequiredActionMultiSelect"; import { useNavigate } from "react-router-dom"; import { CopyToClipboardButton } from "../components/copy-to-clipboard-button/CopyToClipboardButton"; import { GroupResourceContext } from "../context/group-resource/GroupResourceContext"; const TERMS_AND_CONDITIONS_ATTRIBUTE = "terms_and_conditions"; export type BruteForced = { isBruteForceProtected?: boolean; isLocked?: boolean; }; export type UserFormProps = { form: UseFormReturn; realm: RealmRepresentation; user?: UserRepresentation; bruteForce?: BruteForced; userProfileMetadata?: UserProfileMetadata; save: (user: UserFormFields) => void; refresh?: () => void; onGroupsUpdate?: (groups: GroupRepresentation[]) => void; }; export const UserForm = ({ form, realm, user, bruteForce: { isBruteForceProtected, isLocked } = { isBruteForceProtected: false, isLocked: false, }, userProfileMetadata, save, refresh, onGroupsUpdate, }: UserFormProps) => { const { adminClient } = useAdminClient(); const { t } = useTranslation(); const formatDate = useFormatDate(); const { addAlert, addError } = useAlerts(); const { hasAccess } = useAccess(); const isManager = hasAccess("manage-users"); const canViewFederationLink = hasAccess("view-realm"); const { whoAmI } = useWhoAmI(); const termsAndConditionsAcceptedDate = toTermsAndConditionsAcceptedDate( user?.attributes?.[TERMS_AND_CONDITIONS_ATTRIBUTE], ); const { handleSubmit, setValue, control, reset, formState } = form; const { errors } = formState; const [selectedGroups, setSelectedGroups] = useState( [], ); const [open, setOpen] = useState(false); const [locked, setLocked] = useState(isLocked); const navigate = useNavigate(); useEffect(() => { setValue("requiredActions", user?.requiredActions || []); }, [user, setValue]); const unLockUser = async () => { try { await adminClient.users.update({ id: user!.id! }, { enabled: true }); addAlert(t("unlockSuccess"), AlertVariant.success); if (refresh) { refresh(); } } catch (error) { addError("unlockError", error); } }; const deleteItem = (id: string) => { setSelectedGroups(selectedGroups.filter((item) => item.name !== id)); onGroupsUpdate?.(selectedGroups); }; const addChips = async (groups: GroupRepresentation[]): Promise => { setSelectedGroups([...selectedGroups!, ...groups]); onGroupsUpdate?.([...selectedGroups!, ...groups]); }; const addGroups = async (groups: GroupRepresentation[]): Promise => { const newGroups = groups; newGroups.forEach(async (group) => { try { await adminClient.users.addToGroup({ id: user!.id!, groupId: group.id!, }); addAlert(t("addedGroupMembership"), AlertVariant.success); } catch (error) { addError("addedGroupMembershipError", error); } }); }; const toggleModal = () => { setOpen(!open); }; const onFormReset = () => { if (user?.id) { reset(toUserFormFields(user)); } else { navigate(toUsers({ realm: realm.realm! })); } }; const allFieldsReadOnly = () => user?.userProfileMetadata?.attributes && !user.userProfileMetadata.attributes .map((a) => a.readOnly) .reduce((p, c) => p && c, true); const handleEmailVerificationReset = async () => { try { save( toUserFormFields({ ...user, requiredActions: user?.requiredActions?.filter( (action) => action !== "UPDATE_EMAIL", ), attributes: { ...user?.attributes, "kc.email.pending": "", }, }), ); if (refresh) { refresh(); } } catch (error) { addError("emailPendingVerificationUpdateError", error); } }; return ( {open && ( { if (user?.id) { await addGroups(groups || []); } else { await addChips(groups || []); } setOpen(false); }} onClose={() => setOpen(false)} filterGroups={selectedGroups} /> )} {user?.id && ( <> )} {user?.federationLink && canViewFederationLink && ( } > )} {userProfileMetadata ? ( <> {termsAndConditionsAcceptedDate && ( {formatDate(termsAndConditionsAcceptedDate)} )} {user?.attributes?.["kc.email.pending"] && ( {t("userNotYetConfirmedNewEmail", { email: user.attributes!["kc.email.pending"], })} {t("emailPendingVerificationActionMessage")} )} { return ( attribute.name !== "kc.email.pending" && (attribute.name !== TERMS_AND_CONDITIONS_ATTRIBUTE || !termsAndConditionsAcceptedDate) ); }, ), }} hideReadOnly={!user} supportedLocales={realm.supportedLocales || []} currentLocale={whoAmI.locale} t={ ((key: unknown, params) => t(key as string, params as any)) as TFunction } /> ) : ( <> {!realm.registrationEmailAsUsername && ( )} )} {isBruteForceProtected && ( } > { await unLockUser(); setLocked(value); }} isChecked={locked} isDisabled={!locked} label={t("on")} labelOff={t("off")} /> )} {!user?.id && ( } > ( {selectedGroups.map((currentChip) => ( deleteItem(currentChip.name!)} > {currentChip.path} ))} )} /> {errors.requiredActions && ( )} )} ); }; function toTermsAndConditionsAcceptedDate(value: unknown): Date | undefined { const timestamp = Number(Array.isArray(value) ? value[0] : value); if (!Number.isFinite(timestamp) || timestamp <= 0) { return undefined; } const date = new Date(timestamp * 1000); return Number.isNaN(date.getTime()) ? undefined : date; }