Fix the password length limit when not setting a new password
Relates #1446
This commit is contained in:
parent
eadfbc9f6d
commit
43ec14e741
|
@ -1915,7 +1915,7 @@
|
||||||
"message": "Failed to set password"
|
"message": "Failed to set password"
|
||||||
},
|
},
|
||||||
"passwordLengthError": {
|
"passwordLengthError": {
|
||||||
"message": "Password must be between 6 and 50 characters long",
|
"message": "Password must be between 6 and 64 characters long",
|
||||||
"description": "Error string shown to the user when password doesn't meet length criteria"
|
"description": "Error string shown to the user when password doesn't meet length criteria"
|
||||||
},
|
},
|
||||||
"passwordTypeError": {
|
"passwordTypeError": {
|
||||||
|
|
|
@ -1698,7 +1698,7 @@
|
||||||
"message": "Échec de la définition du mot de passe"
|
"message": "Échec de la définition du mot de passe"
|
||||||
},
|
},
|
||||||
"passwordLengthError": {
|
"passwordLengthError": {
|
||||||
"message": "Le mot de passe doit avoir une longueur comprise entre 6 et 50 caractères",
|
"message": "Le mot de passe doit avoir une longueur comprise entre 6 et 64 caractères",
|
||||||
"description": "Error string shown to the user when password doesn't meet length criteria"
|
"description": "Error string shown to the user when password doesn't meet length criteria"
|
||||||
},
|
},
|
||||||
"passwordTypeError": {
|
"passwordTypeError": {
|
||||||
|
|
|
@ -1698,7 +1698,7 @@
|
||||||
"message": "Failed to set password"
|
"message": "Failed to set password"
|
||||||
},
|
},
|
||||||
"passwordLengthError": {
|
"passwordLengthError": {
|
||||||
"message": "Password must be between 6 and 50 characters long",
|
"message": "Password must be between 6 and 64 characters long",
|
||||||
"description": "Error string shown to the user when password doesn't meet length criteria"
|
"description": "Error string shown to the user when password doesn't meet length criteria"
|
||||||
},
|
},
|
||||||
"passwordTypeError": {
|
"passwordTypeError": {
|
||||||
|
|
|
@ -40,7 +40,6 @@ window.CONSTANTS = {
|
||||||
MAX_USERNAME_LENGTH: 20,
|
MAX_USERNAME_LENGTH: 20,
|
||||||
};
|
};
|
||||||
|
|
||||||
window.passwordUtil = require('./ts/util/passwordUtils');
|
|
||||||
window.Signal.Logs = require('./js/modules/logs');
|
window.Signal.Logs = require('./js/modules/logs');
|
||||||
|
|
||||||
window.resetDatabase = () => {
|
window.resetDatabase = () => {
|
||||||
|
|
|
@ -173,7 +173,6 @@ window.setPassword = (passPhrase, oldPhrase) =>
|
||||||
ipc.send('set-password', passPhrase, oldPhrase);
|
ipc.send('set-password', passPhrase, oldPhrase);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.passwordUtil = require('./ts/util/passwordUtils');
|
|
||||||
window.libsession = require('./ts/session');
|
window.libsession = require('./ts/session');
|
||||||
|
|
||||||
window.getMessageController =
|
window.getMessageController =
|
||||||
|
|
|
@ -237,8 +237,7 @@ export class Lightbox extends React.Component<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.paused) {
|
if (current.paused) {
|
||||||
// tslint:disable-next-line no-floating-promises
|
void current.play();
|
||||||
current.play();
|
|
||||||
} else {
|
} else {
|
||||||
current.pause();
|
current.pause();
|
||||||
}
|
}
|
||||||
|
@ -272,7 +271,7 @@ export class Lightbox extends React.Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={styles.controls}>
|
<div style={styles.controls}>
|
||||||
<Flex flexGrow={1}>
|
<Flex flex="1 1 auto">
|
||||||
<IconButton
|
<IconButton
|
||||||
type="close"
|
type="close"
|
||||||
onClick={this.onClose}
|
onClick={this.onClose}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { SessionSpinner } from './SessionSpinner';
|
||||||
import { StringUtils, ToastUtils } from '../../session/utils';
|
import { StringUtils, ToastUtils } from '../../session/utils';
|
||||||
import { lightTheme } from '../../state/ducks/SessionTheme';
|
import { lightTheme } from '../../state/ducks/SessionTheme';
|
||||||
import { ConversationController } from '../../session/conversations';
|
import { ConversationController } from '../../session/conversations';
|
||||||
|
import { PasswordUtil } from '../../util';
|
||||||
|
|
||||||
enum SignInMode {
|
enum SignInMode {
|
||||||
Default,
|
Default,
|
||||||
|
@ -454,7 +455,6 @@ export class RegistrationTabs extends React.Component<any, State> {
|
||||||
error={this.state.passwordErrorString}
|
error={this.state.passwordErrorString}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={window.i18n('enterOptionalPassword')}
|
placeholder={window.i18n('enterOptionalPassword')}
|
||||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
|
||||||
onValueChanged={(val: string) => {
|
onValueChanged={(val: string) => {
|
||||||
this.onPasswordChanged(val);
|
this.onPasswordChanged(val);
|
||||||
}}
|
}}
|
||||||
|
@ -470,7 +470,6 @@ export class RegistrationTabs extends React.Component<any, State> {
|
||||||
error={passwordsDoNotMatch}
|
error={passwordsDoNotMatch}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={window.i18n('confirmPassword')}
|
placeholder={window.i18n('confirmPassword')}
|
||||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
|
||||||
onValueChanged={(val: string) => {
|
onValueChanged={(val: string) => {
|
||||||
this.onPasswordVerifyChanged(val);
|
this.onPasswordVerifyChanged(val);
|
||||||
}}
|
}}
|
||||||
|
@ -592,7 +591,7 @@ export class RegistrationTabs extends React.Component<any, State> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = window.passwordUtil.validatePassword(input, window.i18n);
|
const error = PasswordUtil.validatePassword(input, window.i18n);
|
||||||
if (error) {
|
if (error) {
|
||||||
this.setState({
|
this.setState({
|
||||||
passwordErrorString: error,
|
passwordErrorString: error,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
|
|
||||||
import { SessionModal } from './SessionModal';
|
import { SessionModal } from './SessionModal';
|
||||||
import { SessionButton, SessionButtonColor } from './SessionButton';
|
import { SessionButton, SessionButtonColor } from './SessionButton';
|
||||||
import { PasswordUtil } from '../../util/';
|
import { missingCaseError, PasswordUtil } from '../../util/';
|
||||||
import { ToastUtils } from '../../session/utils';
|
import { ToastUtils } from '../../session/utils';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { SessionToast, SessionToastType } from './SessionToast';
|
import { SessionToast, SessionToastType } from './SessionToast';
|
||||||
|
@ -46,8 +46,6 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
|
|
||||||
this.onPasswordInput = this.onPasswordInput.bind(this);
|
this.onPasswordInput = this.onPasswordInput.bind(this);
|
||||||
this.onPasswordConfirmInput = this.onPasswordConfirmInput.bind(this);
|
this.onPasswordConfirmInput = this.onPasswordConfirmInput.bind(this);
|
||||||
|
|
||||||
this.onPaste = this.onPaste.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
@ -86,8 +84,6 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
}}
|
}}
|
||||||
placeholder={placeholders[0]}
|
placeholder={placeholders[0]}
|
||||||
onKeyUp={this.onPasswordInput}
|
onKeyUp={this.onPasswordInput}
|
||||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
|
||||||
onPaste={this.onPaste}
|
|
||||||
/>
|
/>
|
||||||
{action !== PasswordAction.Remove && (
|
{action !== PasswordAction.Remove && (
|
||||||
<input
|
<input
|
||||||
|
@ -95,8 +91,6 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
id="password-modal-input-confirm"
|
id="password-modal-input-confirm"
|
||||||
placeholder={placeholders[1]}
|
placeholder={placeholders[1]}
|
||||||
onKeyUp={this.onPasswordConfirmInput}
|
onKeyUp={this.onPasswordConfirmInput}
|
||||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
|
||||||
onPaste={this.onPaste}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,7 +102,7 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
<SessionButton
|
<SessionButton
|
||||||
text={window.i18n('ok')}
|
text={window.i18n('ok')}
|
||||||
buttonColor={confirmButtonColor}
|
buttonColor={confirmButtonColor}
|
||||||
onClick={async () => this.setPassword(onOk)}
|
onClick={this.setPassword}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SessionButton
|
<SessionButton
|
||||||
|
@ -145,8 +139,106 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false and set the state error field in the input is not a valid password
|
||||||
|
* or returns true
|
||||||
|
*/
|
||||||
|
private validatePassword(firstPassword: string) {
|
||||||
|
// if user did not fill the first password field, we can't do anything
|
||||||
|
const errorFirstInput = PasswordUtil.validatePassword(
|
||||||
|
firstPassword,
|
||||||
|
window.i18n
|
||||||
|
);
|
||||||
|
if (errorFirstInput !== null) {
|
||||||
|
this.setState({
|
||||||
|
error: errorFirstInput,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleActionSet(
|
||||||
|
enteredPassword: string,
|
||||||
|
enteredPasswordConfirm: string
|
||||||
|
) {
|
||||||
|
// be sure both password are valid
|
||||||
|
if (!this.validatePassword(enteredPassword)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// no need to validate second password. we just need to check that enteredPassword is valid, and that both password matches
|
||||||
|
|
||||||
|
if (enteredPassword !== enteredPasswordConfirm) {
|
||||||
|
this.setState({
|
||||||
|
error: window.i18n('setPasswordInvalid'),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await window.setPassword(enteredPassword, null);
|
||||||
|
ToastUtils.pushToastSuccess(
|
||||||
|
'setPasswordSuccessToast',
|
||||||
|
window.i18n('setPasswordTitle'),
|
||||||
|
window.i18n('setPasswordToastDescription'),
|
||||||
|
SessionIconType.Lock
|
||||||
|
);
|
||||||
|
|
||||||
|
this.props.onOk(this.props.action);
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleActionChange(oldPassword: string, newPassword: string) {
|
||||||
|
// We don't validate oldPassword on change: this is validate on the validatePasswordHash below
|
||||||
|
// we only validate the newPassword here
|
||||||
|
if (!this.validatePassword(newPassword)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isValidWithStoredInDB = Boolean(
|
||||||
|
await this.validatePasswordHash(oldPassword)
|
||||||
|
);
|
||||||
|
if (!isValidWithStoredInDB) {
|
||||||
|
this.setState({
|
||||||
|
error: window.i18n('changePasswordInvalid'),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await window.setPassword(newPassword, oldPassword);
|
||||||
|
|
||||||
|
ToastUtils.pushToastSuccess(
|
||||||
|
'setPasswordSuccessToast',
|
||||||
|
window.i18n('changePasswordTitle'),
|
||||||
|
window.i18n('changePasswordToastDescription'),
|
||||||
|
SessionIconType.Lock
|
||||||
|
);
|
||||||
|
|
||||||
|
this.props.onOk(this.props.action);
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleActionRemove(oldPassword: string) {
|
||||||
|
// We don't validate oldPassword on change: this is validate on the validatePasswordHash below
|
||||||
|
const isValidWithStoredInDB = Boolean(
|
||||||
|
await this.validatePasswordHash(oldPassword)
|
||||||
|
);
|
||||||
|
if (!isValidWithStoredInDB) {
|
||||||
|
this.setState({
|
||||||
|
error: window.i18n('removePasswordInvalid'),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await window.setPassword(null, oldPassword);
|
||||||
|
|
||||||
|
ToastUtils.pushToastWarning(
|
||||||
|
'setPasswordSuccessToast',
|
||||||
|
window.i18n('removePasswordTitle'),
|
||||||
|
window.i18n('removePasswordToastDescription')
|
||||||
|
);
|
||||||
|
|
||||||
|
this.props.onOk(this.props.action);
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
// tslint:disable-next-line: cyclomatic-complexity
|
// tslint:disable-next-line: cyclomatic-complexity
|
||||||
private async setPassword(onSuccess?: any) {
|
private async setPassword() {
|
||||||
const { action } = this.props;
|
const { action } = this.props;
|
||||||
const {
|
const {
|
||||||
currentPasswordEntered,
|
currentPasswordEntered,
|
||||||
|
@ -155,106 +247,28 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
const { Set, Remove, Change } = PasswordAction;
|
const { Set, Remove, Change } = PasswordAction;
|
||||||
|
|
||||||
// Trim leading / trailing whitespace for UX
|
// Trim leading / trailing whitespace for UX
|
||||||
const enteredPassword = (currentPasswordEntered || '').trim();
|
const firstPasswordEntered = (currentPasswordEntered || '').trim();
|
||||||
const enteredPasswordConfirm = (currentPasswordConfirmEntered || '').trim();
|
const secondPasswordEntered = (currentPasswordConfirmEntered || '').trim();
|
||||||
|
|
||||||
// if user did not fill the first password field, we can't do anything
|
switch (action) {
|
||||||
const errorFirstInput = PasswordUtil.validatePassword(
|
case Set: {
|
||||||
enteredPassword,
|
await this.handleActionSet(firstPasswordEntered, secondPasswordEntered);
|
||||||
window.i18n
|
|
||||||
);
|
|
||||||
if (errorFirstInput !== null) {
|
|
||||||
this.setState({
|
|
||||||
error: errorFirstInput,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if action is Set or Change, we need a valid ConfirmPassword
|
|
||||||
if (action === Set || action === Change) {
|
|
||||||
const errorSecondInput = PasswordUtil.validatePassword(
|
|
||||||
enteredPasswordConfirm,
|
|
||||||
window.i18n
|
|
||||||
);
|
|
||||||
if (errorSecondInput !== null) {
|
|
||||||
this.setState({
|
|
||||||
error: errorSecondInput,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
case Change: {
|
||||||
|
await this.handleActionChange(
|
||||||
// Passwords match or remove password successful
|
firstPasswordEntered,
|
||||||
const newPassword = action === Remove ? null : enteredPasswordConfirm;
|
secondPasswordEntered
|
||||||
const oldPassword = action === Set ? null : enteredPassword;
|
);
|
||||||
|
return;
|
||||||
// Check if password match, when setting, changing or removing
|
}
|
||||||
let valid;
|
case Remove: {
|
||||||
if (action === Set) {
|
await this.handleActionRemove(firstPasswordEntered);
|
||||||
valid = enteredPassword === enteredPasswordConfirm;
|
return;
|
||||||
} else {
|
|
||||||
valid = Boolean(await this.validatePasswordHash(oldPassword));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid) {
|
|
||||||
let str;
|
|
||||||
switch (action) {
|
|
||||||
case Set:
|
|
||||||
str = window.i18n('setPasswordInvalid');
|
|
||||||
break;
|
|
||||||
case Change:
|
|
||||||
str = window.i18n('changePasswordInvalid');
|
|
||||||
break;
|
|
||||||
case Remove:
|
|
||||||
str = window.i18n('removePasswordInvalid');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Invalid action ${action}`);
|
|
||||||
}
|
}
|
||||||
this.setState({
|
|
||||||
error: str,
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await window.setPassword(newPassword, oldPassword);
|
|
||||||
let title;
|
|
||||||
let description;
|
|
||||||
switch (action) {
|
|
||||||
case Set:
|
|
||||||
title = window.i18n('setPasswordTitle');
|
|
||||||
description = window.i18n('setPasswordToastDescription');
|
|
||||||
break;
|
|
||||||
case Change:
|
|
||||||
title = window.i18n('changePasswordTitle');
|
|
||||||
description = window.i18n('changePasswordToastDescription');
|
|
||||||
break;
|
|
||||||
case Remove:
|
|
||||||
title = window.i18n('removePasswordTitle');
|
|
||||||
description = window.i18n('removePasswordToastDescription');
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Invalid action ${action}`);
|
throw missingCaseError(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action !== Remove) {
|
|
||||||
ToastUtils.pushToastSuccess(
|
|
||||||
'setPasswordSuccessToast',
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
SessionIconType.Lock
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ToastUtils.pushToastWarning(
|
|
||||||
'setPasswordSuccessToast',
|
|
||||||
title,
|
|
||||||
description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSuccess(this.props.action);
|
|
||||||
this.closeDialog();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private closeDialog() {
|
private closeDialog() {
|
||||||
|
@ -263,26 +277,9 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPaste(event: any) {
|
|
||||||
const clipboard = event.clipboardData.getData('text');
|
|
||||||
|
|
||||||
if (clipboard.length > window.CONSTANTS.MAX_PASSWORD_LENGTH) {
|
|
||||||
const title = String(
|
|
||||||
window.i18n(
|
|
||||||
'pasteLongPasswordToastTitle',
|
|
||||||
window.CONSTANTS.MAX_PASSWORD_LENGTH
|
|
||||||
)
|
|
||||||
);
|
|
||||||
ToastUtils.pushToastWarning('passwordModal', title);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent pating into input
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async onPasswordInput(event: any) {
|
private async onPasswordInput(event: any) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
return this.setPassword(this.props.onOk);
|
return this.setPassword();
|
||||||
}
|
}
|
||||||
const currentPasswordEntered = event.target.value;
|
const currentPasswordEntered = event.target.value;
|
||||||
|
|
||||||
|
@ -291,7 +288,7 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
|
||||||
|
|
||||||
private async onPasswordConfirmInput(event: any) {
|
private async onPasswordConfirmInput(event: any) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
return this.setPassword(this.props.onOk);
|
return this.setPassword();
|
||||||
}
|
}
|
||||||
const currentPasswordConfirmEntered = event.target.value;
|
const currentPasswordConfirmEntered = event.target.value;
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ class SessionPasswordPromptInner extends React.PureComponent<
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onKeyUp = this.onKeyUp.bind(this);
|
this.onKeyUp = this.onKeyUp.bind(this);
|
||||||
this.onPaste = this.onPaste.bind(this);
|
|
||||||
|
|
||||||
this.initLogin = this.initLogin.bind(this);
|
this.initLogin = this.initLogin.bind(this);
|
||||||
this.initClearDataView = this.initClearDataView.bind(this);
|
this.initClearDataView = this.initClearDataView.bind(this);
|
||||||
|
@ -72,8 +71,6 @@ class SessionPasswordPromptInner extends React.PureComponent<
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
placeholder={' '}
|
placeholder={' '}
|
||||||
onKeyUp={this.onKeyUp}
|
onKeyUp={this.onKeyUp}
|
||||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
|
||||||
onPaste={this.onPaste}
|
|
||||||
ref={this.inputRef}
|
ref={this.inputRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -135,24 +132,6 @@ class SessionPasswordPromptInner extends React.PureComponent<
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onPaste(event: any) {
|
|
||||||
const clipboard = event.clipboardData.getData('text');
|
|
||||||
|
|
||||||
if (clipboard.length > window.CONSTANTS.MAX_PASSWORD_LENGTH) {
|
|
||||||
this.setState({
|
|
||||||
error: String(
|
|
||||||
window.i18n(
|
|
||||||
'pasteLongPasswordToastTitle',
|
|
||||||
window.CONSTANTS.MAX_PASSWORD_LENGTH
|
|
||||||
)
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent pasting into input
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onLogin(passPhrase: string) {
|
public async onLogin(passPhrase: string) {
|
||||||
const passPhraseTrimmed = passPhrase.trim();
|
const passPhraseTrimmed = passPhrase.trim();
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { SessionModal } from './SessionModal';
|
||||||
import { SessionButton } from './SessionButton';
|
import { SessionButton } from './SessionButton';
|
||||||
import { ToastUtils } from '../../session/utils';
|
import { ToastUtils } from '../../session/utils';
|
||||||
import { DefaultTheme, withTheme } from 'styled-components';
|
import { DefaultTheme, withTheme } from 'styled-components';
|
||||||
|
import { PasswordUtil } from '../../util';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose: any;
|
onClose: any;
|
||||||
|
@ -77,7 +78,6 @@ class SessionSeedModalInner extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderPasswordView() {
|
private renderPasswordView() {
|
||||||
const maxPasswordLen = 64;
|
|
||||||
const error = this.state.error;
|
const error = this.state.error;
|
||||||
const i18n = window.i18n;
|
const i18n = window.i18n;
|
||||||
const { onClose } = this.props;
|
const { onClose } = this.props;
|
||||||
|
@ -90,7 +90,6 @@ class SessionSeedModalInner extends React.Component<Props, State> {
|
||||||
id="seed-input-password"
|
id="seed-input-password"
|
||||||
placeholder={i18n('password')}
|
placeholder={i18n('password')}
|
||||||
onKeyUp={this.onEnter}
|
onKeyUp={this.onEnter}
|
||||||
maxLength={maxPasswordLen}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
|
@ -143,8 +142,8 @@ class SessionSeedModalInner extends React.Component<Props, State> {
|
||||||
private confirmPassword() {
|
private confirmPassword() {
|
||||||
const passwordHash = this.state.passwordHash;
|
const passwordHash = this.state.passwordHash;
|
||||||
const passwordValue = jQuery('#seed-input-password').val();
|
const passwordValue = jQuery('#seed-input-password').val();
|
||||||
const isPasswordValid = window.passwordUtil.matchesHash(
|
const isPasswordValid = PasswordUtil.matchesHash(
|
||||||
passwordValue,
|
passwordValue as string,
|
||||||
passwordHash
|
passwordHash
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
SessionButtonColor,
|
SessionButtonColor,
|
||||||
SessionButtonType,
|
SessionButtonType,
|
||||||
} from '../SessionButton';
|
} from '../SessionButton';
|
||||||
import { BlockedNumberController } from '../../../util';
|
import { BlockedNumberController, PasswordUtil } from '../../../util';
|
||||||
import { ToastUtils } from '../../../session/utils';
|
import { ToastUtils } from '../../../session/utils';
|
||||||
import { ConversationLookupType } from '../../../state/ducks/conversations';
|
import { ConversationLookupType } from '../../../state/ducks/conversations';
|
||||||
import { StateType } from '../../../state/reducer';
|
import { StateType } from '../../../state/reducer';
|
||||||
|
@ -172,8 +172,7 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
|
||||||
type="password"
|
type="password"
|
||||||
id="password-lock-input"
|
id="password-lock-input"
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
placeholder={' '}
|
placeholder="Password"
|
||||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="spacer-xs" />
|
<div className="spacer-xs" />
|
||||||
|
@ -211,7 +210,7 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
|
||||||
|
|
||||||
// Check if the password matches the hash we have stored
|
// Check if the password matches the hash we have stored
|
||||||
const hash = await window.Signal.Data.getPasswordHash();
|
const hash = await window.Signal.Data.getPasswordHash();
|
||||||
if (hash && !window.passwordUtil.matchesHash(enteredPassword, hash)) {
|
if (hash && !PasswordUtil.matchesHash(enteredPassword, hash)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
pwdLockError: window.i18n('invalidPassword'),
|
pwdLockError: window.i18n('invalidPassword'),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
const { assert } = require('chai');
|
import { assert } from 'chai';
|
||||||
|
import { PasswordUtil } from '../../../../util';
|
||||||
const passwordUtil = require('../../ts/util/passwordUtils');
|
|
||||||
|
|
||||||
describe('Password Util', () => {
|
describe('Password Util', () => {
|
||||||
describe('hash generation', () => {
|
describe('hash generation', () => {
|
||||||
it('generates the same hash for the same phrase', () => {
|
it('generates the same hash for the same phrase', () => {
|
||||||
const first = passwordUtil.generateHash('phrase');
|
const first = PasswordUtil.generateHash('phrase');
|
||||||
const second = passwordUtil.generateHash('phrase');
|
const second = PasswordUtil.generateHash('phrase');
|
||||||
assert.strictEqual(first, second);
|
assert.strictEqual(first, second);
|
||||||
});
|
});
|
||||||
it('generates different hashes for different phrases', () => {
|
it('generates different hashes for different phrases', () => {
|
||||||
const first = passwordUtil.generateHash('0');
|
const first = PasswordUtil.generateHash('0');
|
||||||
const second = passwordUtil.generateHash('1');
|
const second = PasswordUtil.generateHash('1');
|
||||||
assert.notStrictEqual(first, second);
|
assert.notStrictEqual(first, second);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -19,12 +18,12 @@ describe('Password Util', () => {
|
||||||
describe('hash matching', () => {
|
describe('hash matching', () => {
|
||||||
it('returns true for the same hash', () => {
|
it('returns true for the same hash', () => {
|
||||||
const phrase = 'phrase';
|
const phrase = 'phrase';
|
||||||
const hash = passwordUtil.generateHash(phrase);
|
const hash = PasswordUtil.generateHash(phrase);
|
||||||
assert.isTrue(passwordUtil.matchesHash(phrase, hash));
|
assert.isTrue(PasswordUtil.matchesHash(phrase, hash));
|
||||||
});
|
});
|
||||||
it('returns false for different hashes', () => {
|
it('returns false for different hashes', () => {
|
||||||
const hash = passwordUtil.generateHash('phrase');
|
const hash = PasswordUtil.generateHash('phrase');
|
||||||
assert.isFalse(passwordUtil.matchesHash('phrase2', hash));
|
assert.isFalse(PasswordUtil.matchesHash('phrase2', hash));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,26 +43,26 @@ describe('Password Util', () => {
|
||||||
'#'.repeat(50),
|
'#'.repeat(50),
|
||||||
];
|
];
|
||||||
valid.forEach(pass => {
|
valid.forEach(pass => {
|
||||||
assert.isNull(passwordUtil.validatePassword(pass));
|
assert.isNull(PasswordUtil.validatePassword(pass));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an error if password is not a string', () => {
|
it('should return an error if password is not a string', () => {
|
||||||
const invalid = [0, 123456, [], {}, null, undefined];
|
const invalid = [0, 123456, [], {}, null, undefined] as any;
|
||||||
invalid.forEach(pass => {
|
invalid.forEach((pass: any) => {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
passwordUtil.validatePassword(pass),
|
PasswordUtil.validatePassword(pass),
|
||||||
'Password must be a string'
|
'Password must be a string'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an error if password is not between 6 and 50 characters', () => {
|
it('should return an error if password is not between 6 and 64 characters', () => {
|
||||||
const invalid = ['a', 'abcde', '#'.repeat(51), '#'.repeat(100)];
|
const invalid = ['a', 'abcde', '#'.repeat(51), '#'.repeat(100)];
|
||||||
invalid.forEach(pass => {
|
invalid.forEach(pass => {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
passwordUtil.validatePassword(pass),
|
PasswordUtil.validatePassword(pass),
|
||||||
'Password must be between 6 and 50 characters long'
|
'Password must be between 6 and 64 characters long'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -82,7 +81,7 @@ describe('Password Util', () => {
|
||||||
];
|
];
|
||||||
invalid.forEach(pass => {
|
invalid.forEach(pass => {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
passwordUtil.validatePassword(pass),
|
PasswordUtil.validatePassword(pass),
|
||||||
'Password must only contain letters, numbers and symbols'
|
'Password must only contain letters, numbers and symbols'
|
||||||
);
|
);
|
||||||
});
|
});
|
|
@ -3,7 +3,7 @@ import { LocalizerType } from '../types/Util';
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
TYPE: 'Password must be a string',
|
TYPE: 'Password must be a string',
|
||||||
LENGTH: 'Password must be between 6 and 50 characters long',
|
LENGTH: 'Password must be between 6 and 64 characters long',
|
||||||
CHARACTER: 'Password must only contain letters, numbers and symbols',
|
CHARACTER: 'Password must only contain letters, numbers and symbols',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ export const generateHash = (phrase: string) => phrase && sha512(phrase.trim());
|
||||||
export const matchesHash = (phrase: string | null, hash: string) =>
|
export const matchesHash = (phrase: string | null, hash: string) =>
|
||||||
phrase && sha512(phrase.trim()) === hash.trim();
|
phrase && sha512(phrase.trim()) === hash.trim();
|
||||||
|
|
||||||
export const validatePassword = (phrase: string, i18n: LocalizerType) => {
|
export const validatePassword = (phrase: string, i18n?: LocalizerType) => {
|
||||||
if (typeof phrase !== 'string') {
|
if (typeof phrase !== 'string') {
|
||||||
return i18n ? i18n('passwordTypeError') : ERRORS.TYPE;
|
return i18n ? i18n('passwordTypeError') : ERRORS.TYPE;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,10 @@ export const validatePassword = (phrase: string, i18n: LocalizerType) => {
|
||||||
return i18n ? i18n('noGivenPassword') : ERRORS.LENGTH;
|
return i18n ? i18n('noGivenPassword') : ERRORS.LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmed.length < 6 || trimmed.length > 50) {
|
if (
|
||||||
|
trimmed.length < 6 ||
|
||||||
|
trimmed.length > window.CONSTANTS.MAX_PASSWORD_LENGTH
|
||||||
|
) {
|
||||||
return i18n ? i18n('passwordLengthError') : ERRORS.LENGTH;
|
return i18n ? i18n('passwordLengthError') : ERRORS.LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,6 @@ declare global {
|
||||||
lokiSnodeAPI: LokiSnodeAPI;
|
lokiSnodeAPI: LokiSnodeAPI;
|
||||||
mnemonic: RecoveryPhraseUtil;
|
mnemonic: RecoveryPhraseUtil;
|
||||||
onLogin: any;
|
onLogin: any;
|
||||||
passwordUtil: any;
|
|
||||||
resetDatabase: any;
|
resetDatabase: any;
|
||||||
restart: any;
|
restart: any;
|
||||||
seedNodeList: any;
|
seedNodeList: any;
|
||||||
|
|
Loading…
Reference in New Issue