mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 11:35:04 +01:00
store e2ee session values as well in localStorage
This commit is contained in:
parent
cd071e47e0
commit
77bd0d3f3c
@ -233,6 +233,7 @@ export class SessionContainer {
|
|||||||
platform: this._platform,
|
platform: this._platform,
|
||||||
});
|
});
|
||||||
await this._session.load(log);
|
await this._session.load(log);
|
||||||
|
// TODO: check instead storage doesn't have an identity
|
||||||
if (isNewLogin) {
|
if (isNewLogin) {
|
||||||
this._status.set(LoadStatus.SessionSetup);
|
this._status.set(LoadStatus.SessionSetup);
|
||||||
await log.wrap("createIdentity", log => this._session.createIdentity(log));
|
await log.wrap("createIdentity", log => this._session.createIdentity(log));
|
||||||
|
@ -15,12 +15,12 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import anotherjson from "../../../lib/another-json/index.js";
|
import anotherjson from "../../../lib/another-json/index.js";
|
||||||
import {SESSION_KEY_PREFIX, OLM_ALGORITHM, MEGOLM_ALGORITHM} from "./common.js";
|
import {SESSION_E2EE_KEY_PREFIX, OLM_ALGORITHM, MEGOLM_ALGORITHM} from "./common.js";
|
||||||
|
|
||||||
// use common prefix so it's easy to clear properties that are not e2ee related during session clear
|
// use common prefix so it's easy to clear properties that are not e2ee related during session clear
|
||||||
const ACCOUNT_SESSION_KEY = SESSION_KEY_PREFIX + "olmAccount";
|
const ACCOUNT_SESSION_KEY = SESSION_E2EE_KEY_PREFIX + "olmAccount";
|
||||||
const DEVICE_KEY_FLAG_SESSION_KEY = SESSION_KEY_PREFIX + "areDeviceKeysUploaded";
|
const DEVICE_KEY_FLAG_SESSION_KEY = SESSION_E2EE_KEY_PREFIX + "areDeviceKeysUploaded";
|
||||||
const SERVER_OTK_COUNT_SESSION_KEY = SESSION_KEY_PREFIX + "serverOTKCount";
|
const SERVER_OTK_COUNT_SESSION_KEY = SESSION_E2EE_KEY_PREFIX + "serverOTKCount";
|
||||||
|
|
||||||
export class Account {
|
export class Account {
|
||||||
static async load({olm, pickleKey, hsApi, userId, deviceId, olmWorker, txn}) {
|
static async load({olm, pickleKey, hsApi, userId, deviceId, olmWorker, txn}) {
|
||||||
|
@ -20,7 +20,7 @@ import {createEnum} from "../../utils/enum.js";
|
|||||||
export const DecryptionSource = createEnum("Sync", "Timeline", "Retry");
|
export const DecryptionSource = createEnum("Sync", "Timeline", "Retry");
|
||||||
|
|
||||||
// use common prefix so it's easy to clear properties that are not e2ee related during session clear
|
// use common prefix so it's easy to clear properties that are not e2ee related during session clear
|
||||||
export const SESSION_KEY_PREFIX = "e2ee:";
|
export const SESSION_E2EE_KEY_PREFIX = "e2ee:";
|
||||||
export const OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
|
export const OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
|
||||||
export const MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
|
export const MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import {IDBKey} from "./Transaction";
|
|||||||
export interface ITransaction {
|
export interface ITransaction {
|
||||||
idbFactory: IDBFactory;
|
idbFactory: IDBFactory;
|
||||||
IDBKeyRange: typeof IDBKeyRange;
|
IDBKeyRange: typeof IDBKeyRange;
|
||||||
|
databaseName: string;
|
||||||
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, keys: IDBKey[] | undefined);
|
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, keys: IDBKey[] | undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +56,10 @@ export class QueryTarget<T> {
|
|||||||
return this._transaction.IDBKeyRange;
|
return this._transaction.IDBKeyRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get databaseName(): string {
|
||||||
|
return this._transaction.databaseName;
|
||||||
|
}
|
||||||
|
|
||||||
_openCursor(range?: IDBQuery, direction?: IDBCursorDirection): IDBRequest<IDBCursorWithValue | null> {
|
_openCursor(range?: IDBQuery, direction?: IDBCursorDirection): IDBRequest<IDBCursorWithValue | null> {
|
||||||
if (range && direction) {
|
if (range && direction) {
|
||||||
return this._target.openCursor(range, direction);
|
return this._target.openCursor(range, direction);
|
||||||
@ -269,6 +274,7 @@ import {QueryTargetWrapper, Store} from "./Store";
|
|||||||
export function tests() {
|
export function tests() {
|
||||||
|
|
||||||
class MockTransaction extends MockIDBImpl {
|
class MockTransaction extends MockIDBImpl {
|
||||||
|
get databaseName(): string { return "mockdb"; }
|
||||||
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {}
|
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {IDOMStorage} from "./types";
|
||||||
import {Transaction} from "./Transaction";
|
import {Transaction} from "./Transaction";
|
||||||
import { STORE_NAMES, StoreNames, StorageError } from "../common";
|
import { STORE_NAMES, StoreNames, StorageError } from "../common";
|
||||||
import { reqAsPromise } from "./utils";
|
import { reqAsPromise } from "./utils";
|
||||||
@ -29,13 +30,15 @@ export class Storage {
|
|||||||
readonly idbFactory: IDBFactory
|
readonly idbFactory: IDBFactory
|
||||||
readonly IDBKeyRange: typeof IDBKeyRange;
|
readonly IDBKeyRange: typeof IDBKeyRange;
|
||||||
readonly storeNames: typeof StoreNames;
|
readonly storeNames: typeof StoreNames;
|
||||||
|
readonly localStorage: IDOMStorage;
|
||||||
|
|
||||||
constructor(idbDatabase: IDBDatabase, idbFactory: IDBFactory, _IDBKeyRange: typeof IDBKeyRange, hasWebkitEarlyCloseTxnBug: boolean, logger: BaseLogger) {
|
constructor(idbDatabase: IDBDatabase, idbFactory: IDBFactory, _IDBKeyRange: typeof IDBKeyRange, hasWebkitEarlyCloseTxnBug: boolean, localStorage: IDOMStorage, logger: BaseLogger) {
|
||||||
this._db = idbDatabase;
|
this._db = idbDatabase;
|
||||||
this.idbFactory = idbFactory;
|
this.idbFactory = idbFactory;
|
||||||
this.IDBKeyRange = _IDBKeyRange;
|
this.IDBKeyRange = _IDBKeyRange;
|
||||||
this._hasWebkitEarlyCloseTxnBug = hasWebkitEarlyCloseTxnBug;
|
this._hasWebkitEarlyCloseTxnBug = hasWebkitEarlyCloseTxnBug;
|
||||||
this.storeNames = StoreNames;
|
this.storeNames = StoreNames;
|
||||||
|
this.localStorage = localStorage;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,4 +82,8 @@ export class Storage {
|
|||||||
close(): void {
|
close(): void {
|
||||||
this._db.close();
|
this._db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get databaseName(): string {
|
||||||
|
return this._db.name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {IDOMStorage} from "./types";
|
||||||
import {Storage} from "./Storage";
|
import {Storage} from "./Storage";
|
||||||
import { openDatabase, reqAsPromise } from "./utils";
|
import { openDatabase, reqAsPromise } from "./utils";
|
||||||
import { exportSession, importSession, Export } from "./export";
|
import { exportSession, importSession, Export } from "./export";
|
||||||
@ -23,8 +24,8 @@ import { BaseLogger } from "../../../logging/BaseLogger.js";
|
|||||||
import { LogItem } from "../../../logging/LogItem.js";
|
import { LogItem } from "../../../logging/LogItem.js";
|
||||||
|
|
||||||
const sessionName = (sessionId: string) => `hydrogen_session_${sessionId}`;
|
const sessionName = (sessionId: string) => `hydrogen_session_${sessionId}`;
|
||||||
const openDatabaseWithSessionId = function(sessionId: string, idbFactory: IDBFactory, log: LogItem) {
|
const openDatabaseWithSessionId = function(sessionId: string, idbFactory: IDBFactory, localStorage: IDOMStorage, log: LogItem) {
|
||||||
const create = (db, txn, oldVersion, version) => createStores(db, txn, oldVersion, version, log);
|
const create = (db, txn, oldVersion, version) => createStores(db, txn, oldVersion, version, localStorage, log);
|
||||||
return openDatabase(sessionName(sessionId), create, schema.length, idbFactory);
|
return openDatabase(sessionName(sessionId), create, schema.length, idbFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +53,14 @@ async function requestPersistedStorage(): Promise<boolean> {
|
|||||||
export class StorageFactory {
|
export class StorageFactory {
|
||||||
private _serviceWorkerHandler: ServiceWorkerHandler;
|
private _serviceWorkerHandler: ServiceWorkerHandler;
|
||||||
private _idbFactory: IDBFactory;
|
private _idbFactory: IDBFactory;
|
||||||
private _IDBKeyRange: typeof IDBKeyRange
|
private _IDBKeyRange: typeof IDBKeyRange;
|
||||||
|
private _localStorage: IDOMStorage;
|
||||||
|
|
||||||
constructor(serviceWorkerHandler: ServiceWorkerHandler, idbFactory: IDBFactory = window.indexedDB, _IDBKeyRange = window.IDBKeyRange) {
|
constructor(serviceWorkerHandler: ServiceWorkerHandler, idbFactory: IDBFactory = window.indexedDB, _IDBKeyRange = window.IDBKeyRange, localStorage: IDOMStorage = window.localStorage) {
|
||||||
this._serviceWorkerHandler = serviceWorkerHandler;
|
this._serviceWorkerHandler = serviceWorkerHandler;
|
||||||
this._idbFactory = idbFactory;
|
this._idbFactory = idbFactory;
|
||||||
this._IDBKeyRange = _IDBKeyRange;
|
this._IDBKeyRange = _IDBKeyRange;
|
||||||
|
this._localStorage = localStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(sessionId: string, log: LogItem): Promise<Storage> {
|
async create(sessionId: string, log: LogItem): Promise<Storage> {
|
||||||
@ -70,8 +73,8 @@ export class StorageFactory {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const hasWebkitEarlyCloseTxnBug = await detectWebkitEarlyCloseTxnBug(this._idbFactory);
|
const hasWebkitEarlyCloseTxnBug = await detectWebkitEarlyCloseTxnBug(this._idbFactory);
|
||||||
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, log);
|
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);
|
||||||
return new Storage(db, this._idbFactory, this._IDBKeyRange, hasWebkitEarlyCloseTxnBug, log.logger);
|
return new Storage(db, this._idbFactory, this._IDBKeyRange, hasWebkitEarlyCloseTxnBug, this._localStorage, log.logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(sessionId: string): Promise<IDBDatabase> {
|
delete(sessionId: string): Promise<IDBDatabase> {
|
||||||
@ -81,21 +84,22 @@ export class StorageFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async export(sessionId: string, log: LogItem): Promise<Export> {
|
async export(sessionId: string, log: LogItem): Promise<Export> {
|
||||||
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, log);
|
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);
|
||||||
return await exportSession(db);
|
return await exportSession(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
async import(sessionId: string, data: Export, log: LogItem): Promise<void> {
|
async import(sessionId: string, data: Export, log: LogItem): Promise<void> {
|
||||||
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, log);
|
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);
|
||||||
return await importSession(db, data);
|
return await importSession(db, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createStores(db: IDBDatabase, txn: IDBTransaction, oldVersion: number | null, version: number, log: LogItem): Promise<void> {
|
async function createStores(db: IDBDatabase, txn: IDBTransaction, oldVersion: number | null, version: number, localStorage: IDOMStorage, log: LogItem): Promise<void> {
|
||||||
const startIdx = oldVersion || 0;
|
const startIdx = oldVersion || 0;
|
||||||
return log.wrap({l: "storage migration", oldVersion, version}, async log => {
|
return log.wrap({l: "storage migration", oldVersion, version}, async log => {
|
||||||
for(let i = startIdx; i < version; ++i) {
|
for(let i = startIdx; i < version; ++i) {
|
||||||
await log.wrap(`v${i + 1}`, log => schema[i](db, txn, log));
|
const migrationFunc = schema[i];
|
||||||
|
await log.wrap(`v${i + 1}`, log => migrationFunc(db, txn, localStorage, log));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,10 @@ export class Transaction {
|
|||||||
return this._storage.IDBKeyRange;
|
return this._storage.IDBKeyRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get databaseName(): string {
|
||||||
|
return this._storage.databaseName;
|
||||||
|
}
|
||||||
|
|
||||||
get logger(): BaseLogger {
|
get logger(): BaseLogger {
|
||||||
return this._storage.logger;
|
return this._storage.logger;
|
||||||
}
|
}
|
||||||
@ -94,7 +98,7 @@ export class Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get session(): SessionStore {
|
get session(): SessionStore {
|
||||||
return this._store(StoreNames.session, idbStore => new SessionStore(idbStore));
|
return this._store(StoreNames.session, idbStore => new SessionStore(idbStore, this._storage.localStorage));
|
||||||
}
|
}
|
||||||
|
|
||||||
get roomSummary(): RoomSummaryStore {
|
get roomSummary(): RoomSummaryStore {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {IDOMStorage} from "./types";
|
||||||
import {iterateCursor, NOT_DONE, reqAsPromise} from "./utils";
|
import {iterateCursor, NOT_DONE, reqAsPromise} from "./utils";
|
||||||
import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js";
|
import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js";
|
||||||
import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js";
|
import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js";
|
||||||
@ -7,10 +8,13 @@ import {RoomStateEntry} from "./stores/RoomStateStore";
|
|||||||
import {SessionStore} from "./stores/SessionStore";
|
import {SessionStore} from "./stores/SessionStore";
|
||||||
import {encodeScopeTypeKey} from "./stores/OperationStore";
|
import {encodeScopeTypeKey} from "./stores/OperationStore";
|
||||||
import {MAX_UNICODE} from "./stores/common";
|
import {MAX_UNICODE} from "./stores/common";
|
||||||
|
import {LogItem} from "../../../logging/LogItem.js";
|
||||||
|
|
||||||
|
|
||||||
|
export type MigrationFunc = (db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: LogItem) => Promise<void> | void;
|
||||||
// FUNCTIONS SHOULD ONLY BE APPENDED!!
|
// FUNCTIONS SHOULD ONLY BE APPENDED!!
|
||||||
// the index in the array is the database version
|
// the index in the array is the database version
|
||||||
export const schema = [
|
export const schema: MigrationFunc[] = [
|
||||||
createInitialStores,
|
createInitialStores,
|
||||||
createMemberStore,
|
createMemberStore,
|
||||||
migrateSession,
|
migrateSession,
|
||||||
@ -64,7 +68,7 @@ async function createMemberStore(db: IDBDatabase, txn: IDBTransaction): Promise<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
//v3
|
//v3
|
||||||
async function migrateSession(db: IDBDatabase, txn: IDBTransaction): Promise<void> {
|
async function migrateSession(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage): Promise<void> {
|
||||||
const session = txn.objectStore("session");
|
const session = txn.objectStore("session");
|
||||||
try {
|
try {
|
||||||
const PRE_MIGRATION_KEY = 1;
|
const PRE_MIGRATION_KEY = 1;
|
||||||
@ -73,7 +77,7 @@ async function migrateSession(db: IDBDatabase, txn: IDBTransaction): Promise<voi
|
|||||||
session.delete(PRE_MIGRATION_KEY);
|
session.delete(PRE_MIGRATION_KEY);
|
||||||
const {syncToken, syncFilterId, serverVersions} = entry.value;
|
const {syncToken, syncFilterId, serverVersions} = entry.value;
|
||||||
// Cast ok here because only "set" is used and we don't look into return
|
// Cast ok here because only "set" is used and we don't look into return
|
||||||
const store = new SessionStore(session as any);
|
const store = new SessionStore(session as any, localStorage);
|
||||||
store.set("sync", {token: syncToken, filterId: syncFilterId});
|
store.set("sync", {token: syncToken, filterId: syncFilterId});
|
||||||
store.set("serverVersions", serverVersions);
|
store.set("serverVersions", serverVersions);
|
||||||
}
|
}
|
||||||
@ -156,7 +160,7 @@ function createTimelineRelationsStore(db: IDBDatabase) : void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//v11 doesn't change the schema, but ensures all userIdentities have all the roomIds they should (see #470)
|
//v11 doesn't change the schema, but ensures all userIdentities have all the roomIds they should (see #470)
|
||||||
async function fixMissingRoomsInUserIdentities(db, txn, log) {
|
async function fixMissingRoomsInUserIdentities(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: LogItem) {
|
||||||
const roomSummaryStore = txn.objectStore("roomSummary");
|
const roomSummaryStore = txn.objectStore("roomSummary");
|
||||||
const trackedRoomIds: string[] = [];
|
const trackedRoomIds: string[] = [];
|
||||||
await iterateCursor<SummaryData>(roomSummaryStore.openCursor(), roomSummary => {
|
await iterateCursor<SummaryData>(roomSummaryStore.openCursor(), roomSummary => {
|
||||||
|
@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import {Store} from "../Store";
|
import {Store} from "../Store";
|
||||||
|
import {IDOMStorage} from "../types";
|
||||||
|
import {SESSION_E2EE_KEY_PREFIX} from "../../../e2ee/common.js";
|
||||||
|
|
||||||
export interface SessionEntry {
|
export interface SessionEntry {
|
||||||
key: string;
|
key: string;
|
||||||
@ -22,9 +24,11 @@ export interface SessionEntry {
|
|||||||
|
|
||||||
export class SessionStore {
|
export class SessionStore {
|
||||||
private _sessionStore: Store<SessionEntry>
|
private _sessionStore: Store<SessionEntry>
|
||||||
|
private _localStorage: IDOMStorage;
|
||||||
|
|
||||||
constructor(sessionStore: Store<SessionEntry>) {
|
constructor(sessionStore: Store<SessionEntry>, localStorage: IDOMStorage) {
|
||||||
this._sessionStore = sessionStore;
|
this._sessionStore = sessionStore;
|
||||||
|
this._localStorage = localStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(key: string): Promise<any> {
|
async get(key: string): Promise<any> {
|
||||||
@ -34,15 +38,60 @@ export class SessionStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_writeKeyToLocalStorage(key: string, value: any) {
|
||||||
|
// we backup to localStorage so when idb gets cleared for some reason, we don't lose our e2ee identity
|
||||||
|
try {
|
||||||
|
const lsKey = `${this._sessionStore.databaseName}.session.${key}`;
|
||||||
|
const lsValue = JSON.stringify(value);
|
||||||
|
this._localStorage.setItem(lsKey, lsValue);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("could not write to localStorage", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeToLocalStorage() {
|
||||||
|
this._sessionStore.iterateValues(undefined, (value: any, key: string) => {
|
||||||
|
if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {
|
||||||
|
this._writeKeyToLocalStorage(key, value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tryRestoreFromLocalStorage(): boolean {
|
||||||
|
let success = false;
|
||||||
|
const lsPrefix = `${this._sessionStore.databaseName}.session.`;
|
||||||
|
const prefix = `${lsPrefix}${SESSION_E2EE_KEY_PREFIX}`;
|
||||||
|
for(let i = 0; i < this._localStorage.length; i += 1) {
|
||||||
|
const lsKey = this._localStorage.key(i)!;
|
||||||
|
if (lsKey.startsWith(prefix)) {
|
||||||
|
const value = JSON.parse(this._localStorage.getItem(lsKey)!);
|
||||||
|
const key = lsKey.substr(lsPrefix.length);
|
||||||
|
this._sessionStore.put({key, value});
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
set(key: string, value: any): void {
|
set(key: string, value: any): void {
|
||||||
|
if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {
|
||||||
|
this._writeKeyToLocalStorage(key, value);
|
||||||
|
}
|
||||||
this._sessionStore.put({key, value});
|
this._sessionStore.put({key, value});
|
||||||
}
|
}
|
||||||
|
|
||||||
add(key: string, value: any): void {
|
add(key: string, value: any): void {
|
||||||
|
if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {
|
||||||
|
this._writeKeyToLocalStorage(key, value);
|
||||||
|
}
|
||||||
this._sessionStore.add({key, value});
|
this._sessionStore.add({key, value});
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(key: string): void {
|
remove(key: string): void {
|
||||||
|
if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {
|
||||||
|
this._localStorage.removeItem(this._sessionStore.databaseName + key);
|
||||||
|
}
|
||||||
this._sessionStore.delete(key);
|
this._sessionStore.delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/matrix/storage/idb/types.ts
Normal file
23
src/matrix/storage/idb/types.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
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 interface IDOMStorage {
|
||||||
|
getItem(key: string): string | null;
|
||||||
|
setItem(key: string, value: string): void;
|
||||||
|
removeItem(key: string): void;
|
||||||
|
key(n: number): string | null;
|
||||||
|
readonly length: number;
|
||||||
|
}
|
@ -16,12 +16,13 @@ limitations under the License.
|
|||||||
|
|
||||||
import {FDBFactory, FDBKeyRange} from "../../lib/fake-indexeddb/index.js";
|
import {FDBFactory, FDBKeyRange} from "../../lib/fake-indexeddb/index.js";
|
||||||
import {StorageFactory} from "../matrix/storage/idb/StorageFactory";
|
import {StorageFactory} from "../matrix/storage/idb/StorageFactory";
|
||||||
|
import {IDOMStorage} from "../matrix/storage/idb/types";
|
||||||
import {Storage} from "../matrix/storage/idb/Storage";
|
import {Storage} from "../matrix/storage/idb/Storage";
|
||||||
import {Instance as nullLogger} from "../logging/NullLogger.js";
|
import {Instance as nullLogger} from "../logging/NullLogger.js";
|
||||||
import {openDatabase, CreateObjectStore} from "../matrix/storage/idb/utils";
|
import {openDatabase, CreateObjectStore} from "../matrix/storage/idb/utils";
|
||||||
|
|
||||||
export function createMockStorage(): Promise<Storage> {
|
export function createMockStorage(): Promise<Storage> {
|
||||||
return new StorageFactory(null as any, new FDBFactory(), FDBKeyRange).create("1", nullLogger.item);
|
return new StorageFactory(null as any, new FDBFactory(), FDBKeyRange, new MockLocalStorage()).create("1", nullLogger.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createMockDatabase(name: string, createObjectStore: CreateObjectStore, impl: MockIDBImpl): Promise<IDBDatabase> {
|
export function createMockDatabase(name: string, createObjectStore: CreateObjectStore, impl: MockIDBImpl): Promise<IDBDatabase> {
|
||||||
@ -39,3 +40,41 @@ export class MockIDBImpl {
|
|||||||
return FDBKeyRange;
|
return FDBKeyRange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockLocalStorage implements IDOMStorage {
|
||||||
|
private _map: Map<string, string>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._map = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(key: string): string | null {
|
||||||
|
return this._map.get(key) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(key: string, value: string) {
|
||||||
|
this._map.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(key: string): void {
|
||||||
|
this._map.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
get length(): number {
|
||||||
|
return this._map.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
key(n: number): string | null {
|
||||||
|
const it = this._map.keys();
|
||||||
|
let i = -1;
|
||||||
|
let result;
|
||||||
|
while (i < n) {
|
||||||
|
result = it.next();
|
||||||
|
if (result.done) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return result?.value || null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user