diff --git a/res/css/_components.scss b/res/css/_components.scss index b871045fa1..0faf971786 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -121,7 +121,6 @@ @import "./views/elements/_Tooltip.scss"; @import "./views/elements/_TooltipButton.scss"; @import "./views/elements/_Validation.scss"; -@import "./views/elements/_ZxcvbnProgressBar.scss"; @import "./views/emojipicker/_EmojiPicker.scss"; @import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; diff --git a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss index b9babd05f5..9be98e25b2 100644 --- a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss +++ b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss @@ -35,17 +35,6 @@ limitations under the License. align-items: flex-start; } -.mx_CreateKeyBackupDialog_passPhraseHelp { - flex: 1; - height: 85px; - margin-left: 20px; - font-size: 80%; -} - -.mx_CreateKeyBackupDialog_passPhraseHelp progress { - width: 100%; -} - .mx_CreateKeyBackupDialog_passPhraseInput { flex: none; width: 250px; diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index df2a81263d..532b2f960f 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -15,18 +15,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {createRef} from 'react'; import FileSaver from 'file-saver'; import * as sdk from '../../../../index'; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import PropTypes from 'prop-types'; -import { scorePassword } from '../../../../utils/PasswordScorer'; -import { _t } from '../../../../languageHandler'; +import {_t, _td} from '../../../../languageHandler'; import { accessSecretStorage } from '../../../../CrossSigningManager'; import SettingsStore from '../../../../settings/SettingsStore'; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import {copyNode} from "../../../../utils/strings"; -import ZxcvbnProgressBar from "../../../../components/views/elements/ZxcvbnProgressBar"; +import PassphraseField from "../../../../components/views/auth/PassphraseField"; const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE_CONFIRM = 1; @@ -37,7 +36,6 @@ const PHASE_DONE = 5; const PHASE_OPTOUT_CONFIRM = 6; const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. -const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms. /* * Walks the user through the process of creating an e2e key backup @@ -53,17 +51,18 @@ export default class CreateKeyBackupDialog extends React.PureComponent { this._recoveryKeyNode = null; this._keyBackupInfo = null; - this._setZxcvbnResultTimeout = null; this.state = { secureSecretStorage: null, phase: PHASE_PASSPHRASE, passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', copied: false, downloaded: false, - zxcvbnResult: null, }; + + this._passphraseField = createRef(); } async componentDidMount() { @@ -82,12 +81,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent { } } - componentWillUnmount() { - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - } - _collectRecoveryKeyNode = (n) => { this._recoveryKeyNode = n; } @@ -181,22 +174,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent { _onPassPhraseNextClick = async (e) => { e.preventDefault(); + if (!this._passphraseField.current) return; // unmounting - // If we're waiting for the timeout before updating the result at this point, - // skip ahead and do it now, otherwise we'll deny the attempt to proceed - // even if the user entered a valid passphrase - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - this._setZxcvbnResultTimeout = null; - await new Promise((resolve) => { - this.setState({ - zxcvbnResult: scorePassword(this.state.passPhrase), - }, resolve); - }); - } - if (this._passPhraseIsValid()) { - this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); + await this._passphraseField.current.validate({ allowEmpty: false }); + if (!this._passphraseField.current.state.valid) { + this._passphraseField.current.focus(); + this._passphraseField.current.validate({ allowEmpty: false, focused: true }); + return; } + + this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); }; _onPassPhraseConfirmNextClick = async (e) => { @@ -215,9 +202,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent { _onSetAgainClick = () => { this.setState({ passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', phase: PHASE_PASSPHRASE, - zxcvbnResult: null, }); } @@ -227,23 +214,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent { }); } + _onPassPhraseValidate = (result) => { + this.setState({ + passPhraseValid: result.valid, + }); + }; + _onPassPhraseChange = (e) => { this.setState({ passPhrase: e.target.value, }); - - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - this._setZxcvbnResultTimeout = setTimeout(() => { - this._setZxcvbnResultTimeout = null; - this.setState({ - // precompute this and keep it in state: zxcvbn is fast but - // we use it in a couple of different places so no point recomputing - // it unnecessarily. - zxcvbnResult: scorePassword(this.state.passPhrase), - }); - }, PASSPHRASE_FEEDBACK_DELAY); } _onPassPhraseConfirmChange = (e) => { @@ -252,35 +232,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent { }); } - _passPhraseIsValid() { - return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE; - } - _renderPhasePassPhrase() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - let strengthMeter; - let helpText; - if (this.state.zxcvbnResult) { - if (this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE) { - helpText = _t("Great! This recovery passphrase looks strong enough."); - } else { - const suggestions = []; - for (let i = 0; i < this.state.zxcvbnResult.feedback.suggestions.length; ++i) { - suggestions.push(
{this.state.zxcvbnResult.feedback.suggestions[i]}
); - } - const suggestionBlock =
{suggestions.length > 0 ? suggestions : _t("Keep going...")}
; - - helpText =
- {this.state.zxcvbnResult.feedback.warning} - {suggestionBlock} -
; - } - strengthMeter =
- -
; - } - return

{_t( "Warning: You should only set up key backup from a trusted computer.", {}, @@ -294,17 +248,19 @@ export default class CreateKeyBackupDialog extends React.PureComponent {

- -
- {strengthMeter} - {helpText} -
@@ -312,7 +268,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { primaryButton={_t('Next')} onPrimaryButtonClick={this._onPassPhraseNextClick} hasCancel={false} - disabled={!this._passPhraseIsValid()} + disabled={!this.state.passPhraseValid} />
diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index b77c71d08c..12b71206d0 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -61,7 +61,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this._recoveryKey = null; this._recoveryKeyNode = null; - this._setZxcvbnResultTimeout = null; this._backupKey = null; this.state = { @@ -100,9 +99,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { componentWillUnmount() { MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatusChange); - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } } async _fetchBackupInfo() { @@ -504,7 +500,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { onValidate={this._onPassPhraseValidate} fieldRef={this._passphraseField} autoFocus={true} - label={_td("Enter a recovery passphrase")} labelEnterPassword={_td("Enter a recovery passphrase")} labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 82b2a752ea..ec018d5e84 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1888,6 +1888,10 @@ "Your Modular server": "Your Modular server", "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.", "Server Name": "Server Name", + "Enter password": "Enter password", + "Nice, strong password!": "Nice, strong password!", + "Password is allowed, but unsafe": "Password is allowed, but unsafe", + "Keep going...": "Keep going...", "The email field must not be blank.": "The email field must not be blank.", "The username field must not be blank.": "The username field must not be blank.", "The phone number field must not be blank.": "The phone number field must not be blank.", @@ -1902,10 +1906,6 @@ "Use an email address to recover your account": "Use an email address to recover your account", "Enter email address (required on this homeserver)": "Enter email address (required on this homeserver)", "Doesn't look like a valid email address": "Doesn't look like a valid email address", - "Enter password": "Enter password", - "Password is allowed, but unsafe": "Password is allowed, but unsafe", - "Nice, strong password!": "Nice, strong password!", - "Keep going...": "Keep going...", "Passwords don't match": "Passwords don't match", "Other users can invite you to rooms using your contact details": "Other users can invite you to rooms using your contact details", "Enter phone number (required on this homeserver)": "Enter phone number (required on this homeserver)", @@ -2199,9 +2199,9 @@ "Restore": "Restore", "You'll need to authenticate with the server to confirm the upgrade.": "You'll need to authenticate with the server to confirm the upgrade.", "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.", - "Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.", "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:", "Enter a recovery passphrase": "Enter a recovery passphrase", + "Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.", "Back up encrypted message keys": "Back up encrypted message keys", "Set up with a recovery key": "Set up with a recovery key", "That matches!": "That matches!", @@ -2229,7 +2229,6 @@ "Unable to set up secret storage": "Unable to set up secret storage", "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.", "For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.", - "Enter a recovery passphrase...": "Enter a recovery passphrase...", "Please enter your recovery passphrase a second time to confirm.": "Please enter your recovery passphrase a second time to confirm.", "Repeat your recovery passphrase...": "Repeat your recovery passphrase...", "Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).",