From 3071fc0ddcdf6065cf4e64b647c21ade53722173 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 21 Jan 2017 17:39:31 +0000 Subject: [PATCH 01/12] UI for blacklisting unverified devices per-room & globally (written blind; untested as yet) --- src/UserSettingsStore.js | 16 +++++++ src/components/structures/UserSettings.js | 36 ++++++++++++++++ .../views/dialogs/UnknownDeviceDialog.js | 2 + src/components/views/rooms/RoomSettings.js | 43 +++++++++++++++++-- 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index e5dba62ee7..f2f99603d6 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -149,6 +149,22 @@ module.exports = { return MatrixClientPeg.get().setAccountData("im.vector.web.settings", settings); }, + getLocalSettings: function() { + return localStorage.getItem('mx_local_settings'); + }, + + getLocalSetting: function(type, defaultValue = null) { + var settings = this.getLocalSettings(); + return settings.hasOwnProperty(type) ? settings[type] : null; + }, + + setLocalSetting: function(type, value) { + var settings = this.getLocalSettings(); + settings[type] = value; + // FIXME: handle errors + localStorage.setItem('mx_local_settings', settings); + }, + isFeatureEnabled: function(feature: string): boolean { // Disable labs for guests. if (MatrixClientPeg.get().isGuest()) return false; diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 4a1332be8c..a262431f2b 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -57,6 +57,18 @@ const SETTINGS_LABELS = [ */ ]; +const CRYPTO_SETTINGS_LABELS = [ + { + id: 'blacklistUnverifiedDevices', + label: 'Never send encrypted messages to unverified devices', + }, + // XXX: this is here for documentation; the actual setting is managed via RoomSettings + // { + // id: 'blacklistUnverifiedDevicesPerRoom' + // label: 'Never send encrypted messages to unverified devices in this room', + // } +]; + // Enumerate the available themes, with a nice human text label. // 'id' gives the key name in the im.vector.web.settings account data event // 'value' is the value for that key in the event @@ -146,6 +158,8 @@ module.exports = React.createClass({ syncedSettings.theme = 'light'; } this._syncedSettings = syncedSettings; + + this._localSettings = UserSettingsStore.getLocalSettings(); }, componentDidMount: function() { @@ -471,10 +485,32 @@ module.exports = React.createClass({
  • {identityKey}
  • + { CRYPTO_SETTINGS_LABELS.map( this._renderLocalSetting ) } ); }, + _renderLocalSetting: function(setting) { + const client = MatrixClientPeg.get(); + return
    + { + UserSettingsStore.setLocalSetting(setting.id, e.target.checked) + if (setting.id === 'blacklistUnverifiedDevices') { // XXX: this is a bit ugly + client.setGlobalBlacklistUnverifiedDevices(e.target.checked); + } + } + } + /> + +
    ; + }, + _renderDevicesPanel: function() { var DevicesPanel = sdk.getComponent('settings.DevicesPanel'); return ( diff --git a/src/components/views/dialogs/UnknownDeviceDialog.js b/src/components/views/dialogs/UnknownDeviceDialog.js index aad310c855..8e04414b9c 100644 --- a/src/components/views/dialogs/UnknownDeviceDialog.js +++ b/src/components/views/dialogs/UnknownDeviceDialog.js @@ -69,5 +69,7 @@ module.exports = React.createClass({ ); + // XXX: do we want to give the user the option to enable blacklistUnverifiedDevices for this room (or globally) at this point? + // It feels like confused users will likely turn it on and then disappear in a cloud of UISIs... } }); diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index e14a929ebe..7283201858 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -228,11 +228,13 @@ module.exports = React.createClass({ } // encryption - p = this.saveEncryption(); + p = this.saveEnableEncryption(); if (!q.isFulfilled(p)) { promises.push(p); } + this.saveBlacklistUnverifiedDevicesPerRoom(); + console.log("Performing %s operations: %s", promises.length, JSON.stringify(promises)); return promises; }, @@ -252,11 +254,11 @@ module.exports = React.createClass({ return this.refs.url_preview_settings.saveSettings(); }, - saveEncryption: function() { + saveEnableEncryption: function() { if (!this.refs.encrypt) { return q(); } var encrypt = this.refs.encrypt.checked; - if (!encrypt) { return q(); } + if (encrypt) { return q(); } var roomId = this.props.room.roomId; return MatrixClientPeg.get().sendStateEvent( @@ -265,6 +267,29 @@ module.exports = React.createClass({ ); }, + saveBlacklistUnverifiedDevicesPerRoom: function() { + if (!this.refs.blacklistUnverified) return; + if (this._isRoomBlacklistUnverified() !== this.refs.blacklistUnverified.checked) { + this._setRoomBlacklistUnverified(this.refs.blacklistUnverified.checked); + } + }, + + _isRoomBlacklistUnverified: function() { + var blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom; + if (blacklistUnverifiedDevicesPerRoom) { + return blacklistUnverifiedDevicesPerRoom[this.props.room.roomId]; + } + return false; + }, + + _setRoomBlacklistUnverified: function(value) { + var blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom; + blacklistUnverifiedDevicesPerRoom[this.props.room.roomId] = value; + UserSettingsStore.setLocalSettings('blacklistUnverifiedDevicesPerRoom', blacklistUnverifiedDevicesPerRoom); + + this.props.room.setBlacklistUnverifiedDevices(value); + }, + _hasDiff: function(strA, strB) { // treat undefined as an empty string because other components may blindly // call setName("") when there has been no diff made to the name! @@ -477,6 +502,16 @@ module.exports = React.createClass({ var cli = MatrixClientPeg.get(); var roomState = this.props.room.currentState; var isEncrypted = cli.isRoomEncrypted(this.props.room.roomId); + var isGlobalBlacklistUnverified = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevices; + var isRoomBlacklistUnverified = this._isRoomBlacklistUnverified(); + + var settings = + ; if (!isEncrypted && roomState.mayClientSendStateEvent("m.room.encryption", cli)) { @@ -486,6 +521,7 @@ module.exports = React.createClass({ Enable encryption (warning: cannot be disabled again!) + { settings } ); } else { @@ -497,6 +533,7 @@ module.exports = React.createClass({ } Encryption is { isEncrypted ? "" : "not " } enabled in this room. + { settings } ); } }, From c618880af96c560e0434be0d842e2ef47d306887 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 21 Jan 2017 17:43:46 +0000 Subject: [PATCH 02/12] oops --- src/components/views/rooms/RoomSettings.js | 32 ++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 7283201858..a5782dad7b 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -516,24 +516,28 @@ module.exports = React.createClass({ if (!isEncrypted && roomState.mayClientSendStateEvent("m.room.encryption", cli)) { return ( - - { settings } +
    + + { settings } +
    ); } else { return ( - - { settings } +
    + + { settings } +
    ); } }, From 071e364be29cfafab94bf0442370fa0a955f3197 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 21 Jan 2017 17:56:48 +0000 Subject: [PATCH 03/12] improve the verify warning if blacklisting is unabled --- src/Resend.js | 3 ++- src/components/views/dialogs/UnknownDeviceDialog.js | 12 +++++++++++- src/components/views/rooms/MessageComposerInput.js | 3 ++- .../views/rooms/MessageComposerInputOld.js | 3 ++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Resend.js b/src/Resend.js index e67c812b7c..21da1c173b 100644 --- a/src/Resend.js +++ b/src/Resend.js @@ -32,7 +32,8 @@ module.exports = { if (err.name === "UnknownDeviceError") { var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); Modal.createDialog(UnknownDeviceDialog, { - devices: err.devices + devices: err.devices, + room: MatrixClientPeg.get().getRoom(event.getRoomId()), }); } diff --git a/src/components/views/dialogs/UnknownDeviceDialog.js b/src/components/views/dialogs/UnknownDeviceDialog.js index 8e04414b9c..bd23427d2b 100644 --- a/src/components/views/dialogs/UnknownDeviceDialog.js +++ b/src/components/views/dialogs/UnknownDeviceDialog.js @@ -22,6 +22,7 @@ module.exports = React.createClass({ displayName: 'UnknownEventDialog', propTypes: { + room: React.PropTypes.object.isRequired, devices: React.PropTypes.object.isRequired, onFinished: React.PropTypes.func.isRequired, }, @@ -35,6 +36,15 @@ module.exports = React.createClass({ }, render: function() { + var client = MatrixClientPeg.get(); + var warning; + if (client.getGlobalBlacklistUnverifiedDevices() || room.getBlacklistUnverifiedDevices()) { + warning =

    You are currently blacklisting unverified devices; to send messages to these devices you must verify them.

    ; + } + else { + warning =

    We strongly recommend you verify them before continuing.

    ; + } + return (
    @@ -42,7 +52,7 @@ module.exports = React.createClass({

    This room contains unknown devices which have not been verified.

    -

    We strongly recommend you verify them before continuing.

    + { warning }

    Unknown devices:

      { Object.keys(this.props.devices).map(userId=>{ diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 4a6e36b854..54e4894adf 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -561,7 +561,8 @@ export default class MessageComposerInput extends React.Component { if (err.name === "UnknownDeviceError") { var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); Modal.createDialog(UnknownDeviceDialog, { - devices: err.devices + devices: err.devices, + room: this.props.room, }); } dis.dispatch({ diff --git a/src/components/views/rooms/MessageComposerInputOld.js b/src/components/views/rooms/MessageComposerInputOld.js index a5b1f6b786..7fd3e0b427 100644 --- a/src/components/views/rooms/MessageComposerInputOld.js +++ b/src/components/views/rooms/MessageComposerInputOld.js @@ -345,7 +345,8 @@ module.exports = React.createClass({ if (err.name === "UnknownDeviceError") { var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); Modal.createDialog(UnknownDeviceDialog, { - devices: err.devices + devices: err.devices, + room: this.props.room, }); } From 532f4e59c904afe4967ec80dbff3d57b6be0e88a Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 21 Jan 2017 19:06:15 +0100 Subject: [PATCH 04/12] literally blindly add verification buttons --- src/components/views/dialogs/UnknownDeviceDialog.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/UnknownDeviceDialog.js b/src/components/views/dialogs/UnknownDeviceDialog.js index bd23427d2b..a2e7bd13fb 100644 --- a/src/components/views/dialogs/UnknownDeviceDialog.js +++ b/src/components/views/dialogs/UnknownDeviceDialog.js @@ -37,8 +37,9 @@ module.exports = React.createClass({ render: function() { var client = MatrixClientPeg.get(); + var blacklistUnverified = (client.getGlobalBlacklistUnverifiedDevices() || room.getBlacklistUnverifiedDevices()); var warning; - if (client.getGlobalBlacklistUnverifiedDevices() || room.getBlacklistUnverifiedDevices()) { + if (blacklistUnverified) { warning =

      You are currently blacklisting unverified devices; to send messages to these devices you must verify them.

      ; } else { @@ -61,8 +62,11 @@ module.exports = React.createClass({
        { Object.keys(this.props.devices[userId]).map(deviceId=>{ + var DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons'); + var device = this.props.devices[userId][deviceId]; + var buttons = return
      • - { deviceId } ( { this.props.devices[userId][deviceId].getDisplayName() } ) + { deviceId } ( { device.getDisplayName() } ) { buttons }
      • }) } From 39c122fe4f09d048b5237dd539c66b09855cad76 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 21 Jan 2017 22:27:55 +0100 Subject: [PATCH 05/12] fix local storage idiocy --- src/UserSettingsStore.js | 5 +++-- src/components/structures/UserSettings.js | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index f2f99603d6..d7d3e7bc7a 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -150,7 +150,8 @@ module.exports = { }, getLocalSettings: function() { - return localStorage.getItem('mx_local_settings'); + var localSettingsString = localStorage.getItem('mx_local_settings') || '{}'; + return JSON.parse(localSettingsString); }, getLocalSetting: function(type, defaultValue = null) { @@ -162,7 +163,7 @@ module.exports = { var settings = this.getLocalSettings(); settings[type] = value; // FIXME: handle errors - localStorage.setItem('mx_local_settings', settings); + localStorage.setItem('mx_local_settings', JSON.stringify(settings)); }, isFeatureEnabled: function(feature: string): boolean { diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index a262431f2b..1dd30b679c 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -485,7 +485,9 @@ module.exports = React.createClass({
      • {identityKey}

    - { CRYPTO_SETTINGS_LABELS.map( this._renderLocalSetting ) } +
    + { CRYPTO_SETTINGS_LABELS.map( this._renderLocalSetting ) } +
    ); }, From d9c0513ee28f007353b934562733b17a269e527f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 21 Jan 2017 22:49:29 +0100 Subject: [PATCH 06/12] make it work --- src/components/views/rooms/RoomSettings.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index a5782dad7b..548809e9ad 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -24,6 +24,8 @@ var ObjectUtils = require("../../../ObjectUtils"); var dis = require("../../../dispatcher"); var ScalarAuthClient = require("../../../ScalarAuthClient"); var ScalarMessaging = require('../../../ScalarMessaging'); +var UserSettingsStore = require('../../../UserSettingsStore'); + // parse a string as an integer; if the input is undefined, or cannot be parsed // as an integer, return a default. @@ -283,9 +285,9 @@ module.exports = React.createClass({ }, _setRoomBlacklistUnverified: function(value) { - var blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom; + var blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom || {}; blacklistUnverifiedDevicesPerRoom[this.props.room.roomId] = value; - UserSettingsStore.setLocalSettings('blacklistUnverifiedDevicesPerRoom', blacklistUnverifiedDevicesPerRoom); + UserSettingsStore.setLocalSetting('blacklistUnverifiedDevicesPerRoom', blacklistUnverifiedDevicesPerRoom); this.props.room.setBlacklistUnverifiedDevices(value); }, @@ -508,8 +510,8 @@ module.exports = React.createClass({ var settings = ; From 7bc3fc86961bf11e1840ff5f9978c17dc7eb03d7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 22 Jan 2017 01:28:43 +0100 Subject: [PATCH 07/12] make UnknownDeviceDialog work again, other than the mess of vector-im/vector-web#3020 --- src/components/structures/UserSettings.js | 2 +- .../views/dialogs/UnknownDeviceDialog.js | 49 ++++++++++--------- .../views/elements/DeviceVerifyButtons.js | 4 +- .../views/rooms/MessageComposerInput.js | 3 +- .../views/rooms/MessageComposerInputOld.js | 3 +- src/components/views/rooms/RoomSettings.js | 2 +- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 1dd30b679c..00219e3bb2 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -60,7 +60,7 @@ const SETTINGS_LABELS = [ const CRYPTO_SETTINGS_LABELS = [ { id: 'blacklistUnverifiedDevices', - label: 'Never send encrypted messages to unverified devices', + label: 'Never send encrypted messages to unverified devices from this device', }, // XXX: this is here for documentation; the actual setting is managed via RoomSettings // { diff --git a/src/components/views/dialogs/UnknownDeviceDialog.js b/src/components/views/dialogs/UnknownDeviceDialog.js index a2e7bd13fb..656f831ba1 100644 --- a/src/components/views/dialogs/UnknownDeviceDialog.js +++ b/src/components/views/dialogs/UnknownDeviceDialog.js @@ -36,14 +36,15 @@ module.exports = React.createClass({ }, render: function() { + var DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons'); var client = MatrixClientPeg.get(); - var blacklistUnverified = (client.getGlobalBlacklistUnverifiedDevices() || room.getBlacklistUnverifiedDevices()); + var blacklistUnverified = client.getGlobalBlacklistUnverifiedDevices() || this.props.room.getBlacklistUnverifiedDevices(); var warning; if (blacklistUnverified) { - warning =

    You are currently blacklisting unverified devices; to send messages to these devices you must verify them.

    ; + warning =

    You are currently blacklisting unverified devices; to send messages to these devices you must verify them.

    } else { - warning =

    We strongly recommend you verify them before continuing.

    ; + warning =

    We strongly recommend you verify them before continuing.

    } return ( @@ -54,27 +55,27 @@ module.exports = React.createClass({

    This room contains unknown devices which have not been verified.

    { warning } -

    Unknown devices: -

      { - Object.keys(this.props.devices).map(userId=>{ - return
    • -

      { userId }:

      -
        - { - Object.keys(this.props.devices[userId]).map(deviceId=>{ - var DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons'); - var device = this.props.devices[userId][deviceId]; - var buttons = - return
      • - { deviceId } ( { device.getDisplayName() } ) { buttons } -
      • - }) - } -
      -
    • - }) - }
    -

    + Unknown devices: +
      { + Object.keys(this.props.devices).map(userId=>{ + return
    • +

      { userId }:

      +
        + { + Object.keys(this.props.devices[userId]).map(deviceId=>{ + var device = this.props.devices[userId][deviceId]; + var buttons = + return
      • + { buttons } + { deviceId }
        + { device.getDisplayName() } +
      • + }) + } +
      +
    • + }) + }