/* eslint-disable */ // @ts-nocheck import type { UserProfileConfig, UserProfileMetadata, } from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata"; import { isUserProfileError, setUserProfileServerError, useAlerts, useFetch, } from "../../shared/keycloak-ui-shared"; import { AlertVariant, ButtonVariant, DropdownItem, Label, PageSection, Tab, Tabs, TabTitleText, Tooltip, } from "../../shared/@patternfly/react-core"; import { InfoCircleIcon } from "../../shared/@patternfly/react-icons"; import { TFunction } from "i18next"; import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { useAdminClient } from "../admin-client"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeyValueType } from "../components/key-value-form/key-value-convert"; import { KeycloakSpinner } from "../../shared/keycloak-ui-shared"; import { RoutableTabs, useRoutableTab, } from "../components/routable-tabs/RoutableTabs"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAccess } from "../context/access/Access"; import { useRealm } from "../context/realm-context/RealmContext"; import { UserProfileProvider } from "../realm-settings/user-profile/UserProfileContext"; import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled"; import { useParams } from "../utils/useParams"; import { Organizations } from "./Organizations"; import { UserAttributes } from "./UserAttributes"; import { UserConsents } from "./UserConsents"; import { UserCredentials } from "./UserCredentials"; import { BruteForced, UserForm } from "./UserForm"; import { UserGroups } from "./UserGroups"; import { UserIdentityProviderLinks } from "./UserIdentityProviderLinks"; import { UserRoleMapping } from "./UserRoleMapping"; import { UserSessions } from "./UserSessions"; import { UserEvents } from "../events/UserEvents"; import { UserVerifiableCredentials } from "./UserVerifiableCredentials"; import { UserWorkflows } from "./UserWorkflows"; import { UIUserRepresentation, UserFormFields, filterManagedAttributes, toUserFormFields, toUserRepresentation, } from "./form-state"; import { UserParams, UserTab, toUser } from "./routes/User"; import { toUsers } from "./routes/Users"; import { isLightweightUser } from "./utils"; import { extractUserProfileErrorMessages } from "./utils/user-profile"; import "./user-section.css"; import { AdminEvents } from "../events/AdminEvents"; export default function EditUser() { const { adminClient } = useAdminClient(); const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const navigate = useNavigate(); const { hasAccess } = useAccess(); const { id } = useParams(); const { realm: realmName, realmRepresentation: realm } = useRealm(); // Validation of form fields is performed on server, thus we need to clear all errors before submit const clearAllErrorsBeforeSubmit = async (values: UserFormFields) => ({ values, errors: {}, }); const form = useForm({ mode: "onChange", resolver: clearAllErrorsBeforeSubmit, }); const [user, setUser] = useState(); const [bruteForced, setBruteForced] = useState(); const [isUnmanagedAttributesEnabled, setUnmanagedAttributesEnabled] = useState(); const [userProfileMetadata, setUserProfileMetadata] = useState(); const [refreshCount, setRefreshCount] = useState(0); const refresh = () => setRefreshCount((count) => count + 1); const lightweightUser = isLightweightUser(user?.id); const [upConfig, setUpConfig] = useState(); const [realmHasOrganizations, setRealmHasOrganizations] = useState(false); const isFeatureEnabled = useIsFeatureEnabled(); const showOrganizations = isFeatureEnabled(Feature.Organizations) && realm.organizationsEnabled; const showVerifiableCredentials = isFeatureEnabled(Feature.OpenId4VCI) && realm.verifiableCredentialsEnabled; const toTab = (tab: UserTab) => toUser({ realm: realmName, id: user?.id || "", tab, }); const [activeEventsTab, setActiveEventsTab] = useState("userEvents"); const settingsTab = useRoutableTab(toTab("settings")); const attributesTab = useRoutableTab(toTab("attributes")); const credentialsTab = useRoutableTab(toTab("credentials")); const roleMappingTab = useRoutableTab(toTab("role-mapping")); const groupsTab = useRoutableTab(toTab("groups")); const organizationsTab = useRoutableTab(toTab("organizations")); const consentsTab = useRoutableTab(toTab("consents")); const identityProviderLinksTab = useRoutableTab( toTab("identity-provider-links"), ); const sessionsTab = useRoutableTab(toTab("sessions")); const eventsTab = useRoutableTab(toTab("events")); const workflowsTab = useRoutableTab(toTab("workflows")); const verifiableCredentialsTab = useRoutableTab( toTab("verifiable-credentials"), ); useFetch( async () => Promise.all([ adminClient.users.findOne({ id: id!, userProfileMetadata: true, }) as UIUserRepresentation | undefined, adminClient.attackDetection.findOne({ id: id! }), adminClient.users.getUnmanagedAttributes({ id: id! }), adminClient.users.getProfile({ realm: realmName }), showOrganizations ? adminClient.organizations.find({ first: 0, max: 1 }) : [], ]), ([ userData, attackDetection, unmanagedAttributes, upConfig, organizations, ]) => { if (!userData || !attackDetection) { throw new Error(t("notFound")); } const { userProfileMetadata, ...user } = userData; setUserProfileMetadata(userProfileMetadata); user.unmanagedAttributes = unmanagedAttributes; user.attributes = filterManagedAttributes( user.attributes, unmanagedAttributes, ); if (upConfig.unmanagedAttributePolicy !== undefined) { setUnmanagedAttributesEnabled(true); } setUser(user); setUpConfig(upConfig); const isBruteForceProtected = realm.bruteForceProtected; const isLocked = isBruteForceProtected && attackDetection.disabled; setBruteForced({ isBruteForceProtected, isLocked }); setRealmHasOrganizations(organizations.length === 1); form.reset(toUserFormFields(user)); }, [refreshCount], ); const save = async (data: UserFormFields) => { try { await adminClient.users.update( { id: user!.id! }, toUserRepresentation(data), ); addAlert(t("userSaved"), AlertVariant.success); refresh(); } catch (error) { if (isUserProfileError(error)) { if ( isUnmanagedAttributesEnabled && Array.isArray(data.unmanagedAttributes) ) { const unmanagedAttributeErrors: object[] = new Array( data.unmanagedAttributes.length, ); let someUnmanagedAttributeError = false; setUserProfileServerError( error, (field, params) => { if (field.startsWith("attributes.")) { const attributeName = field.substring("attributes.".length); const unmanagedAttrs = data.unmanagedAttributes as KeyValueType[]; let isUnmanagedAttribute = false; unmanagedAttrs.forEach((attr, index) => { if (attr.key === attributeName) { unmanagedAttributeErrors[index] = params; someUnmanagedAttributeError = true; isUnmanagedAttribute = true; } }); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated in forEach above if (!isUnmanagedAttribute) { form.setError(field, params); } } else { form.setError(field, params); } }, ((key, param) => t(key as string, param as any)) as TFunction, ); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated in callback above if (someUnmanagedAttributeError) { form.setError( "unmanagedAttributes", unmanagedAttributeErrors as any, ); } } else { setUserProfileServerError(error, form.setError, (( key, param, ) => t(key as string, param as any)) as TFunction); } const errorMessage = extractUserProfileErrorMessages(error, t); addError("userNotSaved", errorMessage); } else { addError("userCreateError", error); } } }; const [toggleDisableDialog, DisableConfirm] = useConfirmDialog({ titleKey: "disableConfirmUserTitle", messageKey: "disableConfirmUser", continueButtonLabel: "disable", onConfirm: async () => { await save({ ...toUserFormFields(user!), enabled: false, }); }, }); const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "deleteConfirmUsers", messageKey: "deleteConfirmCurrentUser", continueButtonLabel: "delete", continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { try { if (lightweightUser) { await adminClient.users.logout({ id: user!.id! }); } else { await adminClient.users.del({ id: user!.id! }); } addAlert(t("userDeletedSuccess"), AlertVariant.success); navigate(toUsers({ realm: realmName })); } catch (error) { addError("userDeletedError", error); } }, }); const [toggleImpersonateDialog, ImpersonateConfirm] = useConfirmDialog({ titleKey: "impersonateConfirm", messageKey: "impersonateConfirmDialog", continueButtonLabel: "impersonate", onConfirm: async () => { try { const data = await adminClient.users.impersonation( { id: user!.id! }, { user: user!.id!, realm: realmName }, ); if (data.sameRealm) { window.location = data.redirect; } else { window.open(data.redirect, "_blank"); } } catch (error) { addError("impersonateError", error); } }, }); if (!user || !bruteForced) { return ; } return ( <> ), }, ] : [] } dropdownItems={[ toggleImpersonateDialog()} > {t("impersonate")} , toggleDeleteDialog()} > {t("delete")} , ]} onToggle={async (value) => { if (!value) { toggleDisableDialog(); } else { await save({ ...toUserFormFields(user), enabled: value, }); } }} isEnabled={user.enabled} /> {t("details")}} {...settingsTab} > {isUnmanagedAttributesEnabled && ( {t("attributes")}} {...attributesTab} > )} {t("credentials")}} {...credentialsTab} > {showVerifiableCredentials && ( {t("verifiableCredentials")} } {...verifiableCredentialsTab} > )} {t("roleMapping")}} {...roleMappingTab} > {hasAccess("query-groups") && ( {t("groups")}} {...groupsTab} > )} {showOrganizations && realmHasOrganizations && ( {t("organizations")}} {...organizationsTab} > )} {t("consents")}} {...consentsTab} > {t("identityProviderLinks")} } {...identityProviderLinksTab} > {t("sessions")}} {...sessionsTab} > {hasAccess("view-events") && ( {t("events")}} {...eventsTab} > setActiveEventsTab(key as string)} > {t("userEvents")}} > {t("adminEvents")}} > )} {isFeatureEnabled(Feature.Workflows) && ( {t("workflows")}} {...workflowsTab} > )} ); }