From ba3dd0c87ad34433bcb5e9bfe59eca0bcb8bd865 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 23 May 2018 13:35:42 +0100 Subject: [PATCH 1/2] Refactor onOk to async function --- .../views/dialogs/DeactivateAccountDialog.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js index 87228b4733..f41f37e075 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.js +++ b/src/components/views/dialogs/DeactivateAccountDialog.js @@ -47,19 +47,17 @@ export default class DeactivateAccountDialog extends React.Component { }); } - _onOk() { + async _onOk() { // This assumes that the HS requires password UI auth // for this endpoint. In reality it could be any UI auth. this.setState({busy: true}); - MatrixClientPeg.get().deactivateAccount({ - type: 'm.login.password', - user: MatrixClientPeg.get().credentials.userId, - password: this._passwordField.value, - }).done(() => { - Analytics.trackEvent('Account', 'Deactivate Account'); - Lifecycle.onLoggedOut(); - this.props.onFinished(false); - }, (err) => { + try { + await MatrixClientPeg.get().deactivateAccount({ + type: 'm.login.password', + user: MatrixClientPeg.get().credentials.userId, + password: this._passwordField.value, + }); + } catch (err) { let errStr = _t('Unknown error'); // https://matrix.org/jira/browse/SYN-744 if (err.httpStatus == 401 || err.httpStatus == 403) { @@ -70,7 +68,11 @@ export default class DeactivateAccountDialog extends React.Component { busy: false, errStr: errStr, }); - }); + return; + } + Analytics.trackEvent('Account', 'Deactivate Account'); + Lifecycle.onLoggedOut(); + this.props.onFinished(false); } _onCancel() { From b1249899680f57448e94ca2df83d834a4c6e5a15 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 23 May 2018 16:33:32 +0100 Subject: [PATCH 2/2] Implement erasure option upon deactivation --- .../views/dialogs/DeactivateAccountDialog.js | 103 ++++++++++++++++-- src/i18n/strings/en_EN.json | 9 +- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js index f41f37e075..104419f2ef 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.js +++ b/src/components/views/dialogs/DeactivateAccountDialog.js @@ -33,10 +33,21 @@ export default class DeactivateAccountDialog extends React.Component { this._onOk = this._onOk.bind(this); this._onCancel = this._onCancel.bind(this); this._onPasswordFieldChange = this._onPasswordFieldChange.bind(this); + this._onEraseFieldChange = this._onEraseFieldChange.bind(this); + + const deactivationPreferences = + MatrixClientPeg.get().getAccountData('im.riot.account_deactivation_preferences'); + + const shouldErase = ( + deactivationPreferences && + deactivationPreferences.getContent() && + deactivationPreferences.getContent().shouldErase + ) || false; this.state = { confirmButtonEnabled: false, busy: false, + shouldErase, errStr: null, }; } @@ -47,16 +58,54 @@ export default class DeactivateAccountDialog extends React.Component { }); } + _onEraseFieldChange(ev) { + this.setState({ + shouldErase: ev.target.checked, + }); + } + async _onOk() { - // This assumes that the HS requires password UI auth - // for this endpoint. In reality it could be any UI auth. this.setState({busy: true}); + + // Before we deactivate the account insert an event into + // the user's account data indicating that they wish to be + // erased from the homeserver. + // + // We do this because the API for erasing after deactivation + // might not be supported by the connected homeserver. Leaving + // an indication in account data is only best-effort, and + // in the worse case, the HS maintainer would have to run a + // script to erase deactivated accounts that have shouldErase + // set to true in im.riot.account_deactivation_preferences. + // + // Note: The preferences are scoped to Riot, hence the + // "im.riot..." event type. + // + // Note: This may have already been set on previous attempts + // where, for example, the user entered the wrong password. + // This is fine because the UI always indicates the preference + // prior to us calling `deactivateAccount`. try { - await MatrixClientPeg.get().deactivateAccount({ + await MatrixClientPeg.get().setAccountData('im.riot.account_deactivation_preferences', { + shouldErase: this.state.shouldErase, + }); + } catch (err) { + this.setState({ + busy: false, + errStr: _t('Failed to indicate account erasure'), + }); + return; + } + + try { + // This assumes that the HS requires password UI auth + // for this endpoint. In reality it could be any UI auth. + const auth = { type: 'm.login.password', user: MatrixClientPeg.get().credentials.userId, password: this._passwordField.value, - }); + }; + await MatrixClientPeg.get().deactivateAccount(auth, this.state.shouldErase); } catch (err) { let errStr = _t('Unknown error'); // https://matrix.org/jira/browse/SYN-744 @@ -70,6 +119,7 @@ export default class DeactivateAccountDialog extends React.Component { }); return; } + Analytics.trackEvent('Account', 'Deactivate Account'); Lifecycle.onLoggedOut(); this.props.onFinished(false); @@ -107,21 +157,56 @@ export default class DeactivateAccountDialog extends React.Component { onFinished={this.props.onFinished} onEnterPressed={this.onOk} titleClass="danger" - title={_t("Deactivate Account")}> + title={_t("Deactivate Account")} + >
-

{ _t("This will make your account permanently unusable. You will not be able to re-register the same user ID.") }

+

{ _t( + "This will make your account permanently unusable. " + + "You will not be able to log in, and no one will be able " + + "to re-register the same user ID. " + + "This action is irreversible.", + {}, + { b: (sub) => { sub } }, + ) }

-

{ _t("This action is irreversible.") }

+

{ _t( + "Deactivating your account does not by default erase messages you have sent. " + + "If you would like to erase your messages, please tick the box below.", + {}, + { b: (sub) => { sub } }, + ) }

-

{ _t("To continue, please enter your password.") }

+

{ _t( + "Message visibility in Matrix is similar to email. " + + "Erasing your messages means that messages have you sent will not be shared with " + + "any new or unregistered users, but registered users who already had access to " + + "these messages will still have access to their copy.", + ) }

-

{ _t("Password") }:

+

{ _t("To continue, please enter your password:") }

{this._passwordField = e;}} className={passwordBoxClass} /> + +

+ +

+ { error }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7df8ae2161..a384a7c212 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -781,11 +781,16 @@ "Advanced options": "Advanced options", "Block users on other matrix homeservers from joining this room": "Block users on other matrix homeservers from joining this room", "This setting cannot be changed later!": "This setting cannot be changed later!", + "Failed to indicate account erasure": "Failed to indicate account erasure", "Unknown error": "Unknown error", "Incorrect password": "Incorrect password", "Deactivate Account": "Deactivate Account", - "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "This will make your account permanently unusable. You will not be able to re-register the same user ID.", - "This action is irreversible.": "This action is irreversible.", + "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This action is irreversible.": "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This action is irreversible.", + "Deactivating your account does not by default erase messages you have sent. If you would like to erase your messages, please tick the box below.": "Deactivating your account does not by default erase messages you have sent. If you would like to erase your messages, please tick the box below.", + "Message visibility in Matrix is similar to email. Erasing your messages means that messages have you sent will not be shared with any new or unregistered users, but registered users who already had access to these messages will still have access to their copy.": "Message visibility in Matrix is similar to email. Erasing your messages means that messages have you sent will not be shared with any new or unregistered users, but registered users who already had access to these messages will still have access to their copy.", + "To continue, please enter your password:": "To continue, please enter your password:", + "password": "password", + "Please erase all messages I have sent when my account is deactivated. (Warning: this will cause future users to see an incomplete view of conversations, which is a bad experience).": "Please erase all messages I have sent when my account is deactivated. (Warning: this will cause future users to see an incomplete view of conversations, which is a bad experience).", "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:", "Device name": "Device name", "Device key": "Device key",