diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 2fc72ede72f..f281fe356dc 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -28,7 +28,10 @@ import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service"; import { SendService as SendServiceAbstraction } from "@bitwarden/common/abstractions/send.service"; import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { SyncNotifierService as SyncNotifierServiceAbstraction } from "@bitwarden/common/abstractions/sync/syncNotifier.service.abstraction"; import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/abstractions/system.service"; @@ -123,7 +126,7 @@ export default class MainBackground { messagingService: MessagingServiceAbstraction; storageService: AbstractStorageService; secureStorageService: AbstractStorageService; - memoryStorageService: AbstractStorageService; + memoryStorageService: AbstractMemoryStorageService; i18nService: I18nServiceAbstraction; platformUtilsService: PlatformUtilsServiceAbstraction; logService: LogServiceAbstraction; diff --git a/apps/browser/src/background/service_factories/storage-service.factory.ts b/apps/browser/src/background/service_factories/storage-service.factory.ts index 51993277c92..c30bda731e6 100644 --- a/apps/browser/src/background/service_factories/storage-service.factory.ts +++ b/apps/browser/src/background/service_factories/storage-service.factory.ts @@ -1,4 +1,7 @@ -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { BrowserApi } from "../../browser/browserApi"; @@ -35,9 +38,9 @@ export function secureStorageServiceFactory( } export function memoryStorageServiceFactory( - cache: { memoryStorageService?: AbstractStorageService } & CachedServices, + cache: { memoryStorageService?: AbstractMemoryStorageService } & CachedServices, opts: MemoryStorageServiceInitOptions -): Promise { +): Promise { return factory(cache, "memoryStorageService", opts, async () => { if (BrowserApi.manifestVersion === 3) { return new LocalBackedSessionStorageService( diff --git a/apps/browser/src/clipboard/clipboard-state.ts b/apps/browser/src/clipboard/clipboard-state.ts deleted file mode 100644 index cfa2f9459f8..00000000000 --- a/apps/browser/src/clipboard/clipboard-state.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BrowserStateService } from "../services/abstractions/browser-state.service"; - -const clearClipboardStorageKey = "clearClipboardTime"; -export const getClearClipboardTime = async (stateService: BrowserStateService) => { - return await stateService.getFromSessionMemory(clearClipboardStorageKey); -}; - -export const setClearClipboardTime = async (stateService: BrowserStateService, time: number) => { - await stateService.setInSessionMemory(clearClipboardStorageKey, time); -}; diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts index 92c5dfb0170..ab29c86e984 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts @@ -1,5 +1,8 @@ import { BehaviorSubject } from "rxjs"; +import { AbstractMemoryStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; + import { BrowserStateService } from "../../services/browser-state.service"; import { browserSession } from "./browser-session.decorator"; @@ -11,18 +14,24 @@ import { sessionSync } from "./session-sync.decorator"; jest.mock("./session-syncer"); describe("browserSession decorator", () => { - it("should throw if StateService is not a constructor argument", () => { + it("should throw if neither StateService nor MemoryStorageService is a constructor argument", () => { @browserSession class TestClass {} expect(() => { new TestClass(); }).toThrowError( - "Cannot decorate TestClass with browserSession, Browser's StateService must be injected" + "Cannot decorate TestClass with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters" ); }); it("should create if StateService is a constructor argument", () => { - const stateService = Object.create(BrowserStateService.prototype, {}); + const stateService = Object.create(BrowserStateService.prototype, { + memoryStorageService: { + value: Object.create(MemoryStorageService.prototype, { + type: { value: MemoryStorageService.TYPE }, + }), + }, + }); @browserSession class TestClass { @@ -32,15 +41,28 @@ describe("browserSession decorator", () => { expect(new TestClass(stateService)).toBeDefined(); }); + it("should create if MemoryStorageService is a constructor argument", () => { + const memoryStorageService = Object.create(MemoryStorageService.prototype, { + type: { value: MemoryStorageService.TYPE }, + }); + + @browserSession + class TestClass { + constructor(private memoryStorageService: AbstractMemoryStorageService) {} + } + + expect(new TestClass(memoryStorageService)).toBeDefined(); + }); + describe("interaction with @sessionSync decorator", () => { - let stateService: BrowserStateService; + let memoryStorageService: MemoryStorageService; @browserSession class TestClass { @sessionSync({ initializer: (s: string) => s }) private behaviorSubject = new BehaviorSubject(""); - constructor(private stateService: BrowserStateService) {} + constructor(private memoryStorageService: MemoryStorageService) {} fromJSON(json: any) { this.behaviorSubject.next(json); @@ -48,16 +70,18 @@ describe("browserSession decorator", () => { } beforeEach(() => { - stateService = Object.create(BrowserStateService.prototype, {}) as BrowserStateService; + memoryStorageService = Object.create(MemoryStorageService.prototype, { + type: { value: MemoryStorageService.TYPE }, + }); }); it("should create a session syncer", () => { - const testClass = new TestClass(stateService) as any as SessionStorable; + const testClass = new TestClass(memoryStorageService) as any as SessionStorable; expect(testClass.__sessionSyncers.length).toEqual(1); }); it("should initialize the session syncer", () => { - const testClass = new TestClass(stateService) as any as SessionStorable; + const testClass = new TestClass(memoryStorageService) as any as SessionStorable; expect(testClass.__sessionSyncers[0].init).toHaveBeenCalled(); }); }); diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts index 5d9d56c1d71..dbb45ddba8e 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts @@ -1,6 +1,6 @@ import { Constructor } from "type-fest"; -import { BrowserStateService } from "../../services/browser-state.service"; +import { AbstractMemoryStorageService } from "@bitwarden/common/abstractions/storage.service"; import { SessionStorable } from "./session-storable"; import { SessionSyncer } from "./session-syncer"; @@ -22,32 +22,51 @@ export function browserSession>(constructor: TCto super(...args); // Require state service to be injected - const stateService: BrowserStateService = [this as any] - .concat(args) - .find( - (arg) => - typeof arg.setInSessionMemory === "function" && - typeof arg.getFromSessionMemory === "function" - ); - if (!stateService) { - throw new Error( - `Cannot decorate ${constructor.name} with browserSession, Browser's StateService must be injected` - ); - } + const storageService: AbstractMemoryStorageService = this.findStorageService( + [this as any].concat(args) + ); if (this.__syncedItemMetadata == null || !(this.__syncedItemMetadata instanceof Array)) { return; } this.__sessionSyncers = this.__syncedItemMetadata.map((metadata) => - this.buildSyncer(metadata, stateService) + this.buildSyncer(metadata, storageService) ); } - buildSyncer(metadata: SyncedItemMetadata, stateService: BrowserStateService) { - const syncer = new SessionSyncer((this as any)[metadata.propertyKey], stateService, metadata); + buildSyncer(metadata: SyncedItemMetadata, storageSerice: AbstractMemoryStorageService) { + const syncer = new SessionSyncer( + (this as any)[metadata.propertyKey], + storageSerice, + metadata + ); syncer.init(); return syncer; } + + findStorageService(args: any[]): AbstractMemoryStorageService { + const storageService = args.find(this.isMemoryStorageService); + + if (storageService) { + return storageService; + } + + const stateService = args.find( + (arg) => + arg?.memoryStorageService != null && this.isMemoryStorageService(arg.memoryStorageService) + ); + if (stateService) { + return stateService.memoryStorageService; + } + + throw new Error( + `Cannot decorate ${constructor.name} with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters` + ); + } + + isMemoryStorageService(arg: any): arg is AbstractMemoryStorageService { + return arg.type != null && arg.type === AbstractMemoryStorageService.TYPE; + } }; } diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts index c37b640f3f9..05d0da7527b 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts @@ -1,9 +1,10 @@ -import { awaitAsync, awaitAsync as flushAsyncObservables } from "@bitwarden/angular/../test-utils"; +import { awaitAsync } from "@bitwarden/angular/../test-utils"; import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, ReplaySubject } from "rxjs"; +import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; + import { BrowserApi } from "../../browser/browserApi"; -import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { SessionSyncer } from "./session-syncer"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -17,7 +18,7 @@ describe("session syncer", () => { initializer: (s: string) => s, initializeAs: "object", }; - let stateService: MockProxy; + let storageService: MockProxy; let sut: SessionSyncer; let behaviorSubject: BehaviorSubject; @@ -29,9 +30,9 @@ describe("session syncer", () => { manifest_version: 3, }); - stateService = mock(); - stateService.hasInSessionMemory.mockResolvedValue(false); - sut = new SessionSyncer(behaviorSubject, stateService, metaData); + storageService = mock(); + storageService.has.mockResolvedValue(false); + sut = new SessionSyncer(behaviorSubject, storageService, metaData); }); afterEach(() => { @@ -43,13 +44,13 @@ describe("session syncer", () => { describe("constructor", () => { it("should throw if subject is not an instance of Subject", () => { expect(() => { - new SessionSyncer({} as any, stateService, null); + new SessionSyncer({} as any, storageService, null); }).toThrowError("subject must inherit from Subject"); }); it("should create if either ctor or initializer is provided", () => { expect( - new SessionSyncer(behaviorSubject, stateService, { + new SessionSyncer(behaviorSubject, storageService, { propertyKey, sessionKey, ctor: String, @@ -57,7 +58,7 @@ describe("session syncer", () => { }) ).toBeDefined(); expect( - new SessionSyncer(behaviorSubject, stateService, { + new SessionSyncer(behaviorSubject, storageService, { propertyKey, sessionKey, initializer: (s: any) => s, @@ -67,7 +68,7 @@ describe("session syncer", () => { }); it("should throw if neither ctor or initializer is provided", () => { expect(() => { - new SessionSyncer(behaviorSubject, stateService, { + new SessionSyncer(behaviorSubject, storageService, { propertyKey, sessionKey, initializeAs: "object", @@ -82,7 +83,7 @@ describe("session syncer", () => { replaySubject.next("1"); replaySubject.next("2"); replaySubject.next("3"); - sut = new SessionSyncer(replaySubject, stateService, metaData); + sut = new SessionSyncer(replaySubject, storageService, metaData); // block observing the subject jest.spyOn(sut as any, "observe").mockImplementation(); @@ -93,7 +94,7 @@ describe("session syncer", () => { it("should ignore BehaviorSubject's initial value", () => { const behaviorSubject = new BehaviorSubject("initial"); - sut = new SessionSyncer(behaviorSubject, stateService, metaData); + sut = new SessionSyncer(behaviorSubject, storageService, metaData); // block observing the subject jest.spyOn(sut as any, "observe").mockImplementation(); @@ -103,7 +104,7 @@ describe("session syncer", () => { }); it("should grab an initial value from storage if it exists", async () => { - stateService.hasInSessionMemory.mockResolvedValue(true); + storageService.has.mockResolvedValue(true); //Block a call to update const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); @@ -114,7 +115,7 @@ describe("session syncer", () => { }); it("should not grab an initial value from storage if it does not exist", async () => { - stateService.hasInSessionMemory.mockResolvedValue(false); + storageService.has.mockResolvedValue(false); //Block a call to update const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); @@ -139,8 +140,8 @@ describe("session syncer", () => { it("should update the session memory", async () => { // await finishing of fire-and-forget operation await new Promise((resolve) => setTimeout(resolve, 100)); - expect(stateService.setInSessionMemory).toHaveBeenCalledTimes(1); - expect(stateService.setInSessionMemory).toHaveBeenCalledWith(sessionKey, "test"); + expect(storageService.save).toHaveBeenCalledTimes(1); + expect(storageService.save).toHaveBeenCalledWith(sessionKey, "test"); }); it("should update sessionSyncers in other contexts", async () => { @@ -170,27 +171,29 @@ describe("session syncer", () => { it("should ignore messages with the wrong command", async () => { await sut.updateFromMessage({ command: "wrong_command", id: sut.id }); - expect(stateService.getFromSessionMemory).not.toHaveBeenCalled(); + expect(storageService.getBypassCache).not.toHaveBeenCalled(); expect(nextSpy).not.toHaveBeenCalled(); }); it("should ignore messages from itself", async () => { await sut.updateFromMessage({ command: `${sessionKey}_update`, id: sut.id }); - expect(stateService.getFromSessionMemory).not.toHaveBeenCalled(); + expect(storageService.getBypassCache).not.toHaveBeenCalled(); expect(nextSpy).not.toHaveBeenCalled(); }); it("should update from message on emit from another instance", async () => { const builder = jest.fn(); jest.spyOn(SyncedItemMetadata, "builder").mockReturnValue(builder); - stateService.getFromSessionMemory.mockResolvedValue("test"); + storageService.getBypassCache.mockResolvedValue("test"); await sut.updateFromMessage({ command: `${sessionKey}_update`, id: "different_id" }); - await flushAsyncObservables(); + await awaitAsync(); - expect(stateService.getFromSessionMemory).toHaveBeenCalledTimes(1); - expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(sessionKey, builder); + expect(storageService.getBypassCache).toHaveBeenCalledTimes(1); + expect(storageService.getBypassCache).toHaveBeenCalledWith(sessionKey, { + deserializer: builder, + }); expect(nextSpy).toHaveBeenCalledTimes(1); expect(nextSpy).toHaveBeenCalledWith("test"); diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts index 91b371ef817..8c24ffb1852 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts @@ -1,9 +1,9 @@ import { BehaviorSubject, concatMap, ReplaySubject, Subject, Subscription } from "rxjs"; +import { AbstractMemoryStorageService } from "@bitwarden/common/abstractions/storage.service"; import { Utils } from "@bitwarden/common/misc/utils"; import { BrowserApi } from "../../browser/browserApi"; -import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -16,7 +16,7 @@ export class SessionSyncer { constructor( private subject: Subject, - private stateService: BrowserStateService, + private memoryStorageService: AbstractMemoryStorageService, private metaData: SyncedItemMetadata ) { if (!(subject instanceof Subject)) { @@ -43,7 +43,7 @@ export class SessionSyncer { this.observe(); // must be synchronous - this.stateService.hasInSessionMemory(this.metaData.sessionKey).then((hasInSessionMemory) => { + this.memoryStorageService.has(this.metaData.sessionKey).then((hasInSessionMemory) => { if (hasInSessionMemory) { this.update(); } @@ -86,13 +86,15 @@ export class SessionSyncer { async update() { const builder = SyncedItemMetadata.builder(this.metaData); - const value = await this.stateService.getFromSessionMemory(this.metaData.sessionKey, builder); + const value = await this.memoryStorageService.getBypassCache(this.metaData.sessionKey, { + deserializer: builder, + }); this.ignoreNUpdates = 1; this.subject.next(value); } private async updateSession(value: any) { - await this.stateService.setInSessionMemory(this.metaData.sessionKey, value); + await this.memoryStorageService.save(this.metaData.sessionKey, value); await BrowserApi.sendMessage(this.updateMessageCommand, { id: this.id }); } diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 05cd4ede8f5..677e8648bc8 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -40,7 +40,10 @@ import { SendService } from "@bitwarden/common/abstractions/send.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/abstractions/state.service"; import { StateMigrationService } from "@bitwarden/common/abstractions/stateMigration.service"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; @@ -329,7 +332,7 @@ function getBgService(service: keyof MainBackground) { useFactory: ( storageService: AbstractStorageService, secureStorageService: AbstractStorageService, - memoryStorageService: AbstractStorageService, + memoryStorageService: AbstractMemoryStorageService, logService: LogServiceAbstraction, stateMigrationService: StateMigrationService ) => { diff --git a/apps/browser/src/services/abstractions/browser-state.service.ts b/apps/browser/src/services/abstractions/browser-state.service.ts index afe5e2ce694..0c8a35afbd2 100644 --- a/apps/browser/src/services/abstractions/browser-state.service.ts +++ b/apps/browser/src/services/abstractions/browser-state.service.ts @@ -1,5 +1,3 @@ -import { Jsonify } from "type-fest"; - import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/abstractions/state.service"; import { StorageOptions } from "@bitwarden/common/models/domain/storage-options"; @@ -9,9 +7,6 @@ import { BrowserGroupingsComponentState } from "../../models/browserGroupingsCom import { BrowserSendComponentState } from "../../models/browserSendComponentState"; export abstract class BrowserStateService extends BaseStateServiceAbstraction { - abstract hasInSessionMemory(key: string): Promise; - abstract getFromSessionMemory(key: string, deserializer?: (obj: Jsonify) => T): Promise; - abstract setInSessionMemory(key: string, value: any): Promise; getBrowserGroupingComponentState: ( options?: StorageOptions ) => Promise; diff --git a/apps/browser/src/services/browser-state.service.spec.ts b/apps/browser/src/services/browser-state.service.spec.ts index 7d6f8456314..84ac71664da 100644 --- a/apps/browser/src/services/browser-state.service.spec.ts +++ b/apps/browser/src/services/browser-state.service.spec.ts @@ -1,9 +1,8 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { - MemoryStorageServiceInterface, + AbstractMemoryStorageService, AbstractStorageService, } from "@bitwarden/common/abstractions/storage.service"; import { SendType } from "@bitwarden/common/enums/sendType"; @@ -18,9 +17,10 @@ import { BrowserComponentState } from "../models/browserComponentState"; import { BrowserGroupingsComponentState } from "../models/browserGroupingsComponentState"; import { BrowserSendComponentState } from "../models/browserSendComponentState"; -import { AbstractKeyGenerationService } from "./abstractions/abstractKeyGeneration.service"; import { BrowserStateService } from "./browser-state.service"; -import { LocalBackedSessionStorageService } from "./localBackedSessionStorage.service"; + +// disable session syncing to just test class +jest.mock("../decorators/session-sync-observable/"); describe("Browser State Service", () => { let secureStorageService: MockProxy; @@ -50,41 +50,8 @@ describe("Browser State Service", () => { state.activeUserId = userId; }); - describe("direct memory storage access", () => { - let memoryStorageService: LocalBackedSessionStorageService; - - beforeEach(() => { - // We need `AbstractCachedStorageService` in the prototype chain to correctly test cache bypass. - memoryStorageService = new LocalBackedSessionStorageService( - mock(), - mock() - ); - - sut = new BrowserStateService( - diskStorageService, - secureStorageService, - memoryStorageService, - logService, - stateMigrationService, - stateFactory, - useAccountCache - ); - }); - - it("should bypass cache if possible", async () => { - const spyBypass = jest - .spyOn(memoryStorageService, "getBypassCache") - .mockResolvedValue("value"); - const spyGet = jest.spyOn(memoryStorageService, "get"); - const result = await sut.getFromSessionMemory("key"); - expect(spyBypass).toHaveBeenCalled(); - expect(spyGet).not.toHaveBeenCalled(); - expect(result).toBe("value"); - }); - }); - describe("state methods", () => { - let memoryStorageService: MockProxy; + let memoryStorageService: MockProxy; beforeEach(() => { memoryStorageService = mock(); diff --git a/apps/browser/src/services/browser-state.service.ts b/apps/browser/src/services/browser-state.service.ts index 57873f23073..5de24a2a4b8 100644 --- a/apps/browser/src/services/browser-state.service.ts +++ b/apps/browser/src/services/browser-state.service.ts @@ -1,7 +1,5 @@ import { BehaviorSubject } from "rxjs"; -import { Jsonify } from "type-fest"; -import { AbstractCachedStorageService } from "@bitwarden/common/abstractions/storage.service"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { StorageOptions } from "@bitwarden/common/models/domain/storage-options"; import { StateService as BaseStateService } from "@bitwarden/common/services/state.service"; @@ -36,20 +34,6 @@ export class BrowserStateService protected accountDeserializer = Account.fromJSON; - async hasInSessionMemory(key: string): Promise { - return await this.memoryStorageService.has(key); - } - - async getFromSessionMemory(key: string, deserializer?: (obj: Jsonify) => T): Promise { - return this.memoryStorageService instanceof AbstractCachedStorageService - ? await this.memoryStorageService.getBypassCache(key, { deserializer: deserializer }) - : await this.memoryStorageService.get(key); - } - - async setInSessionMemory(key: string, value: any): Promise { - await this.memoryStorageService.save(key, value); - } - async addAccount(account: Account) { // Apply browser overrides to default account values account = new Account(account); diff --git a/apps/browser/src/services/localBackedSessionStorage.service.ts b/apps/browser/src/services/localBackedSessionStorage.service.ts index 54b0537f725..662a431b797 100644 --- a/apps/browser/src/services/localBackedSessionStorage.service.ts +++ b/apps/browser/src/services/localBackedSessionStorage.service.ts @@ -1,10 +1,7 @@ import { Jsonify } from "type-fest"; import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; -import { - AbstractCachedStorageService, - MemoryStorageServiceInterface, -} from "@bitwarden/common/abstractions/storage.service"; +import { AbstractMemoryStorageService } from "@bitwarden/common/abstractions/storage.service"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { MemoryStorageOptions } from "@bitwarden/common/models/domain/storage-options"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; @@ -21,10 +18,7 @@ const keys = { sessionKey: "session", }; -export class LocalBackedSessionStorageService - extends AbstractCachedStorageService - implements MemoryStorageServiceInterface -{ +export class LocalBackedSessionStorageService extends AbstractMemoryStorageService { private cache = new Map(); private localStorage = new BrowserLocalStorageService(); private sessionStorage = new BrowserMemoryStorageService(); diff --git a/apps/web/src/app/core/state/state.service.ts b/apps/web/src/app/core/state/state.service.ts index 577301cf867..0b7a56790f4 100644 --- a/apps/web/src/app/core/state/state.service.ts +++ b/apps/web/src/app/core/state/state.service.ts @@ -8,7 +8,10 @@ import { } from "@bitwarden/angular/services/injection-tokens"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { StateMigrationService } from "@bitwarden/common/abstractions/stateMigration.service"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { CipherData } from "@bitwarden/common/models/data/cipher.data"; import { CollectionData } from "@bitwarden/common/models/data/collection.data"; @@ -25,7 +28,7 @@ export class StateService extends BaseStateService { constructor( storageService: AbstractStorageService, @Inject(SECURE_STORAGE) secureStorageService: AbstractStorageService, - @Inject(MEMORY_STORAGE) memoryStorageService: AbstractStorageService, + @Inject(MEMORY_STORAGE) memoryStorageService: AbstractMemoryStorageService, logService: LogService, stateMigrationService: StateMigrationService, @Inject(STATE_FACTORY) stateFactory: StateFactory, diff --git a/libs/angular/src/services/injection-tokens.ts b/libs/angular/src/services/injection-tokens.ts index 7e1b9124b1a..6ae4f45ea40 100644 --- a/libs/angular/src/services/injection-tokens.ts +++ b/libs/angular/src/services/injection-tokens.ts @@ -1,10 +1,13 @@ import { InjectionToken } from "@angular/core"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { StateFactory } from "@bitwarden/common/factories/stateFactory"; export const WINDOW = new InjectionToken("WINDOW"); -export const MEMORY_STORAGE = new InjectionToken("MEMORY_STORAGE"); +export const MEMORY_STORAGE = new InjectionToken("MEMORY_STORAGE"); export const SECURE_STORAGE = new InjectionToken("SECURE_STORAGE"); export const STATE_FACTORY = new InjectionToken("STATE_FACTORY"); export const STATE_SERVICE_USE_CACHE = new InjectionToken("STATE_SERVICE_USE_CACHE"); diff --git a/libs/common/src/abstractions/storage.service.ts b/libs/common/src/abstractions/storage.service.ts index d405d526694..cdd731f9d69 100644 --- a/libs/common/src/abstractions/storage.service.ts +++ b/libs/common/src/abstractions/storage.service.ts @@ -7,10 +7,11 @@ export abstract class AbstractStorageService { abstract remove(key: string, options?: StorageOptions): Promise; } -export abstract class AbstractCachedStorageService extends AbstractStorageService { +export abstract class AbstractMemoryStorageService extends AbstractStorageService { + // Used to identify the service in the session sync decorator framework + static readonly TYPE = "MemoryStorageService"; + readonly type = AbstractMemoryStorageService.TYPE; + + abstract get(key: string, options?: MemoryStorageOptions): Promise; abstract getBypassCache(key: string, options?: MemoryStorageOptions): Promise; } - -export interface MemoryStorageServiceInterface { - get(key: string, options?: MemoryStorageOptions): Promise; -} diff --git a/libs/common/src/services/memoryStorage.service.ts b/libs/common/src/services/memoryStorage.service.ts index cfb94193e5c..b4d65c0ffa6 100644 --- a/libs/common/src/services/memoryStorage.service.ts +++ b/libs/common/src/services/memoryStorage.service.ts @@ -1,12 +1,6 @@ -import { - AbstractStorageService, - MemoryStorageServiceInterface, -} from "../abstractions/storage.service"; +import { AbstractMemoryStorageService } from "../abstractions/storage.service"; -export class MemoryStorageService - extends AbstractStorageService - implements MemoryStorageServiceInterface -{ +export class MemoryStorageService extends AbstractMemoryStorageService { private store = new Map(); get(key: string): Promise { @@ -33,4 +27,8 @@ export class MemoryStorageService this.store.delete(key); return Promise.resolve(); } + + getBypassCache(key: string): Promise { + return this.get(key); + } } diff --git a/libs/common/src/services/state.service.ts b/libs/common/src/services/state.service.ts index f55ccc39314..eeb97c6531c 100644 --- a/libs/common/src/services/state.service.ts +++ b/libs/common/src/services/state.service.ts @@ -5,7 +5,7 @@ import { LogService } from "../abstractions/log.service"; import { StateService as StateServiceAbstraction } from "../abstractions/state.service"; import { StateMigrationService } from "../abstractions/stateMigration.service"; import { - MemoryStorageServiceInterface, + AbstractMemoryStorageService, AbstractStorageService, } from "../abstractions/storage.service"; import { HtmlStorageLocation } from "../enums/htmlStorageLocation"; @@ -87,7 +87,7 @@ export class StateService< constructor( protected storageService: AbstractStorageService, protected secureStorageService: AbstractStorageService, - protected memoryStorageService: AbstractStorageService & MemoryStorageServiceInterface, + protected memoryStorageService: AbstractMemoryStorageService, protected logService: LogService, protected stateMigrationService: StateMigrationService, protected stateFactory: StateFactory,