From abfa593791975e48440f32cdb1bbff6f0491dd88 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 28 Jan 2020 16:36:07 +0000 Subject: [PATCH 1/4] Make the 'encryption upgrade' flow better Fixes https://github.com/vector-im/riot-web/issues/12086 --- .../CreateSecretStorageDialog.js | 92 ++++++++++--------- .../keybackup/RestoreKeyBackupDialog.js | 2 +- src/i18n/strings/en_EN.json | 3 +- 3 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 0867cae6f4..69dd13f46e 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -25,15 +25,14 @@ import { _t } from '../../../../languageHandler'; import Modal from '../../../../Modal'; const PHASE_LOADING = 0; -const PHASE_RESTORE_KEY_BACKUP = 1; -const PHASE_MIGRATE = 2; -const PHASE_PASSPHRASE = 3; -const PHASE_PASSPHRASE_CONFIRM = 4; -const PHASE_SHOWKEY = 5; -const PHASE_KEEPITSAFE = 6; -const PHASE_STORING = 7; -const PHASE_DONE = 8; -const PHASE_OPTOUT_CONFIRM = 9; +const PHASE_MIGRATE = 1; +const PHASE_PASSPHRASE = 2; +const PHASE_PASSPHRASE_CONFIRM = 3; +const PHASE_SHOWKEY = 4; +const PHASE_KEEPITSAFE = 5; +const PHASE_STORING = 6; +const PHASE_DONE = 7; +const PHASE_OPTOUT_CONFIRM = 8; 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. @@ -58,7 +57,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { accountPassword: PropTypes.string, }; - defaultProps = { + static defaultProps = { hasCancel: true, }; @@ -110,9 +109,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { MatrixClientPeg.get().isCryptoEnabled() && await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo) ); - const phase = backupInfo ? - (backupSigStatus.usable ? PHASE_MIGRATE : PHASE_RESTORE_KEY_BACKUP) : - PHASE_PASSPHRASE; + const phase = backupInfo ? PHASE_MIGRATE : PHASE_PASSPHRASE; this.setState({ phase, @@ -151,9 +148,18 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this._recoveryKeyNode = n; } + _onSkipClick = () => { + // TODO: add confirmation + this.props.onFinished(false); + } + _onMigrateFormSubmit = (e) => { e.preventDefault(); - this._bootstrapSecretStorage(); + if (this.state.backupSigStatus.usable) { + this._bootstrapSecretStorage(); + } else { + this._restoreBackup(); + } } _onCopyClick = () => { @@ -228,6 +234,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { } catch (e) { if (this.state.canUploadKeysWithPasswordOnly && e.httpStatus === 401 && e.data.flows) { this.setState({ + accountPassword: '', accountPasswordCorrect: false, phase: PHASE_MIGRATE, }); @@ -246,12 +253,22 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this.props.onFinished(true); } - _onRestoreKeyBackupClick = () => { + _restoreBackup = async () => { const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog'); - Modal.createTrackedDialog( + const { finished } = Modal.createTrackedDialog( 'Restore Backup', '', RestoreKeyBackupDialog, {showSummary: false}, null, /* priority = */ false, /* static = */ true, ); + + await finished; + await this._fetchBackupInfo(); + if ( + this.state.backupSigStatus.usable && + this.state.canUploadKeysWithPasswordOnly && + this.state.accountPassword + ) { + this._bootstrapSecretStorage(); + } } _onOptOutClick = () => { @@ -367,23 +384,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { }); } - _renderPhaseRestoreKeyBackup() { - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - return
-

{_t( - "Key Backup is enabled on your account but has not been set " + - "up from this session. To set up secret storage, " + - "restore your key backup.", - )}

- - -
; - } - _renderPhaseMigrate() { // TODO: This is a temporary screen so people who have the labs flag turned on and // click the button are aware they're making a change to their account. @@ -392,9 +392,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent { // https://github.com/vector-im/riot-web/issues/11696 const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const Field = sdk.getComponent('views.elements.Field'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); let authPrompt; - if (this.state.canUploadKeysWithPasswordOnly) { + let nextCaption = _t("Next"); + if (!this.state.backupSigStatus.usable) { + authPrompt =
+
{_t("Restore your key backup to upgrade your encryption")}
+
; + nextCaption = _t("Restore"); + } else if (this.state.canUploadKeysWithPasswordOnly) { authPrompt =
{_t("Enter your account password to confirm the upgrade:")}
{authPrompt}
- + > + + ; } @@ -678,8 +688,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { _titleForPhase(phase) { switch (phase) { - case PHASE_RESTORE_KEY_BACKUP: - return _t('Restore your Key Backup'); case PHASE_MIGRATE: return _t('Upgrade your encryption'); case PHASE_PASSPHRASE: @@ -722,9 +730,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { case PHASE_LOADING: content = this._renderBusyPhase(); break; - case PHASE_RESTORE_KEY_BACKUP: - content = this._renderPhaseRestoreKeyBackup(); - break; case PHASE_MIGRATE: content = this._renderPhaseMigrate(); break; @@ -763,6 +768,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { title={this._titleForPhase(this.state.phase)} headerImage={headerImage} hasCancel={this.props.hasCancel && [PHASE_PASSPHRASE].includes(this.state.phase)} + fixedWidth={false} >
{content} diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 0c432ba542..4466775d3a 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -39,7 +39,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { showSummary: PropTypes.bool, }; - defaultProps = { + static defaultProps = { showSummary: true, }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 23ca730d97..75aadc02bd 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1991,7 +1991,7 @@ "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.", "File to import": "File to import", "Import": "Import", - "Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.": "Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.", + "Restore your key backup to upgrade your encryption": "Restore your key backup to upgrade your encryption", "Restore": "Restore", "Enter your account password to confirm the upgrade:": "Enter your account password to confirm the upgrade:", "You'll need to authenticate with the server to confirm the upgrade.": "You'll need to authenticate with the server to confirm the upgrade.", @@ -2022,7 +2022,6 @@ "Verify other users in their profile.": "Verify other users in their profile.", "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.", "Set up secret storage": "Set up secret storage", - "Restore your Key Backup": "Restore your Key Backup", "Upgrade your encryption": "Upgrade your encryption", "Recovery key": "Recovery key", "Keep it safe": "Keep it safe", From 546acb696dd7c2313ebab2cc6f4a29122c6c4b1f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 28 Jan 2020 17:15:50 +0000 Subject: [PATCH 2/4] Add confirmation to skip button Re-using the opt out phase which was unused --- .../CreateSecretStorageDialog.js | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 69dd13f46e..b08b6c5624 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -32,7 +32,7 @@ const PHASE_SHOWKEY = 4; const PHASE_KEEPITSAFE = 5; const PHASE_STORING = 6; const PHASE_DONE = 7; -const PHASE_OPTOUT_CONFIRM = 8; +const PHASE_CONFIRM_SKIP = 8; 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. @@ -148,11 +148,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this._recoveryKeyNode = n; } - _onSkipClick = () => { - // TODO: add confirmation - this.props.onFinished(false); - } - _onMigrateFormSubmit = (e) => { e.preventDefault(); if (this.state.backupSigStatus.usable) { @@ -271,8 +266,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { } } - _onOptOutClick = () => { - this.setState({phase: PHASE_OPTOUT_CONFIRM}); + _onSkipSetupClick = () => { + this.setState({phase: PHASE_CONFIRM_SKIP}); } _onSetUpClick = () => { @@ -496,7 +491,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { disabled={!this._passPhraseIsValid()} > @@ -564,7 +559,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { disabled={this.state.passPhrase !== this.state.passPhraseConfirm} > @@ -669,19 +664,18 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
; } - _renderPhaseOptOutConfirm() { + _renderPhaseSkipConfirm() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return
{_t( - "Without setting up secret storage, you won't be able to restore your " + - "access to encrypted messages or your cross-signing identity for " + - "verifying other devices if you log out or use another device.", + "Without completing security on this device, it won’t have " + + "access to encrypted messages.", )} - - +
; } @@ -694,8 +688,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { return _t('Set up encryption'); case PHASE_PASSPHRASE_CONFIRM: return _t('Confirm passphrase'); - case PHASE_OPTOUT_CONFIRM: - return _t('Warning!'); + case PHASE_CONFIRM_SKIP: + return _t('Are you sure?'); case PHASE_SHOWKEY: return _t('Recovery key'); case PHASE_KEEPITSAFE: @@ -751,8 +745,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { case PHASE_DONE: content = this._renderPhaseDone(); break; - case PHASE_OPTOUT_CONFIRM: - content = this._renderPhaseOptOutConfirm(); + case PHASE_CONFIRM_SKIP: + content = this._renderPhaseSkipConfirm(); break; } } From e1e35a53e43f9e8567da0e671d91cab1e640a555 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 28 Jan 2020 17:18:09 +0000 Subject: [PATCH 3/4] i18n --- src/i18n/strings/en_EN.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 75aadc02bd..6a884d6f25 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2020,8 +2020,6 @@ "Copy it to your personal cloud storage": "Copy it to your personal cloud storage", "This device can now verify other devices, granting them access to encrypted messages and marking them as trusted for other users.": "This device can now verify other devices, granting them access to encrypted messages and marking them as trusted for other users.", "Verify other users in their profile.": "Verify other users in their profile.", - "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.", - "Set up secret storage": "Set up secret storage", "Upgrade your encryption": "Upgrade your encryption", "Recovery key": "Recovery key", "Keep it safe": "Keep it safe", From 5e1b42b3505adb53652a9efb60bd7eda8df15a52 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 28 Jan 2020 17:21:42 +0000 Subject: [PATCH 4/4] unused component --- .../views/dialogs/secretstorage/CreateSecretStorageDialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index b08b6c5624..62a80d0cdc 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -387,7 +387,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { // https://github.com/vector-im/riot-web/issues/11696 const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const Field = sdk.getComponent('views.elements.Field'); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); let authPrompt; let nextCaption = _t("Next");