mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 00:33:44 +00:00
* Add New KeyDefinitionOption * Add New Services * Add WebStorageServiceProvider Tests * Update Error Message * Add `UserKeyDefinition` * Fix Deserialization Helpers * Fix KeyDefinition * Add `UserKeyDefinition` * Fix Deserialization Helpers * Fix KeyDefinition * Move `ClearEvent` * Cleanup * Fix Imports * Remove `updateMock` * Call Super in Web Implementation * Use Better Type to Avoid Casting * Better Error Docs * Move StorageKey Creation to Function * Throw Aggregated Error for Failures
81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import { firstValueFrom } from "rxjs";
|
|
|
|
import { UserId } from "../../types/guid";
|
|
import { StorageServiceProvider } from "../services/storage-service.provider";
|
|
|
|
import { GlobalState } from "./global-state";
|
|
import { GlobalStateProvider } from "./global-state.provider";
|
|
import { StateDefinition, StorageLocation } from "./state-definition";
|
|
import {
|
|
STATE_LOCK_EVENT,
|
|
STATE_LOGOUT_EVENT,
|
|
StateEventInfo,
|
|
} from "./state-event-registrar.service";
|
|
import { ClearEvent, UserKeyDefinition } from "./user-key-definition";
|
|
|
|
export class StateEventRunnerService {
|
|
private readonly stateEventMap: { [Prop in ClearEvent]: GlobalState<StateEventInfo[]> };
|
|
|
|
constructor(
|
|
globalStateProvider: GlobalStateProvider,
|
|
private storageServiceProvider: StorageServiceProvider,
|
|
) {
|
|
this.stateEventMap = {
|
|
lock: globalStateProvider.get(STATE_LOCK_EVENT),
|
|
logout: globalStateProvider.get(STATE_LOGOUT_EVENT),
|
|
};
|
|
}
|
|
|
|
async handleEvent(event: ClearEvent, userId: UserId) {
|
|
let tickets = await firstValueFrom(this.stateEventMap[event].state$);
|
|
tickets ??= [];
|
|
|
|
const failures: string[] = [];
|
|
|
|
for (const ticket of tickets) {
|
|
try {
|
|
const [, service] = this.storageServiceProvider.get(
|
|
ticket.location,
|
|
{}, // The storage location is already the computed storage location for this client
|
|
);
|
|
|
|
const ticketStorageKey = this.storageKeyFor(userId, ticket);
|
|
|
|
// Evaluate current value so we can avoid writing to state if we don't need to
|
|
const currentValue = await service.get(ticketStorageKey);
|
|
if (currentValue != null) {
|
|
await service.remove(ticketStorageKey);
|
|
}
|
|
} catch (err: unknown) {
|
|
let errorMessage = "Unknown Error";
|
|
if (typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
errorMessage = err.message;
|
|
}
|
|
|
|
failures.push(
|
|
`${errorMessage} in ${ticket.state} > ${ticket.key} located ${ticket.location}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (failures.length > 0) {
|
|
// Throw aggregated error
|
|
throw new Error(
|
|
`One or more errors occurred while handling event '${event}' for user ${userId}.\n${failures.join("\n")}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
private storageKeyFor(userId: UserId, ticket: StateEventInfo) {
|
|
const userKey = new UserKeyDefinition<unknown>(
|
|
new StateDefinition(ticket.state, ticket.location as unknown as StorageLocation),
|
|
ticket.key,
|
|
{
|
|
deserializer: (v) => v,
|
|
clearOn: [],
|
|
},
|
|
);
|
|
return userKey.buildKey(userId);
|
|
}
|
|
}
|