From 2cf152b8825fe4ecdb1fb0ea71709ce23fefa1e4 Mon Sep 17 00:00:00 2001 From: Vince <58160433+vincentbavitz@users.noreply.github.com> Date: Tue, 7 Jan 2020 12:44:26 +1100 Subject: [PATCH 1/3] Update signal.js Rmv old deps --- js/modules/signal.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/js/modules/signal.js b/js/modules/signal.js index d1917d615..b9656f62a 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -66,15 +66,6 @@ const { const { SessionSeedModal, } = require('../../ts/components/session/SessionSeedModal'); - -const { - SessionPasswordChangeModal, -} = require('../../ts/components/session/SessionPasswordChangeModal'); - -const { - SessionPasswordRemoveModal, -} = require('../../ts/components/session/SessionPasswordRemoveModal'); - const { SessionConfirm, } = require('../../ts/components/session/SessionConfirm'); @@ -288,8 +279,6 @@ exports.setup = (options = {}) => { SessionModal, SessionQRModal, SessionSeedModal, - SessionPasswordChangeModal, - SessionPasswordRemoveModal, SessionDropdown, MediaGallery, Message, From d8cd0cfe92bcfd6797446a9e75bf6c4aeb30aed5 Mon Sep 17 00:00:00 2001 From: Vince <58160433+vincentbavitz@users.noreply.github.com> Date: Tue, 7 Jan 2020 12:45:57 +1100 Subject: [PATCH 2/3] Update password_dialog_view.js Revert --- js/views/password_dialog_view.js | 225 ++++++++++++++++++++++++++++--- 1 file changed, 210 insertions(+), 15 deletions(-) diff --git a/js/views/password_dialog_view.js b/js/views/password_dialog_view.js index d3221b566..57d2afa00 100644 --- a/js/views/password_dialog_view.js +++ b/js/views/password_dialog_view.js @@ -1,4 +1,4 @@ -/* global Whisper */ +/* global Whisper, i18n, _, Signal, passwordUtil */ // eslint-disable-next-line func-names (function() { @@ -6,28 +6,223 @@ window.Whisper = window.Whisper || {}; - Whisper.PasswordDialogView = Whisper.View.extend({ + const PasswordDialogView = Whisper.View.extend({ className: 'loki-dialog password-dialog modal', - initialize() { - this.close = this.close.bind(this); + templateName: 'password-dialog', + initialize(options) { + this.type = options.type; + this.resolve = options.resolve; + this.okText = options.okText || i18n('ok'); + + this.reject = options.reject; + this.cancelText = options.cancelText || i18n('cancel'); + + this.title = options.title; + this.render(); + this.updateUI(); + }, + events: { + keyup: 'onKeyup', + 'click .ok': 'ok', + 'click .cancel': 'cancel', }, + render_attributes() { + return { + showCancel: !this.hideCancel, + cancel: this.cancelText, + ok: this.okText, + title: this.title, + }; + }, + async updateUI() { + if (this.disableOkButton()) { + this.$('.ok').prop('disabled', true); + } else { + this.$('.ok').prop('disabled', false); + } + }, + disableOkButton() { + const password = this.$('#password').val(); + return _.isEmpty(password); + }, + async validate() { + const password = this.$('#password').val(); + const passwordConfirmation = this.$('#password-confirmation').val(); - render() { - this.dialogView = new Whisper.ReactWrapperView({ - className: 'password-dialog-wrapper', - Component: window.Signal.Components.SessionPasswordChangeModal, - props: { - onClose: this.close, - }, - }); + const pairValidation = this.validatePasswordPair( + password, + passwordConfirmation + ); + const hashValidation = await this.validatePasswordHash(password); - this.$el.append(this.dialogView.el); - return this; + return pairValidation || hashValidation; }, + async validatePasswordHash(password) { + // Check if the password matches the hash we have stored + const hash = await Signal.Data.getPasswordHash(); + if (hash && !passwordUtil.matchesHash(password, hash)) { + return i18n('invalidPassword'); + } + return null; + }, + validatePasswordPair(password, passwordConfirmation) { + if (!_.isEmpty(password)) { + // Check if the password is first valid + const passwordValidation = passwordUtil.validatePassword( + password, + i18n + ); + if (passwordValidation) { + return passwordValidation; + } + + // Check if the confirmation password is the same + if ( + !passwordConfirmation || + password.trim() !== passwordConfirmation.trim() + ) { + return i18n('passwordsDoNotMatch'); + } + } + return null; + }, + okPressed() { + const password = this.$('#password').val(); + if (this.type === 'set') { + window.setPassword(password.trim()); + } else if (this.type === 'remove') { + window.setPassword(null, password.trim()); + } + }, + okErrored() { + if (this.type === 'set') { + this.showError(i18n('setPasswordFail')); + } else if (this.type === 'remove') { + this.showError(i18n('removePasswordFail')); + } + }, + async ok() { + const error = await this.validate(); + if (error) { + this.showError(error); + return; + } + + // Clear any errors + this.showError(null); - close() { + try { + this.okPressed(); + + this.remove(); + if (this.resolve) { + this.resolve(); + } + } catch (e) { + this.okErrored(); + } + }, + cancel() { this.remove(); + if (this.reject) { + this.reject(); + } + }, + onKeyup(event) { + this.updateUI(); + switch (event.key) { + case 'Enter': + this.ok(); + break; + case 'Escape': + case 'Esc': + this.cancel(); + break; + default: + return; + } + event.preventDefault(); + }, + focusCancel() { + this.$('.cancel').focus(); + }, + showError(message) { + if (_.isEmpty(message)) { + this.$('.error').text(''); + this.$('.error').hide(); + } else { + this.$('.error').text(`Error: ${message}`); + this.$('.error').show(); + } + }, + }); + + const ChangePasswordDialogView = PasswordDialogView.extend({ + templateName: 'password-change-dialog', + disableOkButton() { + const oldPassword = this.$('#old-password').val(); + const newPassword = this.$('#new-password').val(); + return _.isEmpty(oldPassword) || _.isEmpty(newPassword); + }, + async validate() { + const oldPassword = this.$('#old-password').val(); + + // Validate the old password + if (!_.isEmpty(oldPassword)) { + const oldPasswordValidation = passwordUtil.validatePassword( + oldPassword, + i18n + ); + if (oldPasswordValidation) { + return oldPasswordValidation; + } + } else { + return i18n('typeInOldPassword'); + } + + const password = this.$('#new-password').val(); + const passwordConfirmation = this.$('#new-password-confirmation').val(); + + const pairValidation = this.validatePasswordPair( + password, + passwordConfirmation + ); + const hashValidation = await this.validatePasswordHash(oldPassword); + + return pairValidation || hashValidation; + }, + okPressed() { + const oldPassword = this.$('#old-password').val(); + const newPassword = this.$('#new-password').val(); + window.setPassword(newPassword.trim(), oldPassword.trim()); + }, + okErrored() { + this.showError(i18n('changePasswordFail')); }, }); + + Whisper.getPasswordDialogView = (type, resolve, reject) => { + // This is a differently styled dialog + if (type === 'change') { + return new ChangePasswordDialogView({ + title: i18n('changePassword'), + okTitle: i18n('change'), + resolve, + reject, + }); + } + + // Set and Remove is basically the same UI + const title = + type === 'remove' ? i18n('removePassword') : i18n('setPassword'); + const okTitle = type === 'remove' ? i18n('remove') : i18n('set'); + return new PasswordDialogView({ + title, + okTitle, + type, + resolve, + reject, + }); + }; })();