From e5da81b6ba3ed6ba9274504f66ca6aed06b7fcb4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 6 May 2020 14:42:03 -0600 Subject: [PATCH 1/7] Ensure key backup gets dealt with correctly during secret storage reset Fixes https://github.com/vector-im/riot-web/issues/13562 We only initialize a new key backup if the user requested one. If they've requested new keys but have not asked for keys to be backed up, we simply delete the now-invalid backup. This also adds some logging to identify in rageshakes when someone resets their cross-signing, and when their key backup is being deleted. --- .../dialogs/secretstorage/CreateSecretStorageDialog.js | 10 +++++++++- src/components/views/settings/CrossSigningPanel.js | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 2c05f231e7..9d07628093 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -236,12 +236,20 @@ export default class CreateSecretStorageDialog extends React.PureComponent { try { if (force) { + console.log("Forcing secret storage reset"); // log something so we can debug this later await cli.bootstrapSecretStorage({ authUploadDeviceSigningKeys: this._doBootstrapUIAuth, createSecretStorageKey: async () => this._recoveryKey, - setupNewKeyBackup: true, + setupNewKeyBackup: this.state.useKeyBackup, setupNewSecretStorage: true, }); + if (!this.state.useKeyBackup && this.state.backupInfo) { + // If the user is resetting their cross-signing keys and doesn't want + // key backup (but had it enabled before), delete the key backup as it's + // no longer valid. + console.log("Deleting invalid key backup (secrets have been reset; key backup not requested)"); + await cli.deleteKeyBackupVersion(this.state.backupInfo.version); + } } else { await cli.bootstrapSecretStorage({ authUploadDeviceSigningKeys: this._doBootstrapUIAuth, diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index cb04d2a018..c07f1c25dd 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -131,8 +131,8 @@ export default class CrossSigningPanel extends React.PureComponent { } _destroySecureSecretStorage = () => { - const ConfirmDestoryCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog"); - Modal.createDialog(ConfirmDestoryCrossSigningDialog, { + const ConfirmDestroyCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog"); + Modal.createDialog(ConfirmDestroyCrossSigningDialog, { onFinished: this.onDestroyStorage, }); } From ad2f704e76d31c25933f257cf62829a67e9739c0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 10 May 2020 11:17:21 +0100 Subject: [PATCH 2/7] don't NPE on invites from Dendrite --- src/components/structures/RoomView.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 69a2e54a2c..f53929df4a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1717,8 +1717,11 @@ export default createReactClass({ } else { const myUserId = this.context.credentials.userId; const myMember = this.state.room.getMember(myUserId); - const inviteEvent = myMember.events.member; - var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); + const inviteEvent = myMember ? myMember.events.member : null; + let inviterName = _t("Unknown"); + if (inviteEvent) { + inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); + } // We deliberately don't try to peek into invites, even if we have permission to peek // as they could be a spam vector. From 6486c749095e577ce08af4c900f9d570d06c5859 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 11 May 2020 14:21:59 +0100 Subject: [PATCH 3/7] Fix passphrase reset in key backup restore dialog We prompt to restore the key backup when bootstrapping if it's not trusted, but the 'set up new recovery options' in this dialog just sets up a new key backup which just goes back to trying to access SSSS if cross-signing is enabled. This makes it reset the SSSS passphase instead. Fixes https://github.com/vector-im/riot-web/issues/13578 --- .../keybackup/RestoreKeyBackupDialog.js | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index f1008dfcb0..7e51e76f6c 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -23,6 +23,7 @@ import { MatrixClient } from 'matrix-js-sdk'; import Modal from '../../../../Modal'; import { _t } from '../../../../languageHandler'; import { accessSecretStorage } from '../../../../CrossSigningManager'; +import SettingsStore from "../../../../settings/SettingsStore"; const RESTORE_TYPE_PASSPHRASE = 0; const RESTORE_TYPE_RECOVERYKEY = 1; @@ -89,14 +90,21 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { _onResetRecoveryClick = () => { this.props.onFinished(false); - Modal.createTrackedDialogAsync('Key Backup', 'Key Backup', - import('../../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'), - { - onFinished: () => { - this._loadBackupStatus(); - }, - }, null, /* priority = */ false, /* static = */ true, - ); + + if (SettingsStore.getValue("feature_cross_signing")) { + // If cross-signing is enabled, we reset the SSSS recovery passphrase (and cross-signing keys) + this.props.onFinished(false); + accessSecretStorage(() => {}, /* forceReset = */ true); + } else { + Modal.createTrackedDialogAsync('Key Backup', 'Key Backup', + import('../../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'), + { + onFinished: () => { + this._loadBackupStatus(); + }, + }, null, /* priority = */ false, /* static = */ true, + ); + } } _onRecoveryKeyChange = (e) => { From 0e8bd59d2f2937ce7e43e98c0f4038cb7df35811 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 11 May 2020 15:43:34 +0100 Subject: [PATCH 4/7] View Source should target the replacing event rather than the root one Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/context_menus/MessageContextMenu.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index 452489aa65..70ab80e6cc 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -130,22 +130,24 @@ export default createReactClass({ }, onViewSourceClick: function() { + const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent; const ViewSource = sdk.getComponent('structures.ViewSource'); Modal.createTrackedDialog('View Event Source', '', ViewSource, { - roomId: this.props.mxEvent.getRoomId(), - eventId: this.props.mxEvent.getId(), - content: this.props.mxEvent.event, + roomId: ev.getRoomId(), + eventId: ev.getId(), + content: ev.event, }, 'mx_Dialog_viewsource'); this.closeMenu(); }, onViewClearSourceClick: function() { + const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent; const ViewSource = sdk.getComponent('structures.ViewSource'); Modal.createTrackedDialog('View Clear Event Source', '', ViewSource, { - roomId: this.props.mxEvent.getRoomId(), - eventId: this.props.mxEvent.getId(), + roomId: ev.getRoomId(), + eventId: ev.getId(), // FIXME: _clearEvent is private - content: this.props.mxEvent._clearEvent, + content: ev._clearEvent, }, 'mx_Dialog_viewsource'); this.closeMenu(); }, From f487c2b38cb04202abc5c220ba6f7b652d4a39f2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 11 May 2020 16:21:08 +0100 Subject: [PATCH 5/7] Fix rageshake with no matrix client We checked for the presence of a matrix client but then went and called a method on it assuming it existed on the line below, so, don't do that. https://github.com/vector-im/riot-web/issues/13624 pointed this out --- src/rageshake/submit-rageshake.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index 921f3fbf40..1ef68637cf 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -101,15 +101,15 @@ export default async function sendBugReport(bugReportEndpoint: string, opts: IOp if (client) { body.append('user_id', client.credentials.userId); body.append('device_id', client.deviceId); - } - if (client.isCryptoEnabled()) { - const keys = [`ed25519:${client.getDeviceEd25519Key()}`]; - if (client.getDeviceCurve25519Key) { - keys.push(`curve25519:${client.getDeviceCurve25519Key()}`); + if (client.isCryptoEnabled()) { + const keys = [`ed25519:${client.getDeviceEd25519Key()}`]; + if (client.getDeviceCurve25519Key) { + keys.push(`curve25519:${client.getDeviceCurve25519Key()}`); + } + body.append('device_keys', keys.join(', ')); + body.append('cross_signing_key', client.getCrossSigningId()); } - body.append('device_keys', keys.join(', ')); - body.append('cross_signing_key', client.getCrossSigningId()); } if (opts.label) { From c0061e2f2a879655d62abc2983af96b175c943f5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 11 May 2020 16:39:14 +0100 Subject: [PATCH 6/7] Don't try and redact redactions for "Remove recent messages" Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/right_panel/UserInfo.js | 2 +- src/components/views/rooms/MemberInfo.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 61f5a8161a..478dc90418 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -557,7 +557,7 @@ const RedactMessagesButton = ({member}) => { let eventsToRedact = []; while (timeline) { eventsToRedact = timeline.getEvents().reduce((events, event) => { - if (event.getSender() === userId && !event.isRedacted()) { + if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction()) { return events.concat(event); } else { return events; diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index be3e8cf971..6b03000961 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -364,7 +364,7 @@ export default createReactClass({ let eventsToRedact = []; for (const timeline of timelineSet.getTimelines()) { eventsToRedact = timeline.getEvents().reduce((events, event) => { - if (event.getSender() === userId && !event.isRedacted()) { + if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction()) { return events.concat(event); } else { return events; From c2dcb60f0b9257c2a9e8263c1637a9b80eb1ba78 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 11 May 2020 20:10:45 +0100 Subject: [PATCH 7/7] Prompt user to specify an alternate server if their chosen one has registration disabled Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/auth/Registration.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index c74f6ed6e3..7818496e71 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -267,6 +267,7 @@ export default createReactClass({ dis.dispatch({action: 'start_login'}); } else { this.setState({ + serverErrorIsFatal: true, // fatal because user cannot continue on this server errorText: _t("Registration has been disabled on this homeserver."), // add empty flows array to get rid of spinner flows: [],