import React, { FunctionComponent, useMemo, useState } from "react"; import { AlertVariant, Button, ButtonVariant, Divider, Dropdown, DropdownItem, DropdownPosition, Form, FormGroup, KebabToggle, Modal, ModalVariant, Switch, Text, TextInput, TextVariants, ValidatedOptions, } from "@patternfly/react-core"; import { Table, TableBody, TableComposable, TableHeader, TableVariant, Tbody, Td, Th, Thead, Tr, } from "@patternfly/react-table"; import { PencilAltIcon, CheckIcon, TimesIcon } from "@patternfly/react-icons"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import { useTranslation } from "react-i18next"; import { useAlerts } from "../components/alert/Alerts"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { useAdminClient, useFetch } from "../context/auth/AdminClient"; import { useWhoAmI } from "../context/whoami/WhoAmI"; import { Controller, useForm, useWatch } from "react-hook-form"; import { PasswordInput } from "../components/password-input/PasswordInput"; import { HelpItem } from "../components/help-enabler/HelpItem"; import "./user-section.css"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import type CredentialRepresentation from "@keycloak/keycloak-admin-client/lib/defs/credentialRepresentation"; import { FormAccess } from "../components/form-access/FormAccess"; type UserCredentialsProps = { user: UserRepresentation; }; type CredentialsForm = { password: string; passwordConfirmation: string; temporaryPassword: boolean; }; const credFormDefaultValues: CredentialsForm = { password: "", passwordConfirmation: "", temporaryPassword: true, }; type DisplayDialogProps = { titleKey: string; onClose: () => void; }; type UserLabelForm = { userLabel: string; }; const userLabelDefaultValues: UserLabelForm = { userLabel: "", }; const DisplayDialog: FunctionComponent = ({ titleKey, onClose, children, }) => { const { t } = useTranslation("users"); return ( {children} ); }; export const UserCredentials = ({ user }: UserCredentialsProps) => { const { t } = useTranslation("users"); const { whoAmI } = useWhoAmI(); const { addAlert, addError } = useAlerts(); const [key, setKey] = useState(0); const refresh = () => setKey(key + 1); const [open, setOpen] = useState(false); const [openSaveConfirm, setOpenSaveConfirm] = useState(false); const [kebabOpen, setKebabOpen] = useState({ status: false, rowKey: "", }); const adminClient = useAdminClient(); const form = useForm({ defaultValues: credFormDefaultValues, }); const userLabelForm = useForm({ defaultValues: userLabelDefaultValues, }); const { control, errors, handleSubmit, register } = form; const { getValues: getValues1, handleSubmit: handleSubmit1, register: register1, } = userLabelForm; const [credentials, setCredentials] = useState(); const [userCredentials, setUserCredentials] = useState< CredentialRepresentation[] >([]); const [selectedCredential, setSelectedCredential] = useState({}); const [isResetPassword, setIsResetPassword] = useState(false); const [showData, setShowData] = useState(false); const [editedUserCredential, setEditedUserCredential] = useState({}); const [isUserLabelEdit, setIsUserLabelEdit] = useState<{ status: boolean; rowKey: string; }>(); useFetch( () => adminClient.users.getCredentials({ id: user.id! }), (credentials) => { setUserCredentials(credentials); }, [key] ); const passwordTypeFinder = userCredentials.find( (credential) => credential.type === "password" ); const passwordWatcher = useWatch({ control, name: "password", }); const passwordConfirmationWatcher = useWatch< CredentialsForm["passwordConfirmation"] >({ control, name: "passwordConfirmation", }); const isNotDisabled = passwordWatcher !== "" && passwordConfirmationWatcher !== ""; const toggleModal = () => { setOpen(!open); }; const toggleConfirmSaveModal = () => { setOpenSaveConfirm(!openSaveConfirm); }; const saveUserPassword = async () => { if (!credentials) { return; } const passwordsMatch = credentials.password === credentials.passwordConfirmation; if (!passwordsMatch) { addAlert( isResetPassword ? t("resetPasswordNotMatchError") : t("savePasswordNotMatchError"), AlertVariant.danger ); } else { try { await adminClient.users.resetPassword({ id: user.id!, credential: { temporary: credentials.temporaryPassword, type: "password", value: credentials.password, }, }); refresh(); addAlert( isResetPassword ? t("resetCredentialsSuccess") : t("savePasswordSuccess"), AlertVariant.success ); setIsResetPassword(false); setOpenSaveConfirm(false); } catch (error) { addError( isResetPassword ? "users:resetPasswordError" : "users:savePasswordError", error ); } } }; const resetPassword = () => { setIsResetPassword(true); setOpen(true); }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: t("deleteCredentialsConfirmTitle"), messageKey: t("deleteCredentialsConfirm"), continueButtonLabel: t("common:delete"), continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { try { await adminClient.users.deleteCredential({ id: user.id!, credentialId: selectedCredential.id!, }); addAlert(t("deleteCredentialsSuccess"), AlertVariant.success); setKey((key) => key + 1); } catch (error) { addError("users:deleteCredentialsError", error); } }, }); const rows = useMemo(() => { if (!selectedCredential.credentialData) { return []; } const credentialData = JSON.parse(selectedCredential.credentialData); const locale = whoAmI.getLocale(); return Object.entries(credentialData) .sort(([a], [b]) => a.localeCompare(b, locale)) .map<[string, string]>(([key, value]) => { if (typeof value === "string") { return [key, value]; } return [key, JSON.stringify(value)]; }); }, [selectedCredential.credentialData]); const saveUserLabel = async () => { const credentialToEdit = userCredentials.find( (credential) => credential.id === editedUserCredential.id ); const userLabelFormValue = getValues1(); if (!credentialToEdit) { return; } try { await adminClient.users.updateCredentialLabel( { id: user.id!, credentialId: credentialToEdit.id!, }, userLabelFormValue.userLabel || "" ); refresh(); addAlert(t("updateCredentialUserLabelSuccess"), AlertVariant.success); setEditedUserCredential({}); } catch (error) { addError("users:updateCredentialUserLabelError", error); } setIsUserLabelEdit({ status: false, rowKey: credentialToEdit.id!, }); }; return ( <> {open && ( { setIsResetPassword(false); setOpen(false); }} actions={[ , , ]} >
} fieldId="kc-temporaryPassword" > {" "} ( onChange(value)} isChecked={value} label={t("common:on")} labelOff={t("common:off")} /> )} >
)} {openSaveConfirm && ( setOpenSaveConfirm(false)} actions={[ , , ]} > {isResetPassword ? `${t("resetPasswordConfirmText")} ${user.username} ${t( "questionMark" )}` : `${t("setPasswordConfirmText")} ${user.username} ${t( "questionMark" )}`} )} {showData && Object.keys(selectedCredential).length !== 0 && ( { setShowData(false); setSelectedCredential({}); }} >
)} {userCredentials.length !== 0 && passwordTypeFinder === undefined && ( <> )} {userCredentials.length !== 0 ? ( {t("type")} {t("userLabel")} {t("data")} {userCredentials.map((credential) => ( <> {credential.type?.charAt(0).toUpperCase()! + credential.type?.slice(1)}
{isUserLabelEdit?.status && isUserLabelEdit.rowKey === credential.id ? ( <>
) : ( <> {credential.userLabel ?? ""}
{credential.type === "password" ? ( ) : ( )} setKebabOpen({ status, rowKey: credential.id!, }) } /> } isOpen={ kebabOpen.status && kebabOpen.rowKey === credential.id } onSelect={() => { setSelectedCredential(credential); }} dropdownItems={[ { toggleDeleteDialog(); setKebabOpen({ status: false, rowKey: credential.id!, }); }} > {t("deleteBtn")} , ]} /> ))}
) : ( )} ); };