mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 22:33:35 +00:00
[PM-18046] Implement session storage (#17346)
* feat: add support for IPC client managed session storage * feat: update SDK * fix: using undecorated service in jslib module directly * feat: add test case for web * chore: document why we use any type * fix: `ipc` too short * typo: omg * Revert "typo: omg" This reverts commit559b05eb5a. * Revert "fix: `ipc` too short" This reverts commit35fc99e10b. * fix: use camelCase
This commit is contained in:
@@ -228,6 +228,7 @@ import { SystemService } from "@bitwarden/common/platform/abstractions/system.se
|
||||
import { ValidationService as ValidationServiceAbstraction } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { ActionsService } from "@bitwarden/common/platform/actions";
|
||||
import { UnsupportedActionsService } from "@bitwarden/common/platform/actions/unsupported-actions.service";
|
||||
import { IpcSessionRepository } from "@bitwarden/common/platform/ipc";
|
||||
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- Used for dependency injection
|
||||
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
|
||||
@@ -1750,6 +1751,11 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: DefaultNewDeviceVerificationComponentService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: IpcSessionRepository,
|
||||
useClass: IpcSessionRepository,
|
||||
deps: [StateProvider],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: PremiumInterestStateService,
|
||||
useClass: NoopPremiumInterestStateService,
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./ipc-message";
|
||||
export * from "./ipc.service";
|
||||
export * from "./ipc-session-repository";
|
||||
|
||||
49
libs/common/src/platform/ipc/ipc-session-repository.spec.ts
Normal file
49
libs/common/src/platform/ipc/ipc-session-repository.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { FakeActiveUserAccessor, FakeStateProvider } from "../../../spec";
|
||||
import { UserId } from "../../types/guid";
|
||||
|
||||
import { IpcSessionRepository } from "./ipc-session-repository";
|
||||
|
||||
describe("IpcSessionRepository", () => {
|
||||
const userId = "user-id" as UserId;
|
||||
let stateProvider!: FakeStateProvider;
|
||||
let repository!: IpcSessionRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
stateProvider = new FakeStateProvider(new FakeActiveUserAccessor(userId));
|
||||
repository = new IpcSessionRepository(stateProvider);
|
||||
});
|
||||
|
||||
it("returns undefined when empty", async () => {
|
||||
const result = await repository.get("BrowserBackground");
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("saves and retrieves a session", async () => {
|
||||
const session = { some: "data" };
|
||||
await repository.save("BrowserBackground", session);
|
||||
|
||||
const result = await repository.get("BrowserBackground");
|
||||
|
||||
expect(result).toEqual(session);
|
||||
});
|
||||
|
||||
it("saves and retrieves a web session", async () => {
|
||||
const session = { some: "data" };
|
||||
await repository.save({ Web: { id: 9001 } }, session);
|
||||
|
||||
const result = await repository.get({ Web: { id: 9001 } });
|
||||
|
||||
expect(result).toEqual(session);
|
||||
});
|
||||
|
||||
it("removes a session", async () => {
|
||||
const session = { some: "data" };
|
||||
await repository.save("BrowserBackground", session);
|
||||
|
||||
await repository.remove("BrowserBackground");
|
||||
const result = await repository.get("BrowserBackground");
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
51
libs/common/src/platform/ipc/ipc-session-repository.ts
Normal file
51
libs/common/src/platform/ipc/ipc-session-repository.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { Endpoint, IpcSessionRepository as SdkIpcSessionRepository } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { GlobalState, IPC_MEMORY, KeyDefinition, StateProvider } from "../state";
|
||||
|
||||
const IPC_SESSIONS = KeyDefinition.record<object, string>(IPC_MEMORY, "ipcSessions", {
|
||||
deserializer: (value: object) => value,
|
||||
});
|
||||
|
||||
/**
|
||||
* Implementation of SDK-defined repository interface/trait. Do not use directly.
|
||||
* All error handling is done by the caller (the SDK).
|
||||
* For more information see IPC docs.
|
||||
*
|
||||
* Interface uses `any` type as defined by the SDK until we get a concrete session type.
|
||||
*/
|
||||
export class IpcSessionRepository implements SdkIpcSessionRepository {
|
||||
private states: GlobalState<Record<string, any>>;
|
||||
|
||||
constructor(private stateProvider: StateProvider) {
|
||||
this.states = this.stateProvider.getGlobal(IPC_SESSIONS);
|
||||
}
|
||||
|
||||
get(endpoint: Endpoint): Promise<any | undefined> {
|
||||
return firstValueFrom(this.states.state$.pipe(map((s) => s?.[endpointToString(endpoint)])));
|
||||
}
|
||||
|
||||
async save(endpoint: Endpoint, session: any): Promise<void> {
|
||||
await this.states.update((s) => ({
|
||||
...s,
|
||||
[endpointToString(endpoint)]: session,
|
||||
}));
|
||||
}
|
||||
|
||||
async remove(endpoint: Endpoint): Promise<void> {
|
||||
await this.states.update((s) => {
|
||||
const newState = { ...s };
|
||||
delete newState[endpointToString(endpoint)];
|
||||
return newState;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function endpointToString(endpoint: Endpoint): string {
|
||||
if (typeof endpoint === "object" && "Web" in endpoint) {
|
||||
return `Web(${endpoint.Web.id})`;
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
@@ -127,6 +127,7 @@ export const CRYPTO_MEMORY = new StateDefinition("crypto", "memory");
|
||||
export const DESKTOP_SETTINGS_DISK = new StateDefinition("desktopSettings", "disk");
|
||||
export const ENVIRONMENT_DISK = new StateDefinition("environment", "disk");
|
||||
export const ENVIRONMENT_MEMORY = new StateDefinition("environment", "memory");
|
||||
export const IPC_MEMORY = new StateDefinition("interProcessCommunication", "memory");
|
||||
export const POPUP_VIEW_MEMORY = new StateDefinition("popupView", "memory", {
|
||||
browser: "memory-large-object",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user