Merge pull request #1294 from Bilb/fix-drop-message-closed-group

pull/1296/head
Audric Ackermann 5 years ago committed by GitHub
commit c82965afe1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -62,7 +62,7 @@
return this;
},
onSubmit(groupName, avatar) {
if(groupName !== this.groupName || avatar !== this.avatarPath) {
if (groupName !== this.groupName || avatar !== this.avatarPath) {
window.MediumGroups.initiateGroupUpdate(
this.groupId,
groupName,

@ -54,7 +54,7 @@ const config = require('./app/config');
// Very important to put before the single instance check, since it is based on the
// userData directory.
const userConfig = require('./app/user_config');
const passwordUtil = require('./app/password_util');
const passwordUtil = require('./ts/util/passwordUtils');
const importMode =
process.argv.some(arg => arg === '--import') || config.get('import');

@ -40,7 +40,7 @@ window.CONSTANTS = {
MAX_USERNAME_LENGTH: 20,
};
window.passwordUtil = require('./app/password_util');
window.passwordUtil = require('./ts/util/passwordUtils');
window.Signal.Logs = require('./js/modules/logs');
window.resetDatabase = () => {

@ -164,7 +164,7 @@ window.setPassword = (passPhrase, oldPhrase) =>
ipc.send('set-password', passPhrase, oldPhrase);
});
window.passwordUtil = require('./app/password_util');
window.passwordUtil = require('./ts/util/passwordUtils');
window.libsession = require('./ts/session');
// We never do these in our code, so we'll prevent it everywhere

@ -1,6 +1,6 @@
const { assert } = require('chai');
const passwordUtil = require('../../app/password_util');
const passwordUtil = require('../../ts/util/passwordUtils');
describe('Password Util', () => {
describe('hash generation', () => {

@ -2,6 +2,7 @@ import React from 'react';
import { RenderTextCallbackType } from '../../types/Util';
import classNames from 'classnames';
import { MultiDeviceProtocol } from '../../session/protocols';
import { FindMember } from '../../util';
declare global {
@ -20,6 +21,7 @@ interface MentionProps {
interface MentionState {
found: any;
us: boolean;
}
class Mention extends React.Component<MentionProps, MentionState> {
@ -46,8 +48,7 @@ class Mention extends React.Component<MentionProps, MentionState> {
public render() {
if (this.state.found) {
// TODO: We don't have to search the database of message just to know that the message is for us!
const us =
this.state.found.authorPhoneNumber === window.lokiPublicChatAPI.ourKey;
const us = this.state.us;
const className = classNames(
'mention-profile-name',
us && 'mention-profile-name-us'
@ -79,7 +80,9 @@ class Mention extends React.Component<MentionProps, MentionState> {
bound
);
if (found) {
this.setState({ found });
const us = await MultiDeviceProtocol.isOurDevice(found.authorPhoneNumber);
this.setState({ found, us });
this.clearOurInterval();
}
}

@ -269,7 +269,7 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
rowHeight={64}
rowRenderer={this.renderRow}
width={width}
autoHeight={true}
autoHeight={false}
/>
)}
</AutoSizer>

@ -192,7 +192,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
rowHeight={64}
rowRenderer={this.renderRow}
width={width}
autoHeight={true}
autoHeight={false}
/>
)}
</AutoSizer>

@ -2,7 +2,7 @@ import React from 'react';
import { SessionModal } from './SessionModal';
import { SessionButton, SessionButtonColor } from './SessionButton';
import { PasswordUtil } from '../../util/';
export enum PasswordAction {
Set = 'set',
Change = 'change',
@ -17,17 +17,20 @@ interface Props {
interface State {
error: string | null;
currentPasswordEntered: string | null;
currentPasswordConfirmEntered: string | null;
}
export class SessionPasswordModal extends React.Component<Props, State> {
private readonly passwordInput: React.RefObject<HTMLInputElement>;
private readonly passwordInputConfirm: React.RefObject<HTMLInputElement>;
private passportInput: HTMLInputElement | null = null;
constructor(props: any) {
super(props);
this.state = {
error: null,
currentPasswordEntered: null,
currentPasswordConfirmEntered: null,
};
this.showError = this.showError.bind(this);
@ -35,32 +38,28 @@ export class SessionPasswordModal extends React.Component<Props, State> {
this.setPassword = this.setPassword.bind(this);
this.closeDialog = this.closeDialog.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.onPaste = this.onPaste.bind(this);
this.onPasswordInput = this.onPasswordInput.bind(this);
this.onPasswordConfirmInput = this.onPasswordConfirmInput.bind(this);
this.passwordInput = React.createRef();
this.passwordInputConfirm = React.createRef();
this.onPaste = this.onPaste.bind(this);
}
public componentDidMount() {
setTimeout(() => {
if (!this.passwordInput.current) {
return;
}
this.passwordInput.current.focus();
}, 100);
// tslint:disable-next-line: no-unused-expression
this.passportInput && this.passportInput.focus();
}, 1);
}
public render() {
const { action, onOk } = this.props;
const placeholders =
this.props.action === PasswordAction.Change
action === PasswordAction.Change
? [window.i18n('typeInOldPassword'), window.i18n('enterPassword')]
: [window.i18n('enterPassword'), window.i18n('confirmPassword')];
const confirmButtonColor =
this.props.action === PasswordAction.Remove
action === PasswordAction.Remove
? SessionButtonColor.Danger
: SessionButtonColor.Primary;
@ -76,9 +75,11 @@ export class SessionPasswordModal extends React.Component<Props, State> {
<input
type="password"
id="password-modal-input"
ref={this.passwordInput}
ref={input => {
this.passportInput = input;
}}
placeholder={placeholders[0]}
onKeyUp={this.onKeyUp}
onKeyUp={this.onPasswordInput}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onPaste={this.onPaste}
/>
@ -86,9 +87,8 @@ export class SessionPasswordModal extends React.Component<Props, State> {
<input
type="password"
id="password-modal-input-confirm"
ref={this.passwordInputConfirm}
placeholder={placeholders[1]}
onKeyUp={this.onKeyUp}
onKeyUp={this.onPasswordConfirmInput}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onPaste={this.onPaste}
/>
@ -117,7 +117,7 @@ export class SessionPasswordModal extends React.Component<Props, State> {
public async validatePasswordHash(password: string | null) {
// Check if the password matches the hash we have stored
const hash = await window.Signal.Data.getPasswordHash();
if (hash && !window.passwordUtil.matchesHash(password, hash)) {
if (hash && !PasswordUtil.matchesHash(password, hash)) {
return false;
}
@ -139,61 +139,60 @@ export class SessionPasswordModal extends React.Component<Props, State> {
);
}
// tslint:disable-next-line: cyclomatic-complexity
private async setPassword(onSuccess?: any) {
// Only initial input required for PasswordAction.Remove
if (
!this.passwordInput.current ||
(!this.passwordInputConfirm.current &&
this.props.action !== PasswordAction.Remove)
) {
return;
}
const { action } = this.props;
const {
currentPasswordEntered,
currentPasswordConfirmEntered,
} = this.state;
const { Set, Remove, Change } = PasswordAction;
// Trim leading / trailing whitespace for UX
const enteredPassword = String(this.passwordInput.current.value).trim();
const enteredPasswordConfirm =
(this.passwordInputConfirm.current &&
String(this.passwordInputConfirm.current.value).trim()) ||
'';
if (
enteredPassword.length === 0 ||
(enteredPasswordConfirm.length === 0 &&
this.props.action !== PasswordAction.Remove)
) {
return;
}
const enteredPassword = (currentPasswordEntered || '').trim();
const enteredPasswordConfirm = (currentPasswordConfirmEntered || '').trim();
// Check passwords entered
if (
enteredPassword.length === 0 ||
(this.props.action === PasswordAction.Change &&
enteredPasswordConfirm.length === 0)
) {
// if user did not fill the first password field, we can't do anything
const errorFirstInput = PasswordUtil.validatePassword(
enteredPassword,
window.i18n
);
if (errorFirstInput !== null) {
this.setState({
error: window.i18n('noGivenPassword'),
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;
}
}
// Passwords match or remove password successful
const newPassword =
this.props.action === PasswordAction.Remove
? null
: enteredPasswordConfirm;
const oldPassword =
this.props.action === PasswordAction.Set ? null : enteredPassword;
const newPassword = action === Remove ? null : enteredPasswordConfirm;
const oldPassword = action === Set ? null : enteredPassword;
// Check if password match, when setting, changing or removing
const valid =
this.props.action !== PasswordAction.Set
? Boolean(await this.validatePasswordHash(oldPassword))
: enteredPassword === enteredPasswordConfirm;
let valid;
if (action === Set) {
valid = enteredPassword === enteredPasswordConfirm;
} else {
valid = Boolean(await this.validatePasswordHash(oldPassword));
}
if (!valid) {
this.setState({
error: window.i18n(`${this.props.action}PasswordInvalid`),
error: window.i18n(`${action}PasswordInvalid`),
});
return;
@ -202,10 +201,10 @@ export class SessionPasswordModal extends React.Component<Props, State> {
await window.setPassword(newPassword, oldPassword);
const toastParams = {
title: window.i18n(`${this.props.action}PasswordTitle`),
description: window.i18n(`${this.props.action}PasswordToastDescription`),
type: this.props.action !== PasswordAction.Remove ? 'success' : 'warning',
icon: this.props.action !== PasswordAction.Remove ? 'lock' : undefined,
title: window.i18n(`${action}PasswordTitle`),
description: window.i18n(`${action}PasswordToastDescription`),
type: action !== Remove ? 'success' : 'warning',
icon: action !== Remove ? 'lock' : undefined,
};
window.pushToast({
@ -244,13 +243,17 @@ export class SessionPasswordModal extends React.Component<Props, State> {
return false;
}
private async onKeyUp(event: any) {
const { onOk } = this.props;
private async onPasswordInput(event: any) {
if (event.key === 'Enter') {
await this.setPassword(onOk);
return this.setPassword(this.props.onOk);
}
this.setState({ currentPasswordEntered: event.target.value });
}
event.preventDefault();
private async onPasswordConfirmInput(event: any) {
if (event.key === 'Enter') {
return this.setPassword(this.props.onOk);
}
this.setState({ currentPasswordConfirmEntered: event.target.value });
}
}

@ -8,9 +8,7 @@ import * as Lodash from 'lodash';
import * as libsession from '../session';
import { handleSessionRequestMessage } from './sessionHandling';
import { handlePairingAuthorisationMessage } from './multidevice';
import {
MediumGroupRequestKeysMessage,
} from '../session/messages/outgoing';
import { MediumGroupRequestKeysMessage } from '../session/messages/outgoing';
import { MultiDeviceProtocol, SessionProtocol } from '../session/protocols';
import { PubKey } from '../session/types';

@ -6,6 +6,7 @@ import { migrateColor } from './migrateColor';
import { makeLookup } from './makeLookup';
import { FindMember } from './findMember';
import * as UserUtil from './user';
import * as PasswordUtil from './passwordUtils';
export * from './blockedNumberController';
@ -17,5 +18,6 @@ export {
migrateColor,
missingCaseError,
UserUtil,
PasswordUtil,
FindMember,
};

@ -1,4 +1,5 @@
const crypto = require('crypto');
import * as crypto from 'crypto';
import { LocalizerType } from '../types/Util';
const ERRORS = {
TYPE: 'Password must be a string',
@ -6,22 +7,26 @@ const ERRORS = {
CHARACTER: 'Password must only contain letters, numbers and symbols',
};
const sha512 = text => {
const sha512 = (text: string) => {
const hash = crypto.createHash('sha512');
hash.update(text.trim());
return hash.digest('hex');
};
const generateHash = phrase => phrase && sha512(phrase.trim());
const matchesHash = (phrase, hash) =>
export const generateHash = (phrase: string) => phrase && sha512(phrase.trim());
export const matchesHash = (phrase: string | null, hash: string) =>
phrase && sha512(phrase.trim()) === hash.trim();
const validatePassword = (phrase, i18n) => {
export const validatePassword = (phrase: string, i18n: LocalizerType) => {
if (typeof phrase !== 'string') {
return i18n ? i18n('passwordTypeError') : ERRORS.TYPE;
}
const trimmed = phrase.trim();
if (trimmed.length === 0) {
return i18n ? i18n('noGivenPassword') : ERRORS.LENGTH;
}
if (trimmed.length < 6 || trimmed.length > 50) {
return i18n ? i18n('passwordLengthError') : ERRORS.LENGTH;
}
@ -34,9 +39,3 @@ const validatePassword = (phrase, i18n) => {
return null;
};
module.exports = {
generateHash,
matchesHash,
validatePassword,
};
Loading…
Cancel
Save