import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import { AlertVariant, ButtonVariant, DropdownItem, PageSection, Tab, TabTitleText, } from "@patternfly/react-core"; import { useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; import { useNavigate } from "react-router-dom-v5-compat"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAccess } from "../context/access/Access"; import { useAdminClient, useFetch } from "../context/auth/AdminClient"; import { useRealm } from "../context/realm-context/RealmContext"; import { toUser, UserParams } from "./routes/User"; import { toUsers } from "./routes/Users"; 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 { UserProfileProvider } from "../realm-settings/user-profile/UserProfileContext"; import "./user-section.css"; const UsersTabs = () => { const { t } = useTranslation("users"); const { addAlert, addError } = useAlerts(); const navigate = useNavigate(); const { realm } = useRealm(); const { hasAccess } = useAccess(); const { adminClient } = useAdminClient(); const userForm = useForm({ mode: "onChange" }); const { id } = useParams(); const [user, setUser] = useState(); const [bruteForced, setBruteForced] = useState(); const [addedGroups, setAddedGroups] = useState([]); const [refreshCount, setRefreshCount] = useState(0); const refresh = () => setRefreshCount((count) => count + 1); useFetch( async () => { if (id) { const user = await adminClient.users.findOne({ id }); if (!user) { throw new Error(t("common:notFound")); } const isBruteForceProtected = (await adminClient.realms.findOne({ realm, }))!.bruteForceProtected; const bruteForce = await adminClient.attackDetection.findOne({ id: user.id!, }); const isLocked: boolean = isBruteForceProtected && bruteForce && bruteForce.disabled; return { user, bruteForced: { isBruteForceProtected, isLocked } }; } return { user: undefined }; }, ({ user, bruteForced }) => { setUser(user); setBruteForced(bruteForced); user && setupForm(user); }, [user?.username, refreshCount] ); const setupForm = (user: UserRepresentation) => { userForm.reset(user); }; const updateGroups = (groups: GroupRepresentation[]) => { setAddedGroups(groups); }; const save = async (formUser: UserRepresentation) => { formUser.username = formUser.username?.trim(); try { if (id) { await adminClient.users.update( { id }, { ...formUser, attributes: { ...user?.attributes, ...formUser.attributes }, } ); addAlert(t("userSaved"), AlertVariant.success); refresh(); } else { const createdUser = await adminClient.users.create({ ...formUser, groups: addedGroups.map((group) => group.path!), }); addAlert(t("userCreated"), AlertVariant.success); navigate(toUser({ id: createdUser.id, realm, tab: "settings" })); } } catch (error) { addError("users:userCreateError", error); } }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "users:deleteConfirm", messageKey: "users:deleteConfirmCurrentUser", continueButtonLabel: "common:delete", continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { try { await adminClient.users.del({ id }); addAlert(t("userDeletedSuccess"), AlertVariant.success); navigate(toUsers({ realm })); } catch (error) { addError("users:userDeletedError", error); } }, }); const [toggleImpersonateDialog, ImpersonateConfirm] = useConfirmDialog({ titleKey: "users:impersonateConfirm", messageKey: "users:impersonateConfirmDialog", continueButtonLabel: "users:impersonate", onConfirm: async () => { try { const data = await adminClient.users.impersonation( { id }, { user: id, realm } ); if (data.sameRealm) { window.location = data.redirect; } else { window.open(data.redirect, "_blank"); } } catch (error) { addError("users:impersonateError", error); } }, }); if (id && !user) { return ; } return ( <> ( toggleImpersonateDialog()} > {t("impersonate")} , toggleDeleteDialog()} > {t("common:delete")} , ]} isEnabled={value} onToggle={(value) => { onChange(value); save(userForm.getValues()); }} /> )} /> {id && user && ( {t("common:details")}} > {bruteForced && ( )} {t("common:attributes")}} > {t("common:credentials")}} > {t("roleMapping")}} > {t("common:groups")}} > {t("consents")}} > {hasAccess("view-identity-providers") && ( {t("identityProviderLinks")} } > )} {t("sessions")}} > )} {!id && ( )} ); }; export default UsersTabs;