From b9ca3ceacd75b7ecd9aa70305bd3dbf78ee05546 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 14 Jun 2022 21:29:21 +0100 Subject: [PATCH] Remove unused sessionStore (#2455) * Remove unused sessionStorage layer * Move pending event abstraction into its temporary home * Add test coverage * Tweak * Fix tests mocks * Add coverage * Add coverage --- spec/TestClient.ts | 3 - spec/unit/crypto.spec.js | 7 +- spec/unit/crypto/backup.spec.js | 15 +- spec/unit/room.spec.ts | 2 + spec/unit/stores/indexeddb.spec.ts | 46 +++- src/client.ts | 20 +- src/crypto/index.ts | 12 - src/crypto/store/indexeddb-crypto-store.ts | 2 +- src/matrix.ts | 1 - src/models/room.ts | 40 +--- src/store/index.ts | 6 +- src/store/indexeddb.ts | 34 +++ src/store/memory.ts | 15 +- src/store/session/webstorage.js | 263 --------------------- src/store/stub.ts | 10 +- 15 files changed, 128 insertions(+), 348 deletions(-) delete mode 100644 src/store/session/webstorage.js diff --git a/spec/TestClient.ts b/spec/TestClient.ts index dc3d556b0..244a9d6e3 100644 --- a/spec/TestClient.ts +++ b/spec/TestClient.ts @@ -23,7 +23,6 @@ import MockHttpBackend from 'matrix-mock-request'; import { LocalStorageCryptoStore } from '../src/crypto/store/localStorage-crypto-store'; import { logger } from '../src/logger'; -import { WebStorageSessionStore } from "../src/store/session/webstorage"; import { syncPromise } from "./test-utils/test-utils"; import { createClient } from "../src/matrix"; import { ICreateClientOpts, IDownloadKeyResult, MatrixClient, PendingEventOrdering } from "../src/client"; @@ -53,7 +52,6 @@ export class TestClient { if (sessionStoreBackend === undefined) { sessionStoreBackend = new MockStorageApi(); } - const sessionStore = new WebStorageSessionStore(sessionStoreBackend); this.httpBackend = new MockHttpBackend(); @@ -62,7 +60,6 @@ export class TestClient { userId: userId, accessToken: accessToken, deviceId: deviceId, - sessionStore: sessionStore, request: this.httpBackend.requestFn as IHttpOpts["request"], ...options, }; diff --git a/spec/unit/crypto.spec.js b/spec/unit/crypto.spec.js index bce8a9d3b..ba74eb517 100644 --- a/spec/unit/crypto.spec.js +++ b/spec/unit/crypto.spec.js @@ -3,7 +3,6 @@ import '../olm-loader'; import { EventEmitter } from "events"; import { Crypto } from "../../src/crypto"; -import { WebStorageSessionStore } from "../../src/store/session/webstorage"; import { MemoryCryptoStore } from "../../src/crypto/store/memory-crypto-store"; import { MockStorageApi } from "../MockStorageApi"; import { TestClient } from "../TestClient"; @@ -14,6 +13,7 @@ import { sleep } from "../../src/utils"; import { CRYPTO_ENABLED } from "../../src/client"; import { DeviceInfo } from "../../src/crypto/deviceinfo"; import { logger } from '../../src/logger'; +import { MemoryStore } from "../../src"; const Olm = global.Olm; @@ -153,7 +153,7 @@ describe("Crypto", function() { beforeEach(async function() { const mockStorage = new MockStorageApi(); - const sessionStore = new WebStorageSessionStore(mockStorage); + const clientStore = new MemoryStore({ localStorage: mockStorage }); const cryptoStore = new MemoryCryptoStore(mockStorage); cryptoStore.storeEndToEndDeviceData({ @@ -180,10 +180,9 @@ describe("Crypto", function() { crypto = new Crypto( mockBaseApis, - sessionStore, "@alice:home.server", "FLIBBLE", - sessionStore, + clientStore, cryptoStore, mockRoomList, ); diff --git a/spec/unit/crypto/backup.spec.js b/spec/unit/crypto/backup.spec.js index 9f435dc91..cab0c0d0d 100644 --- a/spec/unit/crypto/backup.spec.js +++ b/spec/unit/crypto/backup.spec.js @@ -21,7 +21,6 @@ import * as olmlib from "../../../src/crypto/olmlib"; import { MatrixClient } from "../../../src/client"; import { MatrixEvent } from "../../../src/models/event"; import * as algorithms from "../../../src/crypto/algorithms"; -import { WebStorageSessionStore } from "../../../src/store/session/webstorage"; import { MemoryCryptoStore } from "../../../src/crypto/store/memory-crypto-store"; import { MockStorageApi } from "../../MockStorageApi"; import * as testUtils from "../../test-utils/test-utils"; @@ -118,7 +117,7 @@ function saveCrossSigningKeys(k) { Object.assign(keys, k); } -function makeTestClient(sessionStore, cryptoStore) { +function makeTestClient(cryptoStore) { const scheduler = [ "getQueueForEvent", "queueEvent", "removeEventFromQueue", "setProcessFunction", @@ -141,7 +140,6 @@ function makeTestClient(sessionStore, cryptoStore) { scheduler: scheduler, userId: "@alice:bar", deviceId: "device", - sessionStore: sessionStore, cryptoStore: cryptoStore, cryptoCallbacks: { getCrossSigningKey, saveCrossSigningKeys }, }); @@ -161,7 +159,6 @@ describe("MegolmBackup", function() { let mockOlmLib; let mockCrypto; let mockStorage; - let sessionStore; let cryptoStore; let megolmDecryption; beforeEach(async function() { @@ -174,7 +171,6 @@ describe("MegolmBackup", function() { mockCrypto.backupInfo = CURVE25519_BACKUP_INFO; mockStorage = new MockStorageApi(); - sessionStore = new WebStorageSessionStore(mockStorage); cryptoStore = new MemoryCryptoStore(mockStorage); olmDevice = new OlmDevice(cryptoStore); @@ -261,7 +257,7 @@ describe("MegolmBackup", function() { const ibGroupSession = new Olm.InboundGroupSession(); ibGroupSession.create(groupSession.session_key()); - const client = makeTestClient(sessionStore, cryptoStore); + const client = makeTestClient(cryptoStore); megolmDecryption = new MegolmDecryption({ userId: '@user:id', @@ -340,7 +336,7 @@ describe("MegolmBackup", function() { const ibGroupSession = new Olm.InboundGroupSession(); ibGroupSession.create(groupSession.session_key()); - const client = makeTestClient(sessionStore, cryptoStore); + const client = makeTestClient(cryptoStore); megolmDecryption = new MegolmDecryption({ userId: '@user:id', @@ -423,7 +419,7 @@ describe("MegolmBackup", function() { const ibGroupSession = new Olm.InboundGroupSession(); ibGroupSession.create(groupSession.session_key()); - const client = makeTestClient(sessionStore, cryptoStore); + const client = makeTestClient(cryptoStore); megolmDecryption = new MegolmDecryption({ userId: '@user:id', @@ -520,7 +516,6 @@ describe("MegolmBackup", function() { scheduler: scheduler, userId: "@alice:bar", deviceId: "device", - sessionStore: sessionStore, cryptoStore: cryptoStore, }); @@ -606,7 +601,7 @@ describe("MegolmBackup", function() { let client; beforeEach(function() { - client = makeTestClient(sessionStore, cryptoStore); + client = makeTestClient(cryptoStore); megolmDecryption = new MegolmDecryption({ userId: '@user:id', diff --git a/spec/unit/room.spec.ts b/spec/unit/room.spec.ts index 3807795d1..cd6aa4775 100644 --- a/spec/unit/room.spec.ts +++ b/spec/unit/room.spec.ts @@ -1555,6 +1555,8 @@ describe("Room", function() { return Promise.resolve(); }, getSyncToken: () => "sync_token", + getPendingEvents: jest.fn().mockResolvedValue([]), + setPendingEvents: jest.fn().mockResolvedValue(undefined), }, }; } diff --git a/spec/unit/stores/indexeddb.spec.ts b/spec/unit/stores/indexeddb.spec.ts index 06e3097ea..3fc7477cc 100644 --- a/spec/unit/stores/indexeddb.spec.ts +++ b/spec/unit/stores/indexeddb.spec.ts @@ -17,13 +17,17 @@ limitations under the License. import 'fake-indexeddb/auto'; import 'jest-localstorage-mock'; -import { IndexedDBStore, IStateEventWithRoomId } from "../../../src"; +import { IndexedDBStore, IStateEventWithRoomId, MemoryStore } from "../../../src"; import { emitPromise } from "../../test-utils/test-utils"; import { LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend"; describe("IndexedDBStore", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + const roomId = "!room:id"; it("should degrade to MemoryStore on IDB errors", async () => { - const roomId = "!room:id"; const store = new IndexedDBStore({ indexedDB: indexedDB, dbName: "database", @@ -69,4 +73,42 @@ describe("IndexedDBStore", () => { ]); expect(await store.getOutOfBandMembers(roomId)).toHaveLength(2); }); + + it("should use MemoryStore methods for pending events if no localStorage", async () => { + jest.spyOn(MemoryStore.prototype, "setPendingEvents"); + jest.spyOn(MemoryStore.prototype, "getPendingEvents"); + + const store = new IndexedDBStore({ + indexedDB: indexedDB, + dbName: "database", + localStorage: undefined, + }); + + const events = [{ type: "test" }]; + await store.setPendingEvents(roomId, events); + expect(MemoryStore.prototype.setPendingEvents).toHaveBeenCalledWith(roomId, events); + await expect(store.getPendingEvents(roomId)).resolves.toEqual(events); + expect(MemoryStore.prototype.getPendingEvents).toHaveBeenCalledWith(roomId); + }); + + it("should persist pending events to localStorage if available", async () => { + jest.spyOn(MemoryStore.prototype, "setPendingEvents"); + jest.spyOn(MemoryStore.prototype, "getPendingEvents"); + + const store = new IndexedDBStore({ + indexedDB: indexedDB, + dbName: "database", + localStorage, + }); + + await expect(store.getPendingEvents(roomId)).resolves.toEqual([]); + const events = [{ type: "test" }]; + await store.setPendingEvents(roomId, events); + expect(MemoryStore.prototype.setPendingEvents).not.toHaveBeenCalled(); + await expect(store.getPendingEvents(roomId)).resolves.toEqual(events); + expect(MemoryStore.prototype.getPendingEvents).not.toHaveBeenCalled(); + expect(localStorage.getItem("mx_pending_events_" + roomId)).toBe(JSON.stringify(events)); + await store.setPendingEvents(roomId, []); + expect(localStorage.getItem("mx_pending_events_" + roomId)).toBeNull(); + }); }); diff --git a/src/client.ts b/src/client.ts index c29578d20..d8003a1f3 100644 --- a/src/client.ts +++ b/src/client.ts @@ -168,7 +168,6 @@ import { import { IAbortablePromise, IdServerUnbindResult, IImageInfo, Preset, Visibility } from "./@types/partials"; import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper"; import { randomString } from "./randomstring"; -import { WebStorageSessionStore } from "./store/session/webstorage"; import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup"; import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace"; import { ISignatures } from "./@types/signed"; @@ -195,7 +194,6 @@ import { Thread, THREAD_RELATION_TYPE } from "./models/thread"; import { MBeaconInfoEventContent, M_BEACON_INFO } from "./@types/beacon"; export type Store = IStore; -export type SessionStore = WebStorageSessionStore; export type Callback = (err: Error | any | null, data?: T) => void; export type ResetTimelineCallback = (roomId: string) => boolean; @@ -315,14 +313,6 @@ export interface ICreateClientOpts { */ pickleKey?: string; - /** - * A store to be used for end-to-end crypto session data. Most data has been - * migrated out of here to `cryptoStore` instead. If not specified, - * end-to-end crypto will be disabled. The `createClient` helper - * _will not_ create this store at the moment. - */ - sessionStore?: SessionStore; - verificationMethods?: Array; /** @@ -897,7 +887,6 @@ export class MatrixClient extends TypedEventEmitter } = {}; public identityServer: IIdentityServerProvider; - public sessionStore: SessionStore; // XXX: Intended private, used in code. public http: MatrixHttpApi; // XXX: Intended private, used in code. public crypto: Crypto; // XXX: Intended private, used in code. public cryptoCallbacks: ICryptoCallbacks; // XXX: Intended private, used in code. @@ -1029,7 +1018,6 @@ export class MatrixClient extends TypedEventEmitter { - // This should be redundant post cross-signing is a thing, so just - // plonk it in localStorage for now. - this.sessionStore.setLocalTrustedBackupPubKey(trustedPubKey); - await this.backupManager.checkKeyBackup(); - } - /** */ public enableLazyLoading(): void { diff --git a/src/crypto/store/indexeddb-crypto-store.ts b/src/crypto/store/indexeddb-crypto-store.ts index 0d3552928..ecc3d86c3 100644 --- a/src/crypto/store/indexeddb-crypto-store.ts +++ b/src/crypto/store/indexeddb-crypto-store.ts @@ -157,7 +157,7 @@ export class IndexedDBCryptoStore implements CryptoStore { } }).then(backend => { this.backend = backend; - return backend as CryptoStore; + return backend; }); return this.backendPromise; diff --git a/src/matrix.ts b/src/matrix.ts index e2ce5e11c..6813655a9 100644 --- a/src/matrix.ts +++ b/src/matrix.ts @@ -41,7 +41,6 @@ export * from "./interactive-auth"; export * from "./service-types"; export * from "./store/memory"; export * from "./store/indexeddb"; -export * from "./store/session/webstorage"; export * from "./crypto/store/memory-crypto-store"; export * from "./crypto/store/indexeddb-crypto-store"; export * from "./content-repo"; diff --git a/src/models/room.ts b/src/models/room.ts index e2e09e477..3c8e5adcd 100644 --- a/src/models/room.ts +++ b/src/models/room.ts @@ -368,18 +368,16 @@ export class Room extends TypedEventEmitter if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached) { this.pendingEventList = []; - const serializedPendingEventList = client.sessionStore.store.getItem(pendingEventsKey(this.roomId)); - if (serializedPendingEventList) { - JSON.parse(serializedPendingEventList) - .forEach(async (serializedEvent: Partial) => { - const event = new MatrixEvent(serializedEvent); - if (event.getType() === EventType.RoomMessageEncrypted) { - await event.attemptDecryption(this.client.crypto); - } - event.setStatus(EventStatus.NOT_SENT); - this.addPendingEvent(event, event.getTxnId()); - }); - } + this.client.store.getPendingEvents(this.roomId).then(events => { + events.forEach(async (serializedEvent: Partial) => { + const event = new MatrixEvent(serializedEvent); + if (event.getType() === EventType.RoomMessageEncrypted) { + await event.attemptDecryption(this.client.crypto); + } + event.setStatus(EventStatus.NOT_SENT); + this.addPendingEvent(event, event.getTxnId()); + }); + }); } // awaited by getEncryptionTargetMembers while room members are loading @@ -2075,15 +2073,7 @@ export class Room extends TypedEventEmitter return isEventEncrypted || !isRoomEncrypted; }); - const { store } = this.client.sessionStore; - if (this.pendingEventList.length > 0) { - store.setItem( - pendingEventsKey(this.roomId), - JSON.stringify(pendingEvents), - ); - } else { - store.removeItem(pendingEventsKey(this.roomId)); - } + this.client.store.setPendingEvents(this.roomId, pendingEvents); } } @@ -3112,14 +3102,6 @@ export class Room extends TypedEventEmitter } } -/** - * @param {string} roomId ID of the current room - * @returns {string} Storage key to retrieve pending events - */ -function pendingEventsKey(roomId: string): string { - return `mx_pending_events_${roomId}`; -} - // a map from current event status to a list of allowed next statuses const ALLOWED_TRANSITIONS: Record = { [EventStatus.ENCRYPTING]: [ diff --git a/src/store/index.ts b/src/store/index.ts index f71f7c093..3f4a0dade 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -17,7 +17,7 @@ limitations under the License. import { EventType } from "../@types/event"; import { Room } from "../models/room"; import { User } from "../models/user"; -import { MatrixEvent } from "../models/event"; +import { IEvent, MatrixEvent } from "../models/event"; import { Filter } from "../filter"; import { RoomSummary } from "../models/room-summary"; import { IMinimalEvent, IRooms, ISyncResponse } from "../sync-accumulator"; @@ -218,4 +218,8 @@ export interface IStore { getClientOptions(): Promise; storeClientOptions(options: IStartClientOpts): Promise; + + getPendingEvents(roomId: string): Promise[]>; + + setPendingEvents(roomId: string, events: Partial[]): Promise; } diff --git a/src/store/indexeddb.ts b/src/store/indexeddb.ts index 7d6e3f17c..09a85fd1b 100644 --- a/src/store/indexeddb.ts +++ b/src/store/indexeddb.ts @@ -325,6 +325,40 @@ export class IndexedDBStore extends MemoryStore { } }; } + + // XXX: ideally these would be stored in indexeddb as part of the room but, + // we don't store rooms as such and instead accumulate entire sync responses atm. + public async getPendingEvents(roomId: string): Promise[]> { + if (!this.localStorage) return super.getPendingEvents(roomId); + + const serialized = this.localStorage.getItem(pendingEventsKey(roomId)); + if (serialized) { + try { + return JSON.parse(serialized); + } catch (e) { + logger.error("Could not parse persisted pending events", e); + } + } + return []; + } + + public async setPendingEvents(roomId: string, events: Partial[]): Promise { + if (!this.localStorage) return super.setPendingEvents(roomId, events); + + if (events.length > 0) { + this.localStorage.setItem(pendingEventsKey(roomId), JSON.stringify(events)); + } else { + this.localStorage.removeItem(pendingEventsKey(roomId)); + } + } +} + +/** + * @param {string} roomId ID of the current room + * @returns {string} Storage key to retrieve pending events + */ +function pendingEventsKey(roomId: string): string { + return `mx_pending_events_${roomId}`; } type DegradableFn, T> = (...args: A) => Promise; diff --git a/src/store/memory.ts b/src/store/memory.ts index 4d59fc138..cb49e425f 100644 --- a/src/store/memory.ts +++ b/src/store/memory.ts @@ -22,7 +22,7 @@ limitations under the License. import { EventType } from "../@types/event"; import { Room } from "../models/room"; import { User } from "../models/user"; -import { MatrixEvent } from "../models/event"; +import { IEvent, MatrixEvent } from "../models/event"; import { RoomState, RoomStateEvent } from "../models/room-state"; import { RoomMember } from "../models/room-member"; import { Filter } from "../filter"; @@ -48,7 +48,7 @@ export interface IOpts { * Construct a new in-memory data store for the Matrix Client. * @constructor * @param {Object=} opts Config options - * @param {LocalStorage} opts.localStorage The local storage instance to persist + * @param {Storage} opts.localStorage The local storage instance to persist * some forms of data such as tokens. Rooms will NOT be stored. */ export class MemoryStore implements IStore { @@ -60,8 +60,9 @@ export class MemoryStore implements IStore { // } private filters: Record> = {}; public accountData: Record = {}; // type : content - private readonly localStorage: Storage; + protected readonly localStorage: Storage; private oobMembers: Record = {}; // roomId: [member events] + private pendingEvents: { [roomId: string]: Partial[] } = {}; private clientOptions = {}; constructor(opts: IOpts = {}) { @@ -420,4 +421,12 @@ export class MemoryStore implements IStore { this.clientOptions = Object.assign({}, options); return Promise.resolve(); } + + public async getPendingEvents(roomId: string): Promise[]> { + return this.pendingEvents[roomId] ?? []; + } + + public async setPendingEvents(roomId: string, events: Partial[]): Promise { + this.pendingEvents[roomId] = events; + } } diff --git a/src/store/session/webstorage.js b/src/store/session/webstorage.js deleted file mode 100644 index f11bbe207..000000000 --- a/src/store/session/webstorage.js +++ /dev/null @@ -1,263 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 New Vector Ltd -Copyright 2018 New Vector Ltd -Copyright 2019 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. -*/ - -/** - * @module store/session/webstorage - */ - -import * as utils from "../../utils"; -import { logger } from '../../logger'; - -const DEBUG = false; // set true to enable console logging. -const E2E_PREFIX = "session.e2e."; - -/** - * Construct a web storage session store, capable of storing account keys, - * session keys and access tokens. - * @constructor - * @param {WebStorage} webStore A web storage implementation, e.g. - * 'window.localStorage' or 'window.sessionStorage' or a custom implementation. - * @throws if the supplied 'store' does not meet the Storage interface of the - * WebStorage API. - */ -export function WebStorageSessionStore(webStore) { - this.store = webStore; - if (!utils.isFunction(webStore.getItem) || - !utils.isFunction(webStore.setItem) || - !utils.isFunction(webStore.removeItem) || - !utils.isFunction(webStore.key) || - typeof(webStore.length) !== 'number' - ) { - throw new Error( - "Supplied webStore does not meet the WebStorage API interface", - ); - } -} - -WebStorageSessionStore.prototype = { - /** - * Remove the stored end to end account for the logged-in user. - */ - removeEndToEndAccount: function() { - this.store.removeItem(KEY_END_TO_END_ACCOUNT); - }, - - /** - * Load the end to end account for the logged-in user. - * Note that the end-to-end account is now stored in the - * crypto store rather than here: this remains here so - * old sessions can be migrated out of the session store. - * @return {?string} Base64 encoded account. - */ - getEndToEndAccount: function() { - return this.store.getItem(KEY_END_TO_END_ACCOUNT); - }, - - /** - * Retrieves the known devices for all users. - * @return {object} A map from user ID to map of device ID to keys for the device. - */ - getAllEndToEndDevices: function() { - const prefix = keyEndToEndDevicesForUser(''); - const devices = {}; - for (let i = 0; i < this.store.length; ++i) { - const key = this.store.key(i); - const userId = key.slice(prefix.length); - if (key.startsWith(prefix)) devices[userId] = getJsonItem(this.store, key); - } - return devices; - }, - - getEndToEndDeviceTrackingStatus: function() { - return getJsonItem(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS); - }, - - /** - * Get the sync token corresponding to the device list. - * - * @return {String?} token - */ - getEndToEndDeviceSyncToken: function() { - return getJsonItem(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN); - }, - - /** - * Removes all end to end device data from the store - */ - removeEndToEndDeviceData: function() { - removeByPrefix(this.store, keyEndToEndDevicesForUser('')); - removeByPrefix(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS); - removeByPrefix(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN); - }, - - /** - * Retrieve the end-to-end sessions between the logged-in user and another - * device. - * @param {string} deviceKey The public key of the other device. - * @return {object} A map from sessionId to Base64 end-to-end session. - */ - getEndToEndSessions: function(deviceKey) { - return getJsonItem(this.store, keyEndToEndSessions(deviceKey)); - }, - - /** - * Retrieve all end-to-end sessions between the logged-in user and other - * devices. - * @return {object} A map of {deviceKey -> {sessionId -> session pickle}} - */ - getAllEndToEndSessions: function() { - const deviceKeys = getKeysWithPrefix(this.store, keyEndToEndSessions('')); - const results = {}; - for (const k of deviceKeys) { - const unprefixedKey = k.slice(keyEndToEndSessions('').length); - results[unprefixedKey] = getJsonItem(this.store, k); - } - return results; - }, - - /** - * Remove all end-to-end sessions from the store - * This is used after migrating sessions awat from the sessions store. - */ - removeAllEndToEndSessions: function() { - removeByPrefix(this.store, keyEndToEndSessions('')); - }, - - /** - * Retrieve a list of all known inbound group sessions - * - * @return {{senderKey: string, sessionId: string}} - */ - getAllEndToEndInboundGroupSessionKeys: function() { - const prefix = E2E_PREFIX + 'inboundgroupsessions/'; - const result = []; - for (let i = 0; i < this.store.length; i++) { - const key = this.store.key(i); - if (!key.startsWith(prefix)) { - continue; - } - // we can't use split, as the components we are trying to split out - // might themselves contain '/' characters. We rely on the - // senderKey being a (32-byte) curve25519 key, base64-encoded - // (hence 43 characters long). - - result.push({ - senderKey: key.slice(prefix.length, prefix.length + 43), - sessionId: key.slice(prefix.length + 44), - }); - } - return result; - }, - - getEndToEndInboundGroupSession: function(senderKey, sessionId) { - const key = keyEndToEndInboundGroupSession(senderKey, sessionId); - return this.store.getItem(key); - }, - - removeAllEndToEndInboundGroupSessions: function() { - removeByPrefix(this.store, E2E_PREFIX + 'inboundgroupsessions/'); - }, - - /** - * Get the end-to-end state for all rooms - * @return {object} roomId -> object with the end-to-end info for the room. - */ - getAllEndToEndRooms: function() { - const roomKeys = getKeysWithPrefix(this.store, keyEndToEndRoom('')); - const results = {}; - for (const k of roomKeys) { - const unprefixedKey = k.slice(keyEndToEndRoom('').length); - results[unprefixedKey] = getJsonItem(this.store, k); - } - return results; - }, - - removeAllEndToEndRooms: function() { - removeByPrefix(this.store, keyEndToEndRoom('')); - }, - - setLocalTrustedBackupPubKey: function(pubkey) { - this.store.setItem(KEY_END_TO_END_TRUSTED_BACKUP_PUBKEY, pubkey); - }, - - // XXX: This store is deprecated really, but added this as a temporary - // thing until cross-signing lands. - getLocalTrustedBackupPubKey: function() { - return this.store.getItem(KEY_END_TO_END_TRUSTED_BACKUP_PUBKEY); - }, -}; - -const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account"; -const KEY_END_TO_END_DEVICE_SYNC_TOKEN = E2E_PREFIX + "device_sync_token"; -const KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS = E2E_PREFIX + "device_tracking"; -const KEY_END_TO_END_TRUSTED_BACKUP_PUBKEY = E2E_PREFIX + "trusted_backup_pubkey"; - -function keyEndToEndDevicesForUser(userId) { - return E2E_PREFIX + "devices/" + userId; -} - -function keyEndToEndSessions(deviceKey) { - return E2E_PREFIX + "sessions/" + deviceKey; -} - -function keyEndToEndInboundGroupSession(senderKey, sessionId) { - return E2E_PREFIX + "inboundgroupsessions/" + senderKey + "/" + sessionId; -} - -function keyEndToEndRoom(roomId) { - return E2E_PREFIX + "rooms/" + roomId; -} - -function getJsonItem(store, key) { - try { - // if the key is absent, store.getItem() returns null, and - // JSON.parse(null) === null, so this returns null. - return JSON.parse(store.getItem(key)); - } catch (e) { - debuglog("Failed to get key %s: %s", key, e); - debuglog(e.stack); - } - return null; -} - -function getKeysWithPrefix(store, prefix) { - const results = []; - for (let i = 0; i < store.length; ++i) { - const key = store.key(i); - if (key.startsWith(prefix)) results.push(key); - } - return results; -} - -function removeByPrefix(store, prefix) { - const toRemove = []; - for (let i = 0; i < store.length; ++i) { - const key = store.key(i); - if (key.startsWith(prefix)) toRemove.push(key); - } - for (const key of toRemove) { - store.removeItem(key); - } -} - -function debuglog(...args) { - if (DEBUG) { - logger.log(...args); - } -} diff --git a/src/store/stub.ts b/src/store/stub.ts index 1b3a8773f..c9fc57055 100644 --- a/src/store/stub.ts +++ b/src/store/stub.ts @@ -22,7 +22,7 @@ limitations under the License. import { EventType } from "../@types/event"; import { Room } from "../models/room"; import { User } from "../models/user"; -import { MatrixEvent } from "../models/event"; +import { IEvent, MatrixEvent } from "../models/event"; import { Filter } from "../filter"; import { ISavedSync, IStore } from "./index"; import { RoomSummary } from "../models/room-summary"; @@ -262,4 +262,12 @@ export class StubStore implements IStore { public storeClientOptions(options: object): Promise { return Promise.resolve(); } + + public async getPendingEvents(roomId: string): Promise[]> { + return []; + } + + public setPendingEvents(roomId: string, events: Partial[]): Promise { + return Promise.resolve(); + } }