From 46bc6b1d149ed2c93914e5ee9bdfa3eb596df1e7 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Sep 2020 11:37:06 +0100 Subject: [PATCH 01/10] Rename key backup to secure backup --- res/css/_components.scss | 2 +- ...ckupPanel.scss => _SecureBackupPanel.scss} | 2 +- ...KeyBackupPanel.js => SecureBackupPanel.js} | 2 +- .../tabs/user/SecurityUserSettingsTab.js | 10 +-- src/i18n/strings/en_EN.json | 62 +++++++++---------- 5 files changed, 39 insertions(+), 39 deletions(-) rename res/css/views/settings/{_KeyBackupPanel.scss => _SecureBackupPanel.scss} (95%) rename src/components/views/settings/{KeyBackupPanel.js => SecureBackupPanel.js} (99%) diff --git a/res/css/_components.scss b/res/css/_components.scss index 45ed6b3300..be1818cf8e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -199,10 +199,10 @@ @import "./views/settings/_E2eAdvancedPanel.scss"; @import "./views/settings/_EmailAddresses.scss"; @import "./views/settings/_IntegrationManager.scss"; -@import "./views/settings/_KeyBackupPanel.scss"; @import "./views/settings/_Notifications.scss"; @import "./views/settings/_PhoneNumbers.scss"; @import "./views/settings/_ProfileSettings.scss"; +@import "./views/settings/_SecureBackupPanel.scss"; @import "./views/settings/_SetIdServer.scss"; @import "./views/settings/_SetIntegrationManager.scss"; @import "./views/settings/_UpdateCheckButton.scss"; diff --git a/res/css/views/settings/_KeyBackupPanel.scss b/res/css/views/settings/_SecureBackupPanel.scss similarity index 95% rename from res/css/views/settings/_KeyBackupPanel.scss rename to res/css/views/settings/_SecureBackupPanel.scss index 872162caad..3522a81e32 100644 --- a/res/css/views/settings/_KeyBackupPanel.scss +++ b/res/css/views/settings/_SecureBackupPanel.scss @@ -1,6 +1,6 @@ /* Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/components/views/settings/KeyBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js similarity index 99% rename from src/components/views/settings/KeyBackupPanel.js rename to src/components/views/settings/SecureBackupPanel.js index 8a74276f58..35009bc22d 100644 --- a/src/components/views/settings/KeyBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -23,7 +23,7 @@ import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; import { isSecureBackupRequired } from '../../../utils/WellKnownUtils'; -export default class KeyBackupPanel extends React.PureComponent { +export default class SecureBackupPanel extends React.PureComponent { constructor(props) { super(props); diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 90dcc0b658..c25ac438e0 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -29,6 +29,7 @@ import {sleep} from "../../../../../utils/promise"; import dis from "../../../../../dispatcher/dispatcher"; import {privateShouldBeEncrypted} from "../../../../../createRoom"; import {SettingLevel} from "../../../../../settings/SettingLevel"; +import SecureBackupPanel from "../../SecureBackupPanel"; export class IgnoredUser extends React.Component { static propTypes = { @@ -288,12 +289,11 @@ export default class SecurityUserSettingsTab extends React.Component { const SettingsFlag = sdk.getComponent('views.elements.SettingsFlag'); const EventIndexPanel = sdk.getComponent('views.settings.EventIndexPanel'); - const KeyBackupPanel = sdk.getComponent('views.settings.KeyBackupPanel'); - const keyBackup = ( + const secureBackup = (
- {_t("Key backup")} + {_t("Secure Backup")}
- +
); @@ -352,7 +352,7 @@ export default class SecurityUserSettingsTab extends React.Component {
{_t("Encryption")}
- {keyBackup} + {secureBackup} {eventIndex} {crossSigning} {this._renderCurrentDeviceInfo()} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 95b6c23a77..464edba6ce 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -697,36 +697,6 @@ "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", "The integration manager is offline or it cannot reach your homeserver.": "The integration manager is offline or it cannot reach your homeserver.", - "Delete Backup": "Delete Backup", - "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.", - "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", - "Unable to load key backup status": "Unable to load key backup status", - "Restore from Backup": "Restore from Backup", - "This session is backing up your keys. ": "This session is backing up your keys. ", - "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.", - "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.", - "Connect this session to Key Backup": "Connect this session to Key Backup", - "not stored": "not stored", - "Backing up %(sessionsRemaining)s keys...": "Backing up %(sessionsRemaining)s keys...", - "All keys backed up": "All keys backed up", - "Backup has a valid signature from this user": "Backup has a valid signature from this user", - "Backup has a invalid signature from this user": "Backup has a invalid signature from this user", - "Backup has a signature from unknown user with ID %(deviceId)s": "Backup has a signature from unknown user with ID %(deviceId)s", - "Backup has a signature from unknown session with ID %(deviceId)s": "Backup has a signature from unknown session with ID %(deviceId)s", - "Backup has a valid signature from this session": "Backup has a valid signature from this session", - "Backup has an invalid signature from this session": "Backup has an invalid signature from this session", - "Backup has a valid signature from verified session ": "Backup has a valid signature from verified session ", - "Backup has a valid signature from unverified session ": "Backup has a valid signature from unverified session ", - "Backup has an invalid signature from verified session ": "Backup has an invalid signature from verified session ", - "Backup has an invalid signature from unverified session ": "Backup has an invalid signature from unverified session ", - "Backup is not signed by any of your sessions": "Backup is not signed by any of your sessions", - "This backup is trusted because it has been restored on this session": "This backup is trusted because it has been restored on this session", - "Backup version: ": "Backup version: ", - "Algorithm: ": "Algorithm: ", - "Backup key stored: ": "Backup key stored: ", - "Your keys are not being backed up from this session.": "Your keys are not being backed up from this session.", - "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.", - "Start using Key Backup": "Start using Key Backup", "Error saving email notification preferences": "Error saving email notification preferences", "An error occurred whilst saving your email notification preferences.": "An error occurred whilst saving your email notification preferences.", "Keywords": "Keywords", @@ -758,6 +728,36 @@ "Display Name": "Display Name", "Profile picture": "Profile picture", "Save": "Save", + "Delete Backup": "Delete Backup", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", + "Unable to load key backup status": "Unable to load key backup status", + "Restore from Backup": "Restore from Backup", + "This session is backing up your keys. ": "This session is backing up your keys. ", + "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.", + "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.", + "Connect this session to Key Backup": "Connect this session to Key Backup", + "not stored": "not stored", + "Backing up %(sessionsRemaining)s keys...": "Backing up %(sessionsRemaining)s keys...", + "All keys backed up": "All keys backed up", + "Backup has a valid signature from this user": "Backup has a valid signature from this user", + "Backup has a invalid signature from this user": "Backup has a invalid signature from this user", + "Backup has a signature from unknown user with ID %(deviceId)s": "Backup has a signature from unknown user with ID %(deviceId)s", + "Backup has a signature from unknown session with ID %(deviceId)s": "Backup has a signature from unknown session with ID %(deviceId)s", + "Backup has a valid signature from this session": "Backup has a valid signature from this session", + "Backup has an invalid signature from this session": "Backup has an invalid signature from this session", + "Backup has a valid signature from verified session ": "Backup has a valid signature from verified session ", + "Backup has a valid signature from unverified session ": "Backup has a valid signature from unverified session ", + "Backup has an invalid signature from verified session ": "Backup has an invalid signature from verified session ", + "Backup has an invalid signature from unverified session ": "Backup has an invalid signature from unverified session ", + "Backup is not signed by any of your sessions": "Backup is not signed by any of your sessions", + "This backup is trusted because it has been restored on this session": "This backup is trusted because it has been restored on this session", + "Backup version: ": "Backup version: ", + "Algorithm: ": "Algorithm: ", + "Backup key stored: ": "Backup key stored: ", + "Your keys are not being backed up from this session.": "Your keys are not being backed up from this session.", + "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.", + "Start using Key Backup": "Start using Key Backup", "Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS", "Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)", "Could not connect to Identity Server": "Could not connect to Identity Server", @@ -904,7 +904,7 @@ "Bulk options": "Bulk options", "Accept all %(invitedRooms)s invites": "Accept all %(invitedRooms)s invites", "Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites", - "Key backup": "Key backup", + "Secure Backup": "Secure Backup", "Message search": "Message search", "Cross-signing": "Cross-signing", "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.", From e3f47525e55be59888654ee4d80f097e73ec198b Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Sep 2020 11:37:56 +0100 Subject: [PATCH 02/10] Rename backup CSS classes --- res/css/views/settings/_SecureBackupPanel.scss | 12 ++++++------ src/components/views/settings/SecureBackupPanel.js | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/res/css/views/settings/_SecureBackupPanel.scss b/res/css/views/settings/_SecureBackupPanel.scss index 3522a81e32..548e72fbc3 100644 --- a/res/css/views/settings/_SecureBackupPanel.scss +++ b/res/css/views/settings/_SecureBackupPanel.scss @@ -15,23 +15,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_KeyBackupPanel_sigValid, .mx_KeyBackupPanel_sigInvalid, -.mx_KeyBackupPanel_deviceVerified, .mx_KeyBackupPanel_deviceNotVerified { +.mx_SecureBackupPanel_sigValid, .mx_SecureBackupPanel_sigInvalid, +.mx_SecureBackupPanel_deviceVerified, .mx_SecureBackupPanel_deviceNotVerified { font-weight: bold; } -.mx_KeyBackupPanel_sigValid, .mx_KeyBackupPanel_deviceVerified { +.mx_SecureBackupPanel_sigValid, .mx_SecureBackupPanel_deviceVerified { color: $e2e-verified-color; } -.mx_KeyBackupPanel_sigInvalid, .mx_KeyBackupPanel_deviceNotVerified { +.mx_SecureBackupPanel_sigInvalid, .mx_SecureBackupPanel_deviceNotVerified { color: $e2e-warning-color; } -.mx_KeyBackupPanel_deviceName { +.mx_SecureBackupPanel_deviceName { font-style: italic; } -.mx_KeyBackupPanel_buttonRow { +.mx_SecureBackupPanel_buttonRow { margin: 1em 0; } diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index 35009bc22d..684818aa1d 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -229,14 +229,14 @@ export default class SecureBackupPanel extends React.PureComponent { let backupSigStatuses = this.state.backupSigStatus.sigs.map((sig, i) => { const deviceName = sig.device ? (sig.device.getDisplayName() || sig.device.deviceId) : null; const validity = sub => - + {sub} ; const verify = sub => - + {sub} ; - const device = sub => {deviceName}; + const device = sub => {deviceName}; const fromThisDevice = ( sig.device && sig.device.getFingerprint() === MatrixClientPeg.get().getDeviceEd25519Key() @@ -324,7 +324,7 @@ export default class SecureBackupPanel extends React.PureComponent { } const buttonRow = ( -
+
{restoreButtonCaption}     @@ -355,7 +355,7 @@ export default class SecureBackupPanel extends React.PureComponent {

{encryptedMessageAreEncrypted}

{_t("Back up your keys before signing out to avoid losing them.")}

-
+
{_t("Start using Key Backup")} From 19e9e0137c027cbf1766b1fed4d8c7471be27d61 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Sep 2020 13:05:30 +0100 Subject: [PATCH 03/10] Reorganise backup panel so feature description is always present This ensure the feature description is always shown at the top. --- .../views/settings/SecureBackupPanel.js | 77 ++++++++++--------- src/i18n/strings/en_EN.json | 3 +- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index 684818aa1d..ec345f6317 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -165,31 +165,30 @@ export default class SecureBackupPanel extends React.PureComponent { render() { const Spinner = sdk.getComponent("elements.Spinner"); const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); - const encryptedMessageAreEncrypted = _t( - "Encrypted messages are secured with end-to-end encryption. " + - "Only you and the recipient(s) have the keys to read these messages.", + const featureDescription = _t( + "Back up your encryption keys with your account data in case you " + + "lose access to your sessions. Your keys will be secured with a " + + "unique Recovery Key.", ); + let statusDescription; + let details; + let actions; if (this.state.error) { - return ( + statusDescription = (
{_t("Unable to load key backup status")}
); } else if (this.state.loading) { - return ; + statusDescription = ; } else if (this.state.backupInfo) { - let clientBackupStatus; let restoreButtonCaption = _t("Restore from Backup"); if (MatrixClientPeg.get().getKeyBackupEnabled()) { - clientBackupStatus =
-

{encryptedMessageAreEncrypted}

-

✅ {_t("This session is backing up your keys. ")}

-
; + statusDescription =

✅ {_t("This session is backing up your keys. ")}

; } else { - clientBackupStatus =
-

{encryptedMessageAreEncrypted}

+ statusDescription = <>

{_t( "This session is not backing up your keys, " + "but you do have an existing backup you can restore from " + @@ -200,7 +199,7 @@ export default class SecureBackupPanel extends React.PureComponent { "Connect this session to key backup before signing out to avoid " + "losing any keys that may only be on this session.", )}

-
; + ; restoreButtonCaption = _t("Connect this session to Key Backup"); } @@ -323,17 +322,7 @@ export default class SecureBackupPanel extends React.PureComponent { ; } - const buttonRow = ( -
- - {restoreButtonCaption} -     - {deleteBackupButton} -
- ); - - return
-
{clientBackupStatus}
+ details = (
{_t("Advanced")}
{_t("Backup version: ")}{this.state.backupInfo.version}
@@ -343,24 +332,40 @@ export default class SecureBackupPanel extends React.PureComponent {
{backupSigStatuses}
{trustedLocally}
- {buttonRow} -
; - } else { - return
-
-

{_t( - "Your keys are not being backed up from this session.", {}, - {b: sub => {sub}}, - )}

-

{encryptedMessageAreEncrypted}

-

{_t("Back up your keys before signing out to avoid losing them.")}

+ ); + + actions = ( +
+ + {restoreButtonCaption} +     + {deleteBackupButton}
+ ); + } else { + statusDescription = <> +

{_t( + "Your keys are not being backed up from this session.", {}, + {b: sub => {sub}}, + )}

+

{_t("Back up your keys before signing out to avoid losing them.")}

+ ; + actions = (
{_t("Start using Key Backup")}
-
; + ); } + + return ( +
+

{featureDescription}

+ {statusDescription} + {details} + {actions} +
+ ); } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 464edba6ce..843373ad07 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -730,7 +730,7 @@ "Save": "Save", "Delete Backup": "Delete Backup", "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.", - "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", + "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.", "Unable to load key backup status": "Unable to load key backup status", "Restore from Backup": "Restore from Backup", "This session is backing up your keys. ": "This session is backing up your keys. ", @@ -1732,6 +1732,7 @@ "Clear cache and resync": "Clear cache and resync", "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!", "Updating %(brand)s": "Updating %(brand)s", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", "I don't want my encrypted messages": "I don't want my encrypted messages", "Manually export keys": "Manually export keys", "You'll lose access to your encrypted messages": "You'll lose access to your encrypted messages", From cc2c179a0f429fe2b5890bd9ffcf76f78c67521d Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Sep 2020 13:18:57 +0100 Subject: [PATCH 04/10] Set default text colour for Settings tabs --- res/css/views/settings/tabs/_SettingsTab.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index 5f00ed86f7..892f5fe744 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -14,6 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_SettingsTab { + color: $muted-fg-color; +} + .mx_SettingsTab_warningText { color: $warning-color; } From b484bc5e098e72b09d8904042f4cf67540c304db Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Sep 2020 13:52:14 +0100 Subject: [PATCH 05/10] Rearrange backup status to always have advanced --- .../views/settings/SecureBackupPanel.js | 68 ++++++++++--------- src/i18n/strings/en_EN.json | 6 +- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index ec345f6317..93e28c8bc9 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -165,24 +165,28 @@ export default class SecureBackupPanel extends React.PureComponent { render() { const Spinner = sdk.getComponent("elements.Spinner"); const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); - const featureDescription = _t( - "Back up your encryption keys with your account data in case you " + - "lose access to your sessions. Your keys will be secured with a " + - "unique Recovery Key.", - ); + + const { + loading, + error, + backupInfo, + backupSigStatus, + backupKeyStored, + sessionsRemaining, + } = this.state; let statusDescription; - let details; + let extraDetails; let actions; - if (this.state.error) { + if (error) { statusDescription = (
{_t("Unable to load key backup status")}
); - } else if (this.state.loading) { + } else if (loading) { statusDescription = ; - } else if (this.state.backupInfo) { + } else if (backupInfo) { let restoreButtonCaption = _t("Restore from Backup"); if (MatrixClientPeg.get().getKeyBackupEnabled()) { @@ -203,15 +207,7 @@ export default class SecureBackupPanel extends React.PureComponent { restoreButtonCaption = _t("Connect this session to Key Backup"); } - let keyStatus; - if (this.state.backupKeyStored === true) { - keyStatus = _t("in secret storage"); - } else { - keyStatus = _t("not stored"); - } - let uploadStatus; - const { sessionsRemaining } = this.state; if (!MatrixClientPeg.get().getKeyBackupEnabled()) { // No upload status to show when backup disabled. uploadStatus = ""; @@ -225,7 +221,7 @@ export default class SecureBackupPanel extends React.PureComponent {
; } - let backupSigStatuses = this.state.backupSigStatus.sigs.map((sig, i) => { + let backupSigStatuses = backupSigStatus.sigs.map((sig, i) => { const deviceName = sig.device ? (sig.device.getDisplayName() || sig.device.deviceId) : null; const validity = sub => @@ -306,12 +302,12 @@ export default class SecureBackupPanel extends React.PureComponent { {sigStatus}
; }); - if (this.state.backupSigStatus.sigs.length === 0) { + if (backupSigStatus.sigs.length === 0) { backupSigStatuses = _t("Backup is not signed by any of your sessions"); } let trustedLocally; - if (this.state.backupSigStatus.trusted_locally) { + if (backupSigStatus.trusted_locally) { trustedLocally = _t("This backup is trusted because it has been restored on this session"); } @@ -322,17 +318,13 @@ export default class SecureBackupPanel extends React.PureComponent { ; } - details = ( -
- {_t("Advanced")} -
{_t("Backup version: ")}{this.state.backupInfo.version}
-
{_t("Algorithm: ")}{this.state.backupInfo.algorithm}
-
{_t("Backup key stored: ")}{keyStatus}
- {uploadStatus} -
{backupSigStatuses}
-
{trustedLocally}
-
- ); + extraDetails = <> +
{_t("Backup version: ")}{backupInfo.version}
+
{_t("Algorithm: ")}{backupInfo.algorithm}
+ {uploadStatus} +
{backupSigStatuses}
+
{trustedLocally}
+ ; actions = (
@@ -361,9 +353,19 @@ export default class SecureBackupPanel extends React.PureComponent { return (
-

{featureDescription}

+

{_t( + "Back up your encryption keys with your account data in case you " + + "lose access to your sessions. Your keys will be secured with a " + + "unique Recovery Key.", + )}

{statusDescription} - {details} +
+ {_t("Advanced")} +
{_t("Backup key stored: ")}{ + backupKeyStored === true ? _t("in secret storage") : _t("not stored") + }
+ {extraDetails} +
{actions}
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 843373ad07..49e8b1d2e8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -730,14 +730,12 @@ "Save": "Save", "Delete Backup": "Delete Backup", "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.", - "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.", "Unable to load key backup status": "Unable to load key backup status", "Restore from Backup": "Restore from Backup", "This session is backing up your keys. ": "This session is backing up your keys. ", "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.", "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.", "Connect this session to Key Backup": "Connect this session to Key Backup", - "not stored": "not stored", "Backing up %(sessionsRemaining)s keys...": "Backing up %(sessionsRemaining)s keys...", "All keys backed up": "All keys backed up", "Backup has a valid signature from this user": "Backup has a valid signature from this user", @@ -754,10 +752,12 @@ "This backup is trusted because it has been restored on this session": "This backup is trusted because it has been restored on this session", "Backup version: ": "Backup version: ", "Algorithm: ": "Algorithm: ", - "Backup key stored: ": "Backup key stored: ", "Your keys are not being backed up from this session.": "Your keys are not being backed up from this session.", "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.", "Start using Key Backup": "Start using Key Backup", + "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.", + "Backup key stored: ": "Backup key stored: ", + "not stored": "not stored", "Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS", "Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)", "Could not connect to Identity Server": "Could not connect to Identity Server", From 217f14591021c2718408aac1935132edeae3611e Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Sep 2020 13:55:25 +0100 Subject: [PATCH 06/10] Switch to imports in backup panel --- src/components/views/settings/SecureBackupPanel.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index 93e28c8bc9..683120f5b6 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -17,11 +17,14 @@ limitations under the License. import React from 'react'; -import * as sdk from '../../../index'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; import { isSecureBackupRequired } from '../../../utils/WellKnownUtils'; +import Spinner from '../elements/Spinner'; +import AccessibleButton from '../elements/AccessibleButton'; +import QuestionDialog from '../dialogs/QuestionDialog'; +import RestoreKeyBackupDialog from '../dialogs/keybackup/RestoreKeyBackupDialog'; export default class SecureBackupPanel extends React.PureComponent { constructor(props) { @@ -135,7 +138,6 @@ export default class SecureBackupPanel extends React.PureComponent { } _deleteBackup = () => { - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); Modal.createTrackedDialog('Delete Backup', '', QuestionDialog, { title: _t('Delete Backup'), description: _t( @@ -155,7 +157,6 @@ export default class SecureBackupPanel extends React.PureComponent { } _restoreBackup = async () => { - const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog'); Modal.createTrackedDialog( 'Restore Backup', '', RestoreKeyBackupDialog, null, null, /* priority = */ false, /* static = */ true, @@ -163,9 +164,6 @@ export default class SecureBackupPanel extends React.PureComponent { } render() { - const Spinner = sdk.getComponent("elements.Spinner"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); - const { loading, error, From 2fbb551035efa159bedbbd52211a25e3da9604ab Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Sep 2020 14:09:34 +0100 Subject: [PATCH 07/10] Put backup details in a table --- .../views/settings/_SecureBackupPanel.scss | 12 +++++++++ .../views/settings/SecureBackupPanel.js | 26 +++++++++++++++---- src/i18n/strings/en_EN.json | 6 ++--- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/res/css/views/settings/_SecureBackupPanel.scss b/res/css/views/settings/_SecureBackupPanel.scss index 548e72fbc3..587cab8f36 100644 --- a/res/css/views/settings/_SecureBackupPanel.scss +++ b/res/css/views/settings/_SecureBackupPanel.scss @@ -35,3 +35,15 @@ limitations under the License. .mx_SecureBackupPanel_buttonRow { margin: 1em 0; } + +.mx_SecureBackupPanel_statusList { + border-spacing: 0; + + td { + padding: 0; + + &:first-of-type { + padding-inline-end: 1em; + } + } +} diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index 683120f5b6..7f7a014df9 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -174,6 +174,7 @@ export default class SecureBackupPanel extends React.PureComponent { } = this.state; let statusDescription; + let extraDetailsTableRows; let extraDetails; let actions; if (error) { @@ -316,9 +317,18 @@ export default class SecureBackupPanel extends React.PureComponent { ; } + extraDetailsTableRows = <> + + {_t("Backup version:")} + {backupInfo.version} + + + {_t("Algorithm:")} + {backupInfo.algorithm} + + ; + extraDetails = <> -
{_t("Backup version: ")}{backupInfo.version}
-
{_t("Algorithm: ")}{backupInfo.algorithm}
{uploadStatus}
{backupSigStatuses}
{trustedLocally}
@@ -359,9 +369,15 @@ export default class SecureBackupPanel extends React.PureComponent { {statusDescription}
{_t("Advanced")} -
{_t("Backup key stored: ")}{ - backupKeyStored === true ? _t("in secret storage") : _t("not stored") - }
+ + + + + + {extraDetailsTableRows} +
{_t("Backup key stored:")}{ + backupKeyStored === true ? _t("in secret storage") : _t("not stored") + }
{extraDetails}
{actions} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 49e8b1d2e8..476ce11b2d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -750,13 +750,13 @@ "Backup has an invalid signature from unverified session ": "Backup has an invalid signature from unverified session ", "Backup is not signed by any of your sessions": "Backup is not signed by any of your sessions", "This backup is trusted because it has been restored on this session": "This backup is trusted because it has been restored on this session", - "Backup version: ": "Backup version: ", - "Algorithm: ": "Algorithm: ", + "Backup version:": "Backup version:", + "Algorithm:": "Algorithm:", "Your keys are not being backed up from this session.": "Your keys are not being backed up from this session.", "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.", "Start using Key Backup": "Start using Key Backup", "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.", - "Backup key stored: ": "Backup key stored: ", + "Backup key stored:": "Backup key stored:", "not stored": "not stored", "Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS", "Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)", From aae68f7d1aa3fecb68a590a420de062ee695308f Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 8 Sep 2020 14:10:34 +0100 Subject: [PATCH 08/10] Move 4S status to backup panel This moves the various 4S status diagnostics to the backup panel and out of the cross-signing panel. The available actions are unchanged as part of this commit, but they will be updated next. --- .../views/settings/CrossSigningPanel.js | 61 +++------------ .../views/settings/SecureBackupPanel.js | 78 +++++++++++++++---- src/i18n/strings/en_EN.json | 18 +++-- 3 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index 3eeb072e2d..8ef68e4b2a 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -31,13 +31,13 @@ export default class CrossSigningPanel extends React.PureComponent { this.state = { error: null, - crossSigningPublicKeysOnDevice: false, - crossSigningPrivateKeysInStorage: false, - masterPrivateKeyCached: false, - selfSigningPrivateKeyCached: false, - userSigningPrivateKeyCached: false, - sessionBackupKeyCached: false, - secretStorageKeyInAccount: false, + crossSigningPublicKeysOnDevice: null, + crossSigningPrivateKeysInStorage: null, + masterPrivateKeyCached: null, + selfSigningPrivateKeyCached: null, + userSigningPrivateKeyCached: null, + homeserverSupportsCrossSigning: null, + crossSigningReady: null, }; } @@ -83,14 +83,9 @@ export default class CrossSigningPanel extends React.PureComponent { const masterPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("master")); const selfSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing")); const userSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing")); - const sessionBackupKeyFromCache = await cli._crypto.getSessionBackupPrivateKey(); - const sessionBackupKeyCached = !!(sessionBackupKeyFromCache); - const sessionBackupKeyWellFormed = sessionBackupKeyFromCache instanceof Uint8Array; - const secretStorageKeyInAccount = await secretStorage.hasKey(); const homeserverSupportsCrossSigning = await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"); const crossSigningReady = await cli.isCrossSigningReady(); - const secretStorageReady = await cli.isSecretStorageReady(); this.setState({ crossSigningPublicKeysOnDevice, @@ -98,12 +93,8 @@ export default class CrossSigningPanel extends React.PureComponent { masterPrivateKeyCached, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, - sessionBackupKeyCached, - sessionBackupKeyWellFormed, - secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, - secretStorageReady, }); } @@ -149,12 +140,8 @@ export default class CrossSigningPanel extends React.PureComponent { masterPrivateKeyCached, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, - sessionBackupKeyCached, - sessionBackupKeyWellFormed, - secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, - secretStorageReady, } = this.state; let errorSection; @@ -169,14 +156,9 @@ export default class CrossSigningPanel extends React.PureComponent { summarisedStatus =

{_t( "Your homeserver does not support cross-signing.", )}

; - } else if (crossSigningReady && secretStorageReady) { + } else if (crossSigningReady) { summarisedStatus =

✅ {_t( - "Cross-signing and secret storage are ready for use.", - )}

; - } else if (crossSigningReady && !secretStorageReady) { - summarisedStatus =

✅ {_t( - "Cross-signing is ready for use, but secret storage is " + - "currently not being used to backup your keys.", + "Cross-signing is ready for use.", )}

; } else if (crossSigningPrivateKeysInStorage) { summarisedStatus =

{_t( @@ -185,17 +167,15 @@ export default class CrossSigningPanel extends React.PureComponent { )}

; } else { summarisedStatus =

{_t( - "Cross-signing and secret storage are not yet set up.", + "Cross-signing is not set up.", )}

; } const keysExistAnywhere = ( - secretStorageKeyInAccount || crossSigningPrivateKeysInStorage || crossSigningPublicKeysOnDevice ); const keysExistEverywhere = ( - secretStorageKeyInAccount && crossSigningPrivateKeysInStorage && crossSigningPublicKeysOnDevice ); @@ -223,16 +203,6 @@ export default class CrossSigningPanel extends React.PureComponent { ); } - let sessionBackupKeyWellFormedText = ""; - if (sessionBackupKeyCached) { - sessionBackupKeyWellFormedText = ", "; - if (sessionBackupKeyWellFormed) { - sessionBackupKeyWellFormedText += _t("well formed"); - } else { - sessionBackupKeyWellFormedText += _t("unexpected type"); - } - } - return (
{summarisedStatus} @@ -259,17 +229,6 @@ export default class CrossSigningPanel extends React.PureComponent { {_t("User signing private key:")} {userSigningPrivateKeyCached ? _t("cached locally") : _t("not found locally")} - - {_t("Session backup key:")} - - {sessionBackupKeyCached ? _t("cached locally") : _t("not found locally")} - {sessionBackupKeyWellFormedText} - - - - {_t("Secret storage public key:")} - {secretStorageKeyInAccount ? _t("in account data") : _t("not found")} - {_t("Homeserver feature support:")} {homeserverSupportsCrossSigning ? _t("exists") : _t("not found")} diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index 7f7a014df9..0f43770288 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -34,9 +34,13 @@ export default class SecureBackupPanel extends React.PureComponent { this.state = { loading: true, error: null, + backupKeyStored: null, + backupKeyCached: null, + backupKeyWellFormed: null, + secretStorageKeyInAccount: null, + secretStorageReady: null, backupInfo: null, backupSigStatus: null, - backupKeyStored: null, sessionsRemaining: 0, }; } @@ -76,56 +80,73 @@ export default class SecureBackupPanel extends React.PureComponent { } async _checkKeyBackupStatus() { + this._getUpdatedDiagnostics(); try { const {backupInfo, trustInfo} = await MatrixClientPeg.get().checkKeyBackup(); - const backupKeyStored = Boolean(await MatrixClientPeg.get().isKeyBackupKeyStored()); this.setState({ + loading: false, + error: null, backupInfo, backupSigStatus: trustInfo, - backupKeyStored, - error: null, - loading: false, }); } catch (e) { console.log("Unable to fetch check backup status", e); if (this._unmounted) return; this.setState({ + loading: false, error: e, backupInfo: null, backupSigStatus: null, - backupKeyStored: null, - loading: false, }); } } async _loadBackupStatus() { - this.setState({loading: true}); + this.setState({ loading: true }); + this._getUpdatedDiagnostics(); try { const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo); - const backupKeyStored = await MatrixClientPeg.get().isKeyBackupKeyStored(); if (this._unmounted) return; this.setState({ + loading: false, error: null, backupInfo, backupSigStatus, - backupKeyStored, - loading: false, }); } catch (e) { console.log("Unable to fetch key backup status", e); if (this._unmounted) return; this.setState({ + loading: false, error: e, backupInfo: null, backupSigStatus: null, - backupKeyStored: null, - loading: false, }); } } + async _getUpdatedDiagnostics() { + const cli = MatrixClientPeg.get(); + const secretStorage = cli._crypto._secretStorage; + + const backupKeyStored = await cli.isKeyBackupKeyStored(); + const backupKeyFromCache = await cli._crypto.getSessionBackupPrivateKey(); + const backupKeyCached = !!(backupKeyFromCache); + const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array; + const secretStorageKeyInAccount = await secretStorage.hasKey(); + const secretStorageReady = await cli.isSecretStorageReady(); + + if (this._unmounted) return; + this.setState({ + backupKeyStored, + backupKeyCached, + backupKeyWellFormed, + secretStorageKeyInAccount, + secretStorageReady, + }); + } + _startNewBackup = () => { Modal.createTrackedDialogAsync('Key Backup', 'Key Backup', import('../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'), @@ -167,9 +188,13 @@ export default class SecureBackupPanel extends React.PureComponent { const { loading, error, + backupKeyStored, + backupKeyCached, + backupKeyWellFormed, + secretStorageKeyInAccount, + secretStorageReady, backupInfo, backupSigStatus, - backupKeyStored, sessionsRemaining, } = this.state; @@ -359,6 +384,16 @@ export default class SecureBackupPanel extends React.PureComponent { ); } + let backupKeyWellFormedText = ""; + if (backupKeyCached) { + backupKeyWellFormedText = ", "; + if (backupKeyWellFormed) { + backupKeyWellFormedText += _t("well formed"); + } else { + backupKeyWellFormedText += _t("unexpected type"); + } + } + return (

{_t( @@ -376,6 +411,21 @@ export default class SecureBackupPanel extends React.PureComponent { backupKeyStored === true ? _t("in secret storage") : _t("not stored") } + + {_t("Backup key cached:")} + + {backupKeyCached ? _t("cached locally") : _t("not found locally")} + {backupKeyWellFormedText} + + + + {_t("Secret storage public key:")} + {secretStorageKeyInAccount ? _t("in account data") : _t("not found")} + + + {_t("Secret storage:")} + {secretStorageReady ? _t("ready") : _t("not ready")} + {extraDetailsTableRows} {extraDetails} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 476ce11b2d..1bf431f6e0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -645,14 +645,11 @@ "Confirm password": "Confirm password", "Change Password": "Change Password", "Your homeserver does not support cross-signing.": "Your homeserver does not support cross-signing.", - "Cross-signing and secret storage are ready for use.": "Cross-signing and secret storage are ready for use.", - "Cross-signing is ready for use, but secret storage is currently not being used to backup your keys.": "Cross-signing is ready for use, but secret storage is currently not being used to backup your keys.", + "Cross-signing is ready for use.": "Cross-signing is ready for use.", "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.", - "Cross-signing and secret storage are not yet set up.": "Cross-signing and secret storage are not yet set up.", + "Cross-signing is not set up.": "Cross-signing is not set up.", "Reset cross-signing and secret storage": "Reset cross-signing and secret storage", "Bootstrap cross-signing and secret storage": "Bootstrap cross-signing and secret storage", - "well formed": "well formed", - "unexpected type": "unexpected type", "Cross-signing public keys:": "Cross-signing public keys:", "in memory": "in memory", "not found": "not found", @@ -663,9 +660,6 @@ "not found locally": "not found locally", "Self signing private key:": "Self signing private key:", "User signing private key:": "User signing private key:", - "Session backup key:": "Session backup key:", - "Secret storage public key:": "Secret storage public key:", - "in account data": "in account data", "Homeserver feature support:": "Homeserver feature support:", "exists": "exists", "Your homeserver does not support session management.": "Your homeserver does not support session management.", @@ -755,9 +749,17 @@ "Your keys are not being backed up from this session.": "Your keys are not being backed up from this session.", "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.", "Start using Key Backup": "Start using Key Backup", + "well formed": "well formed", + "unexpected type": "unexpected type", "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.", "Backup key stored:": "Backup key stored:", "not stored": "not stored", + "Backup key cached:": "Backup key cached:", + "Secret storage public key:": "Secret storage public key:", + "in account data": "in account data", + "Secret storage:": "Secret storage:", + "ready": "ready", + "not ready": "not ready", "Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS", "Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)", "Could not connect to Identity Server": "Could not connect to Identity Server", From bbe2084f66b4102934db263cb2767ef3443ba11a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 8 Sep 2020 18:01:56 +0100 Subject: [PATCH 09/10] Add independent set up / reset actions in Settings This adds set up and reset actions to each of cross-signing and secure backup that do separate things, rather than mixing concerns together. (It's temporarily still a bit of lie for backup, as more changes are needed to stop resetting cross-signing as well.) --- .../views/settings/_SecureBackupPanel.scss | 4 ++ src/SecurityManager.js | 2 +- .../CreateSecretStorageDialog.js | 3 + .../views/settings/CrossSigningPanel.js | 51 +++++++++----- .../views/settings/SecureBackupPanel.js | 69 +++++++++++++------ src/i18n/strings/en_EN.json | 6 +- 6 files changed, 90 insertions(+), 45 deletions(-) diff --git a/res/css/views/settings/_SecureBackupPanel.scss b/res/css/views/settings/_SecureBackupPanel.scss index 587cab8f36..a9dab06b57 100644 --- a/res/css/views/settings/_SecureBackupPanel.scss +++ b/res/css/views/settings/_SecureBackupPanel.scss @@ -34,6 +34,10 @@ limitations under the License. .mx_SecureBackupPanel_buttonRow { margin: 1em 0; + + :nth-child(n + 1) { + margin-inline-end: 10px; + } } .mx_SecureBackupPanel_statusList { diff --git a/src/SecurityManager.js b/src/SecurityManager.js index 891f43b705..cc7db3ead7 100644 --- a/src/SecurityManager.js +++ b/src/SecurityManager.js @@ -250,7 +250,7 @@ export async function accessSecretStorage(func = async () => { }, forceReset = f 'Cross-signing keys dialog', '', InteractiveAuthDialog, { title: _t("Setting up keys"), - matrixClient: MatrixClientPeg.get(), + matrixClient: cli, makeRequest, }, ); diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 07ff3c9b76..d4b1a73c3e 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -280,6 +280,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent { const { forceReset } = this.props; try { + // JRS: In an upcoming change, the cross-signing steps will be + // removed from here and this will instead be about secret storage + // only. if (forceReset) { console.log("Forcing cross-signing and secret storage reset"); await cli.bootstrapSecretStorage({ diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index 8ef68e4b2a..a0ca84645f 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -19,9 +19,9 @@ import React from 'react'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; -import { accessSecretStorage } from '../../../SecurityManager'; import Modal from '../../../Modal'; import Spinner from '../elements/Spinner'; +import InteractiveAuthDialog from '../dialogs/InteractiveAuthDialog'; export default class CrossSigningPanel extends React.PureComponent { constructor(props) { @@ -66,7 +66,7 @@ export default class CrossSigningPanel extends React.PureComponent { }; _onBootstrapClick = () => { - this._bootstrapSecureSecretStorage(false); + this._bootstrapCrossSigning({ forceReset: false }); }; onStatusChanged = () => { @@ -99,35 +99,50 @@ export default class CrossSigningPanel extends React.PureComponent { } /** - * Bootstrapping secret storage may take one of these paths: - * 1. Create secret storage from a passphrase and store cross-signing keys - * in secret storage. + * Bootstrapping cross-signing take one of these paths: + * 1. Create cross-signing keys locally and store in secret storage (if it + * already exists on the account). * 2. Access existing secret storage by requesting passphrase and accessing * cross-signing keys as needed. * 3. All keys are loaded and there's nothing to do. * @param {bool} [forceReset] Bootstrap again even if keys already present */ - _bootstrapSecureSecretStorage = async (forceReset=false) => { + _bootstrapCrossSigning = async ({ forceReset = false }) => { this.setState({ error: null }); try { - await accessSecretStorage(() => undefined, forceReset); + const cli = MatrixClientPeg.get(); + await cli.bootstrapCrossSigning({ + authUploadDeviceSigningKeys: async (makeRequest) => { + const { finished } = Modal.createTrackedDialog( + 'Cross-signing keys dialog', '', InteractiveAuthDialog, + { + title: _t("Setting up keys"), + matrixClient: cli, + makeRequest, + }, + ); + const [confirmed] = await finished; + if (!confirmed) { + throw new Error("Cross-signing key upload auth canceled"); + } + }, + setupNewCrossSigning: forceReset, + }); } catch (e) { this.setState({ error: e }); - console.error("Error bootstrapping secret storage", e); + console.error("Error bootstrapping cross-signing", e); } if (this._unmounted) return; this._getUpdatedStatus(); } - onDestroyStorage = (act) => { - if (!act) return; - this._bootstrapSecureSecretStorage(true); - } - - _destroySecureSecretStorage = () => { + _resetCrossSigning = () => { const ConfirmDestroyCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog"); Modal.createDialog(ConfirmDestroyCrossSigningDialog, { - onFinished: this.onDestroyStorage, + onFinished: (act) => { + if (!act) return; + this._bootstrapCrossSigning({ forceReset: true }); + }, }); } @@ -184,8 +199,8 @@ export default class CrossSigningPanel extends React.PureComponent { if (keysExistAnywhere) { resetButton = (

- - {_t("Reset cross-signing and secret storage")} + + {_t("Reset")}
); @@ -197,7 +212,7 @@ export default class CrossSigningPanel extends React.PureComponent { bootstrapButton = (
- {_t("Bootstrap cross-signing and secret storage")} + {_t("Set up")}
); diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index 0f43770288..7d4cfc2c10 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -25,6 +25,7 @@ import Spinner from '../elements/Spinner'; import AccessibleButton from '../elements/AccessibleButton'; import QuestionDialog from '../dialogs/QuestionDialog'; import RestoreKeyBackupDialog from '../dialogs/keybackup/RestoreKeyBackupDialog'; +import { accessSecretStorage } from '../../../SecurityManager'; export default class SecureBackupPanel extends React.PureComponent { constructor(props) { @@ -184,6 +185,19 @@ export default class SecureBackupPanel extends React.PureComponent { ); } + _resetSecretStorage = async () => { + this.setState({ error: null }); + try { + await accessSecretStorage(() => { }, /* forceReset = */ true); + } catch (e) { + console.error("Error resetting secret storage", e); + if (this._unmounted) return; + this.setState({ error: e }); + } + if (this._unmounted) return; + this._loadBackupStatus(); + } + render() { const { loading, @@ -201,7 +215,7 @@ export default class SecureBackupPanel extends React.PureComponent { let statusDescription; let extraDetailsTableRows; let extraDetails; - let actions; + let actions = []; if (error) { statusDescription = (
@@ -335,13 +349,6 @@ export default class SecureBackupPanel extends React.PureComponent { trustedLocally = _t("This backup is trusted because it has been restored on this session"); } - let deleteBackupButton; - if (!isSecureBackupRequired()) { - deleteBackupButton = - {_t("Delete Backup")} - ; - } - extraDetailsTableRows = <> {_t("Backup version:")} @@ -359,14 +366,19 @@ export default class SecureBackupPanel extends React.PureComponent {
{trustedLocally}
; - actions = ( -
- - {restoreButtonCaption} -     - {deleteBackupButton} -
+ actions.push( + + {restoreButtonCaption} + , ); + + if (!isSecureBackupRequired()) { + actions.push( + + {_t("Delete Backup")} + , + ); + } } else { statusDescription = <>

{_t( @@ -375,12 +387,18 @@ export default class SecureBackupPanel extends React.PureComponent { )}

{_t("Back up your keys before signing out to avoid losing them.")}

; - actions = ( -
- - {_t("Start using Key Backup")} - -
+ actions.push( + + {_t("Set up")} + , + ); + } + + if (secretStorageKeyInAccount) { + actions.push( + + {_t("Reset")} + , ); } @@ -394,6 +412,13 @@ export default class SecureBackupPanel extends React.PureComponent { } } + let actionRow; + if (actions.length) { + actionRow =
+ {actions} +
; + } + return (

{_t( @@ -430,7 +455,7 @@ export default class SecureBackupPanel extends React.PureComponent { {extraDetails} - {actions} + {actionRow}

); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1bf431f6e0..b73d2b25fe 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -648,8 +648,7 @@ "Cross-signing is ready for use.": "Cross-signing is ready for use.", "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.", "Cross-signing is not set up.": "Cross-signing is not set up.", - "Reset cross-signing and secret storage": "Reset cross-signing and secret storage", - "Bootstrap cross-signing and secret storage": "Bootstrap cross-signing and secret storage", + "Reset": "Reset", "Cross-signing public keys:": "Cross-signing public keys:", "in memory": "in memory", "not found": "not found", @@ -748,7 +747,6 @@ "Algorithm:": "Algorithm:", "Your keys are not being backed up from this session.": "Your keys are not being backed up from this session.", "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.", - "Start using Key Backup": "Start using Key Backup", "well formed": "well formed", "unexpected type": "unexpected type", "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.", @@ -948,7 +946,6 @@ "Uploaded sound": "Uploaded sound", "Sounds": "Sounds", "Notification sound": "Notification sound", - "Reset": "Reset", "Set a new custom sound": "Set a new custom sound", "Browse": "Browse", "Change room avatar": "Change room avatar", @@ -1173,6 +1170,7 @@ "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", "Try again later, or ask a room admin to check if you have access.": "Try again later, or ask a room admin to check if you have access.", "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.": "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.", + "Start using Key Backup": "Start using Key Backup", "Never lose encrypted messages": "Never lose encrypted messages", "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", "Securely back up your keys to avoid losing them. Learn more.": "Securely back up your keys to avoid losing them. Learn more.", From 6d1b1d9460af5cd4ec9e8334c07d3cbabc03b113 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 9 Sep 2020 14:12:36 +0100 Subject: [PATCH 10/10] Fix linting error --- src/components/views/settings/SecureBackupPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/SecureBackupPanel.js b/src/components/views/settings/SecureBackupPanel.js index 7d4cfc2c10..7f0655d54a 100644 --- a/src/components/views/settings/SecureBackupPanel.js +++ b/src/components/views/settings/SecureBackupPanel.js @@ -215,7 +215,7 @@ export default class SecureBackupPanel extends React.PureComponent { let statusDescription; let extraDetailsTableRows; let extraDetails; - let actions = []; + const actions = []; if (error) { statusDescription = (