import { Box, Field, FieldError, FieldGroup, FieldHint, FieldLabel, FieldRow, PasswordInput, Button } from '@rocket.chat/fuselage'; import { PasswordVerifierList } from '@rocket.chat/ui-client'; import { useToastMessageDispatch, usePasswordPolicy } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; import { useEffect, useId } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; import { e2e } from '../../../lib/e2ee/rocketchat.e2e'; import { useE2EEState } from '../../room/hooks/useE2EEState'; const PASSPHRASE_POLICY = Object.freeze({ enabled: true, minLength: 30, mustContainAtLeastOneLowercase: true, mustContainAtLeastOneUppercase: true, mustContainAtLeastOneNumber: true, mustContainAtLeastOneSpecialCharacter: true, forbidRepeatingCharacters: false, }); const useKeysExist = () => { const state = useE2EEState(); return state === 'READY' || state === 'SAVE_PASSWORD'; }; const useValidatePassphrase = (passphrase: string) => { const validate = usePasswordPolicy(PASSPHRASE_POLICY); return validate(passphrase); }; const useChangeE2EPasswordMutation = () => { return useMutation({ mutationFn: async (newPassword: string) => { await e2e.changePassword(newPassword); }, }); }; const defaultValues = { passphrase: '', confirmationPassphrase: '', }; export const ChangePassphrase = () => { const { t } = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); const uniqueId = useId(); const passphraseId = `passphrase-${uniqueId}`; const passphraseHintId = `${passphraseId}-hint`; const passphraseErrorId = `${passphraseId}-error`; const confirmPassphraseId = `confirm-passphrase-${uniqueId}`; const confirmPassphraseErrorId = `${confirmPassphraseId}-error`; const passphraseVerifierId = `verifier-${uniqueId}`; const e2ePasswordExplanationId = `explanation-${uniqueId}`; const { watch, formState: { errors, isValid }, handleSubmit, reset, resetField, control, trigger, } = useForm({ defaultValues, mode: 'all', }); const { passphrase, confirmationPassphrase } = watch(); const { validations, valid } = useValidatePassphrase(passphrase); useEffect(() => { if (!valid) { resetField('confirmationPassphrase'); return; } if (confirmationPassphrase) { const validateConfirmation = async () => { await trigger('confirmationPassphrase'); }; void validateConfirmation(); } }, [valid, confirmationPassphrase, resetField, trigger]); const keysExist = useKeysExist(); const updatePassword = useChangeE2EPasswordMutation(); const handleSave = async ({ passphrase }: { passphrase: string }) => { try { await updatePassword.mutateAsync(passphrase); dispatchToastMessage({ type: 'success', message: t('Encryption_key_saved_successfully') }); reset(); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } }; return ( <> {t('Change_E2EE_password')} {t('New_E2EE_password')} (valid ? true : t('Password_must_meet_the_complexity_requirements')), }} render={({ field }) => ( )} /> {errors.passphrase && ( {errors.passphrase.message} )} {keysExist ? ( ) : ( To set a new password, first { e.preventDefault(); await e2e.decodePrivateKeyFlow(); }} > enter your current E2EE password. )} {valid && ( {t('Confirm_new_E2EE_password')} (passphrase !== value ? t('Passwords_do_not_match') : true), }} render={({ field }) => ( )} /> {errors.confirmationPassphrase && ( {errors.confirmationPassphrase.message} )} )} ); };