From d68db74efe9bfb6952fe268ed53f97d9f305293c Mon Sep 17 00:00:00 2001 From: Zoe Date: Tue, 21 Jan 2020 17:17:40 +0000 Subject: [PATCH 1/5] Room list reflects encryption state --- res/css/views/rooms/_RoomHeader.scss | 4 +- res/css/views/rooms/_RoomTile.scss | 13 ++++ src/components/structures/RoomView.js | 1 + src/components/views/rooms/RoomTile.js | 101 +++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index 0d92247735..a235a47fdd 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -22,7 +22,9 @@ limitations under the License. margin: 0; position: absolute; bottom: 0; - right: -5px; + right: -1px; + height: 10px; + width: 10px } } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index db2c09f6f1..a36d781669 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -98,6 +98,19 @@ limitations under the License. z-index: 2; } +// Note we match .mx_E2EIcon to make sure this matches more tightly than just +// .mx_E2EIcon on its own +.mx_RoomTile_e2eIcon.mx_E2EIcon { + height: 10px; + width: 10px; + display: block; + position: absolute; + bottom: -0px; + right: -1px; + z-index: 1; + margin: 0; +} + .mx_RoomTile_name { font-size: 14px; padding: 0 6px; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9b02f6d503..f10c98dd98 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -796,6 +796,7 @@ export default createReactClass({ return; } + // Duplication betwen here and _updateE2eStatus in RoomTile /* At this point, the user has encryption on and cross-signing on */ const e2eMembers = await room.getEncryptionTargetMembers(); const verified = []; diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index f4f5fa10fc..c277433a5c 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -33,6 +33,8 @@ import RoomViewStore from '../../../stores/RoomViewStore'; import SettingsStore from "../../../settings/SettingsStore"; import {_t} from "../../../languageHandler"; import {RovingTabIndexWrapper} from "../../../accessibility/RovingTabIndex"; +import E2EIcon from './E2EIcon'; +import rate_limited_func from '../../../ratelimitedfunc'; export default createReactClass({ displayName: 'RoomTile', @@ -70,6 +72,7 @@ export default createReactClass({ notificationCount: this.props.room.getUnreadNotificationCount(), selected: this.props.room.roomId === RoomViewStore.getRoomId(), statusMessage: this._getStatusMessage(), + e2eStatus: null, }); }, @@ -102,6 +105,85 @@ export default createReactClass({ return statusUser._unstable_statusMessage; }, + onRoomStateMember: function(ev, state, member) { + // we only care about leaving users + // because trust state will change if someone joins a megolm session anyway + if (member.membership !== "leave") { + return; + } + // ignore members in other rooms + if (member.roomId !== this.props.room.roomId) { + return; + } + + this._updateE2eStatus(); + }, + + onUserVerificationChanged: function(userId, _trustStatus) { + if (!this.props.room.getMember(userId)) { + // Not in this room + return; + } + this._updateE2eStatus(); + }, + + onRoomTimeline: function(ev, room) { + if (!room) return; + if (room.roomId != this.props.room.roomId) return; + console.warn("e2e onRoomTimeline"); + if (ev.getType() !== "m.room.encryption") return; + console.warn("e2e onRoomTimeline ENCRYPTION"); + MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); + this.onFindingRoomToBeEncrypted(); + }, + + onFindingRoomToBeEncrypted: function () { + const cli = MatrixClientPeg.get(); + cli.on("RoomState.members", this.onRoomStateMember); + cli.on("userTrustStatusChanged", this.onUserVerificationChanged); + + this._updateE2eStatus(); + }, + + _updateE2eStatus: async function() { + const cli = MatrixClientPeg.get(); + if (!cli.isRoomEncrypted(this.props.room.roomId)) { + return; + } + if (!SettingsStore.isFeatureEnabled("feature_cross_signing")) { + return; + } + + // Duplication between here and _updateE2eStatus in RoomView + const e2eMembers = await this.props.room.getEncryptionTargetMembers(); + const verified = []; + const unverified = []; + e2eMembers.map(({userId}) => userId) + .filter((userId) => userId !== cli.getUserId()) + .forEach((userId) => { + (cli.checkUserTrust(userId).isCrossSigningVerified() ? + verified : unverified).push(userId) + }); + + /* Check all verified user devices. */ + for (const userId of verified) { + const devices = await cli.getStoredDevicesForUser(userId); + const allDevicesVerified = devices.every(({deviceId}) => { + return cli.checkDeviceTrust(userId, deviceId).isVerified(); + }); + if (!allDevicesVerified) { + this.setState({ + e2eStatus: "warning", + }); + return; + } + } + + this.setState({ + e2eStatus: unverified.length === 0 ? "verified" : "normal", + }); + }, + onRoomName: function(room) { if (room !== this.props.room) return; this.setState({ @@ -151,10 +233,19 @@ export default createReactClass({ }, componentDidMount: function() { + /* We bind here rather than in the definition because otherwise we wind up with the + method only being callable once every 500ms across all instances, which would be wrong */ + this._updateE2eStatus = rate_limited_func(this._updateE2eStatus, 500); + const cli = MatrixClientPeg.get(); cli.on("accountData", this.onAccountData); cli.on("Room.name", this.onRoomName); cli.on("RoomState.events", this.onJoinRule); + if (cli.isRoomEncrypted(this.props.room.roomId)) { + this.onFindingRoomToBeEncrypted(); + } else { + cli.on("Room.timeline", this.onRoomTimeline); + } ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange); this.dispatcherRef = dis.register(this.onAction); @@ -172,6 +263,9 @@ export default createReactClass({ MatrixClientPeg.get().removeListener("accountData", this.onAccountData); MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); cli.removeListener("RoomState.events", this.onJoinRule); + cli.removeListener("RoomState.members", this.onRoomStateMember); + cli.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); + cli.removeListener("Room.timeline", this.onRoomTimeline); } ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange); dis.unregister(this.dispatcherRef); @@ -433,6 +527,12 @@ export default createReactClass({ privateIcon =
; } + let e2eIcon = null; + // For now, skip the icon for DMs. Possibly we want to move the DM icon elsewhere? + if (!dmUserId && this.state.e2eStatus) { + e2eIcon = + } + return {({onFocus, isActive, ref}) => @@ -453,6 +553,7 @@ export default createReactClass({
{ dmIndicator } + { e2eIcon }
{ privateIcon } From ee33c7cd627bdc378d1760f288d0b027f94bf6f1 Mon Sep 17 00:00:00 2001 From: Zoe Date: Thu, 23 Jan 2020 11:09:52 +0000 Subject: [PATCH 2/5] lint --- src/components/views/rooms/RoomTile.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index c277433a5c..6ad7bc3f2d 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -34,6 +34,7 @@ import SettingsStore from "../../../settings/SettingsStore"; import {_t} from "../../../languageHandler"; import {RovingTabIndexWrapper} from "../../../accessibility/RovingTabIndex"; import E2EIcon from './E2EIcon'; +// eslint-disable-next-line camelcase import rate_limited_func from '../../../ratelimitedfunc'; export default createReactClass({ @@ -137,7 +138,7 @@ export default createReactClass({ this.onFindingRoomToBeEncrypted(); }, - onFindingRoomToBeEncrypted: function () { + onFindingRoomToBeEncrypted: function() { const cli = MatrixClientPeg.get(); cli.on("RoomState.members", this.onRoomStateMember); cli.on("userTrustStatusChanged", this.onUserVerificationChanged); @@ -162,7 +163,7 @@ export default createReactClass({ .filter((userId) => userId !== cli.getUserId()) .forEach((userId) => { (cli.checkUserTrust(userId).isCrossSigningVerified() ? - verified : unverified).push(userId) + verified : unverified).push(userId); }); /* Check all verified user devices. */ @@ -530,7 +531,7 @@ export default createReactClass({ let e2eIcon = null; // For now, skip the icon for DMs. Possibly we want to move the DM icon elsewhere? if (!dmUserId && this.state.e2eStatus) { - e2eIcon = + e2eIcon = ; } return From a409b9b96fe706e02d88c8956886be0d103da794 Mon Sep 17 00:00:00 2001 From: Zoe Date: Thu, 23 Jan 2020 11:14:01 +0000 Subject: [PATCH 3/5] whoops, left some printfs --- src/components/views/rooms/RoomTile.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 6ad7bc3f2d..0b50d85ff6 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -131,9 +131,7 @@ export default createReactClass({ onRoomTimeline: function(ev, room) { if (!room) return; if (room.roomId != this.props.room.roomId) return; - console.warn("e2e onRoomTimeline"); if (ev.getType() !== "m.room.encryption") return; - console.warn("e2e onRoomTimeline ENCRYPTION"); MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); this.onFindingRoomToBeEncrypted(); }, From e9dccd1460935cd8e68fe8f9595223cc7ebac4ba Mon Sep 17 00:00:00 2001 From: Zoe Date: Thu, 23 Jan 2020 14:44:57 +0000 Subject: [PATCH 4/5] moving icons exactly one pixel to the right and one pixel down --- res/css/views/rooms/_RoomHeader.scss | 4 ++-- res/css/views/rooms/_RoomTile.scss | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index a235a47fdd..6bfcd437c1 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -21,8 +21,8 @@ limitations under the License. .mx_E2EIcon { margin: 0; position: absolute; - bottom: 0; - right: -1px; + bottom: -1px; + right: -2px; height: 10px; width: 10px } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index a36d781669..376f4370e3 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -105,8 +105,8 @@ limitations under the License. width: 10px; display: block; position: absolute; - bottom: -0px; - right: -1px; + bottom: -1px; + right: -2px; z-index: 1; margin: 0; } From 395c82b1e5042e915cf3d1184ace4d23ea2ad240 Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 24 Jan 2020 11:04:40 +0000 Subject: [PATCH 5/5] Update src/components/structures/RoomView.js Co-Authored-By: J. Ryan Stinnett --- src/components/structures/RoomView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f10c98dd98..4c88f533f0 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -796,7 +796,7 @@ export default createReactClass({ return; } - // Duplication betwen here and _updateE2eStatus in RoomTile + // Duplication between here and _updateE2eStatus in RoomTile /* At this point, the user has encryption on and cross-signing on */ const e2eMembers = await room.getEncryptionTargetMembers(); const verified = [];