[FIX] Change password not working on some cases (#4312)

* create iTotp enum

* migrate change user profile to method

* Fix minor i18n issues

* Use saveUserProfileMethod from Services

* Catch cancel event from TwoFactor modal

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Gleidson Daniel Silva 2022-06-23 16:59:57 -03:00 committed by GitHub
parent bc09527d78
commit 303d127949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 11 deletions

5
app/definitions/ITotp.ts Normal file
View File

@ -0,0 +1,5 @@
export enum TwoFactorMethods {
TOTP = 'totp',
EMAIL = 'email',
PASSWORD = 'password'
}

View File

@ -71,6 +71,7 @@
"error-remove-last-owner": "This is the last owner. Please set a new owner before removing this one.", "error-remove-last-owner": "This is the last owner. Please set a new owner before removing this one.",
"error-role-in-use": "Cannot delete role because it's in use", "error-role-in-use": "Cannot delete role because it's in use",
"error-role-name-required": "Role name is required", "error-role-name-required": "Role name is required",
"error-password-same-as-current": "Entered password same as current password",
"error-the-field-is-required": "The field {{field}} is required.", "error-the-field-is-required": "The field {{field}} is required.",
"error-too-many-requests": "Error, too many requests. Please slow down. You must wait {{seconds}} seconds before trying again.", "error-too-many-requests": "Error, too many requests. Please slow down. You must wait {{seconds}} seconds before trying again.",
"error-user-is-not-activated": "User is not activated", "error-user-is-not-activated": "User is not activated",
@ -836,5 +837,6 @@
"Mark_as_unread": "Mark as unread", "Mark_as_unread": "Mark as unread",
"Mark_as_unread_Info": "Display room as unread when there are unread messages", "Mark_as_unread_Info": "Display room as unread when there are unread messages",
"Show_badge_for_mentions": "Show badge for mentions", "Show_badge_for_mentions": "Show badge for mentions",
"Show_badge_for_mentions_Info": "Display badge for direct mentions only" "Show_badge_for_mentions_Info": "Display badge for direct mentions only",
"totp-invalid": "Code or password invalid"
} }

View File

@ -69,6 +69,7 @@
"error-remove-last-owner": "Este é o último proprietário. Por favor, defina um novo proprietário antes de remover este.", "error-remove-last-owner": "Este é o último proprietário. Por favor, defina um novo proprietário antes de remover este.",
"error-role-in-use": "Não é possível remover o papel pois ele está em uso", "error-role-in-use": "Não é possível remover o papel pois ele está em uso",
"error-role-name-required": "Nome do papel é obrigatório", "error-role-name-required": "Nome do papel é obrigatório",
"error-password-same-as-current": "Senha digitada coincide com a senha atual",
"error-the-field-is-required": "O campo {{field}} é obrigatório.", "error-the-field-is-required": "O campo {{field}} é obrigatório.",
"error-too-many-requests": "Erro, muitas solicitações. Por favor, diminua a velocidade. Você deve esperar {{seconds}} segundos antes de tentar novamente.", "error-too-many-requests": "Erro, muitas solicitações. Por favor, diminua a velocidade. Você deve esperar {{seconds}} segundos antes de tentar novamente.",
"error-user-is-not-activated": "O usuário não está ativo", "error-user-is-not-activated": "O usuário não está ativo",
@ -743,5 +744,6 @@
"sending_email_confirmation": "enviando email de confirmação", "sending_email_confirmation": "enviando email de confirmação",
"Unsupported_format": "Formato não suportado", "Unsupported_format": "Formato não suportado",
"Downloaded_file": "Arquivo baixado", "Downloaded_file": "Arquivo baixado",
"Error_Download_file": "Erro ao baixar o arquivo" "Error_Download_file": "Erro ao baixar o arquivo",
"totp-invalid": "Código ou senha inválida"
} }

View File

@ -918,6 +918,15 @@ export function getUserInfo(userId: string) {
export const toggleFavorite = (roomId: string, favorite: boolean) => sdk.post('rooms.favorite', { roomId, favorite }); export const toggleFavorite = (roomId: string, favorite: boolean) => sdk.post('rooms.favorite', { roomId, favorite });
export const saveUserProfileMethod = (
params: IProfileParams,
customFields = {},
twoFactorOptions: {
twoFactorCode: string;
twoFactorMethod: string;
} | null
) => sdk.current.methodCall('saveUserProfile', params, customFields, twoFactorOptions);
export const deleteOwnAccount = (password: string, confirmRelinquish = false): any => export const deleteOwnAccount = (password: string, confirmRelinquish = false): any =>
// RC 0.67.0 // RC 0.67.0
sdk.post('users.deleteOwnAccount', { password, confirmRelinquish }); sdk.post('users.deleteOwnAccount', { password, confirmRelinquish });

View File

@ -41,6 +41,8 @@ import {
IProfileParams, IProfileParams,
IUser IUser
} from '../../definitions'; } from '../../definitions';
import { twoFactor } from '../../lib/services/twoFactor';
import { TwoFactorMethods } from '../../definitions/ITotp';
import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet'; import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet';
import { DeleteAccountActionSheetContent } from './components/DeleteAccountActionSheetContent'; import { DeleteAccountActionSheetContent } from './components/DeleteAccountActionSheetContent';
@ -71,6 +73,10 @@ interface IProfileViewState {
customFields: { customFields: {
[key: string | number]: string; [key: string | number]: string;
}; };
twoFactorCode: null | {
twoFactorCode: string;
twoFactorMethod: string;
};
} }
class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> { class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> {
@ -113,7 +119,8 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
url: '' url: ''
}, },
avatarSuggestions: {}, avatarSuggestions: {},
customFields: {} customFields: {},
twoFactorCode: null
}; };
async componentDidMount() { async componentDidMount() {
@ -194,14 +201,17 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
); );
}; };
handleError = (e: any, func: string, action: string) => { handleError = (e: any, _func: string, action: string) => {
if (e.data && e.data.error.includes('[error-too-many-requests]')) { if (e.data && e.data.error.includes('[error-too-many-requests]')) {
return showErrorAlert(e.data.error); return showErrorAlert(e.data.error);
} }
if (I18n.isTranslated(e.error)) {
return showErrorAlert(I18n.t(e.error));
}
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t(action) })); showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t(action) }));
}; };
submit = async () => { submit = async (): Promise<void> => {
Keyboard.dismiss(); Keyboard.dismiss();
if (!this.formIsChanged()) { if (!this.formIsChanged()) {
@ -210,7 +220,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
this.setState({ saving: true }); this.setState({ saving: true });
const { name, username, email, newPassword, currentPassword, avatar, customFields } = this.state; const { name, username, email, newPassword, currentPassword, avatar, customFields, twoFactorCode } = this.state;
const { user, dispatch } = this.props; const { user, dispatch } = this.props;
const params = {} as IProfileParams; const params = {} as IProfileParams;
@ -275,9 +285,16 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
} }
} }
const result = await Services.saveUserProfile(params, customFields); const twoFactorOptions = params.currentPassword
? {
twoFactorCode: params.currentPassword,
twoFactorMethod: TwoFactorMethods.PASSWORD
}
: null;
if (result.success) { const result = await Services.saveUserProfileMethod(params, customFields, twoFactorCode || twoFactorOptions);
if (result) {
logEvent(events.PROFILE_SAVE_CHANGES); logEvent(events.PROFILE_SAVE_CHANGES);
if (customFields) { if (customFields) {
dispatch(setUser({ customFields, ...params })); dispatch(setUser({ customFields, ...params }));
@ -287,10 +304,18 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
EventEmitter.emit(LISTENER, { message: I18n.t('Profile_saved_successfully') }); EventEmitter.emit(LISTENER, { message: I18n.t('Profile_saved_successfully') });
this.init(); this.init();
} }
this.setState({ saving: false }); this.setState({ saving: false, currentPassword: null, twoFactorCode: null });
} catch (e) { } catch (e: any) {
if (e?.error === 'totp-invalid' && e?.details.method !== TwoFactorMethods.PASSWORD) {
try {
const code = await twoFactor({ method: e?.details.method, invalid: e?.error === 'totp-invalid' && !!twoFactorCode });
return this.setState({ twoFactorCode: code }, () => this.submit());
} catch {
// cancelled twoFactor modal
}
}
logEvent(events.PROFILE_SAVE_CHANGES_F); logEvent(events.PROFILE_SAVE_CHANGES_F);
this.setState({ saving: false, currentPassword: null }); this.setState({ saving: false, currentPassword: null, twoFactorCode: null });
this.handleError(e, 'saveUserProfile', 'saving_profile'); this.handleError(e, 'saveUserProfile', 'saving_profile');
} }
}; };