From f16c08f13e90c6149d88fcbd0b679748983010a1 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 6 May 2021 15:23:58 +0200 Subject: [PATCH] remove room from all user identities when leaving and delete identity as well as all device identities if no rooms left --- src/matrix/e2ee/DeviceTracker.js | 38 +++++++++++++------ src/matrix/e2ee/RoomEncryption.js | 1 + .../storage/idb/stores/DeviceIdentityStore.js | 9 +++++ .../storage/idb/stores/RoomStateStore.js | 3 +- src/matrix/storage/idb/stores/common.js | 18 +++++++++ 5 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 src/matrix/storage/idb/stores/common.js diff --git a/src/matrix/e2ee/DeviceTracker.js b/src/matrix/e2ee/DeviceTracker.js index ed55b79a..a14b42f3 100644 --- a/src/matrix/e2ee/DeviceTracker.js +++ b/src/matrix/e2ee/DeviceTracker.js @@ -121,24 +121,38 @@ export class DeviceTracker { } } + async _removeRoomFromUserIdentity(roomId, userId, txn) { + const {userIdentities, deviceIdentities} = txn; + const identity = await userIdentities.get(userId); + if (identity) { + identity.roomIds = identity.roomIds.filter(id => id !== roomId); + // no more encrypted rooms with this user, remove + if (identity.roomIds.length === 0) { + userIdentities.remove(userId); + deviceIdentities.removeAllForUser(userId); + } else { + userIdentities.set(identity); + } + } + } + async _applyMemberChange(memberChange, txn) { // TODO: depends whether we encrypt for invited users?? // add room - if (memberChange.previousMembership !== "join" && memberChange.membership === "join") { + if (memberChange.hasJoined) { await this._writeMember(memberChange.member, txn); } // remove room - else if (memberChange.previousMembership === "join" && memberChange.membership !== "join") { - const {userIdentities} = txn; - const identity = await userIdentities.get(memberChange.userId); - if (identity) { - identity.roomIds = identity.roomIds.filter(roomId => roomId !== memberChange.roomId); - // no more encrypted rooms with this user, remove - if (identity.roomIds.length === 0) { - userIdentities.remove(identity.userId); - } else { - userIdentities.set(identity); - } + else if (memberChange.hasLeft) { + const {roomId} = memberChange; + // if we left the room, remove room from all user identities in the room + if (memberChange.userId === this._ownUserId) { + const userIds = await txn.roomMembers.getAllUserIds(roomId); + await Promise.all(userIds.map(userId => { + return this._removeRoomFromUserIdentity(roomId, userId, txn); + })); + } else { + await this._removeRoomFromUserIdentity(roomId, memberChange.userId, txn); } } } diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 60e03193..aba7d07d 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -85,6 +85,7 @@ export class RoomEncryption { async writeMemberChanges(memberChanges, txn, log) { let shouldFlush = false; const memberChangesArray = Array.from(memberChanges.values()); + // this also clears our session if we leave the room ourselves if (memberChangesArray.some(m => m.hasLeft)) { log.log({ l: "discardOutboundSession", diff --git a/src/matrix/storage/idb/stores/DeviceIdentityStore.js b/src/matrix/storage/idb/stores/DeviceIdentityStore.js index 4d209532..fed8878b 100644 --- a/src/matrix/storage/idb/stores/DeviceIdentityStore.js +++ b/src/matrix/storage/idb/stores/DeviceIdentityStore.js @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import {MAX_UNICODE, MIN_UNICODE} from "./common.js"; + function encodeKey(userId, deviceId) { return `${userId}|${deviceId}`; } @@ -66,4 +68,11 @@ export class DeviceIdentityStore { remove(userId, deviceId) { this._store.delete(encodeKey(userId, deviceId)); } + + removeAllForUser(userId) { + // exclude both keys as they are theoretical min and max, + // but we should't have a match for just the room id, or room id with max + const range = IDBKeyRange.bound(encodeKey(userId, MIN_UNICODE), encodeKey(userId, MAX_UNICODE), true, true); + this._store.delete(range); + } } diff --git a/src/matrix/storage/idb/stores/RoomStateStore.js b/src/matrix/storage/idb/stores/RoomStateStore.js index 4b5ea118..20ef6942 100644 --- a/src/matrix/storage/idb/stores/RoomStateStore.js +++ b/src/matrix/storage/idb/stores/RoomStateStore.js @@ -1,5 +1,6 @@ /* Copyright 2020 Bruno Windels +Copyright 2021 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. @@ -14,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -const MAX_UNICODE = "\u{10FFFF}"; +import {MAX_UNICODE} from "./common.js"; export class RoomStateStore { constructor(idbStore) { diff --git a/src/matrix/storage/idb/stores/common.js b/src/matrix/storage/idb/stores/common.js new file mode 100644 index 00000000..e05fe486 --- /dev/null +++ b/src/matrix/storage/idb/stores/common.js @@ -0,0 +1,18 @@ +/* +Copyright 2021 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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export const MIN_UNICODE = "\u{0}"; +export const MAX_UNICODE = "\u{10FFFF}";