1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-13 01:58:44 +02:00
bitwarden-browser/libs/common/spec/fake-state-provider.ts
Matt Gibson 56bffb04bb
Ps/pm 5533/migrate decrypted user key (#7970)
* Move user key memory state to state providers

Note: state service observable change is because these updates are no longer internal to the class, but reporter directly to account service through crypto service on update of a user key

* remove decrypted user key state

Note, we're going to move the encrypted cryptoSymmetric key (and associated master key encrypted user keys)  as part of the master key service creation. Crypto service will no longer be responsible for the encrypted forms of user key.

* Deprecate notices belong on abstraction

* Allow for single-direction status updates

This is necessary since we don't want to have to guarantee that the update to logged out occurs after the update to locked.

* Remove deprecated subject

It turns out the set for cryptoMasterKey was also unused 🎉
2024-02-22 15:07:26 -05:00

210 lines
7.3 KiB
TypeScript

import { mock } from "jest-mock-extended";
import { Observable, map } from "rxjs";
import {
GlobalState,
GlobalStateProvider,
KeyDefinition,
ActiveUserState,
SingleUserState,
SingleUserStateProvider,
StateProvider,
ActiveUserStateProvider,
DerivedState,
DeriveDefinition,
DerivedStateProvider,
} from "../src/platform/state";
import { UserId } from "../src/types/guid";
import { DerivedStateDependencies } from "../src/types/state";
import { FakeAccountService } from "./fake-account-service";
import {
FakeActiveUserState,
FakeDerivedState,
FakeGlobalState,
FakeSingleUserState,
} from "./fake-state";
export class FakeGlobalStateProvider implements GlobalStateProvider {
mock = mock<GlobalStateProvider>();
establishedMocks: Map<string, FakeGlobalState<unknown>> = new Map();
states: Map<string, GlobalState<unknown>> = new Map();
get<T>(keyDefinition: KeyDefinition<T>): GlobalState<T> {
this.mock.get(keyDefinition);
let result = this.states.get(keyDefinition.fullName);
if (result == null) {
let fake: FakeGlobalState<T>;
// Look for established mock
if (this.establishedMocks.has(keyDefinition.key)) {
fake = this.establishedMocks.get(keyDefinition.key) as FakeGlobalState<T>;
} else {
fake = new FakeGlobalState<T>();
}
fake.keyDefinition = keyDefinition;
result = fake;
this.states.set(keyDefinition.fullName, result);
result = new FakeGlobalState<T>();
this.states.set(keyDefinition.fullName, result);
}
return result as GlobalState<T>;
}
getFake<T>(keyDefinition: KeyDefinition<T>): FakeGlobalState<T> {
return this.get(keyDefinition) as FakeGlobalState<T>;
}
mockFor<T>(keyDefinitionKey: string, initialValue?: T): FakeGlobalState<T> {
if (!this.establishedMocks.has(keyDefinitionKey)) {
this.establishedMocks.set(keyDefinitionKey, new FakeGlobalState<T>(initialValue));
}
return this.establishedMocks.get(keyDefinitionKey) as FakeGlobalState<T>;
}
}
export class FakeSingleUserStateProvider implements SingleUserStateProvider {
mock = mock<SingleUserStateProvider>();
establishedMocks: Map<string, FakeSingleUserState<unknown>> = new Map();
states: Map<string, SingleUserState<unknown>> = new Map();
get<T>(userId: UserId, keyDefinition: KeyDefinition<T>): SingleUserState<T> {
this.mock.get(userId, keyDefinition);
let result = this.states.get(`${keyDefinition.fullName}_${userId}`);
if (result == null) {
let fake: FakeSingleUserState<T>;
// Look for established mock
if (this.establishedMocks.has(keyDefinition.key)) {
fake = this.establishedMocks.get(keyDefinition.key) as FakeSingleUserState<T>;
} else {
fake = new FakeSingleUserState<T>(userId);
}
fake.keyDefinition = keyDefinition;
result = fake;
this.states.set(`${keyDefinition.fullName}_${userId}`, result);
}
return result as SingleUserState<T>;
}
getFake<T>(userId: UserId, keyDefinition: KeyDefinition<T>): FakeSingleUserState<T> {
return this.get(userId, keyDefinition) as FakeSingleUserState<T>;
}
mockFor<T>(userId: UserId, keyDefinitionKey: string, initialValue?: T): FakeSingleUserState<T> {
if (!this.establishedMocks.has(keyDefinitionKey)) {
this.establishedMocks.set(keyDefinitionKey, new FakeSingleUserState<T>(userId, initialValue));
}
return this.establishedMocks.get(keyDefinitionKey) as FakeSingleUserState<T>;
}
}
export class FakeActiveUserStateProvider implements ActiveUserStateProvider {
activeUserId$: Observable<UserId>;
establishedMocks: Map<string, FakeActiveUserState<unknown>> = new Map();
states: Map<string, FakeActiveUserState<unknown>> = new Map();
constructor(public accountService: FakeAccountService) {
this.activeUserId$ = accountService.activeAccountSubject.asObservable().pipe(map((a) => a.id));
}
get<T>(keyDefinition: KeyDefinition<T>): ActiveUserState<T> {
let result = this.states.get(keyDefinition.fullName);
if (result == null) {
// Look for established mock
if (this.establishedMocks.has(keyDefinition.key)) {
result = this.establishedMocks.get(keyDefinition.key);
} else {
result = new FakeActiveUserState<T>(this.accountService);
}
result.keyDefinition = keyDefinition;
this.states.set(keyDefinition.fullName, result);
}
return result as ActiveUserState<T>;
}
getFake<T>(keyDefinition: KeyDefinition<T>): FakeActiveUserState<T> {
return this.get(keyDefinition) as FakeActiveUserState<T>;
}
mockFor<T>(keyDefinitionKey: string, initialValue?: T): FakeActiveUserState<T> {
if (!this.establishedMocks.has(keyDefinitionKey)) {
this.establishedMocks.set(
keyDefinitionKey,
new FakeActiveUserState<T>(this.accountService, initialValue),
);
}
return this.establishedMocks.get(keyDefinitionKey) as FakeActiveUserState<T>;
}
}
export class FakeStateProvider implements StateProvider {
mock = mock<StateProvider>();
getUserState$<T>(keyDefinition: KeyDefinition<T>, userId?: UserId): Observable<T> {
this.mock.getUserState$(keyDefinition, userId);
if (userId) {
return this.getUser<T>(userId, keyDefinition).state$;
}
return this.getActive<T>(keyDefinition).state$;
}
async setUserState<T>(
keyDefinition: KeyDefinition<T>,
value: T,
userId?: UserId,
): Promise<[UserId, T]> {
await this.mock.setUserState(keyDefinition, value, userId);
if (userId) {
return [userId, await this.getUser(userId, keyDefinition).update(() => value)];
} else {
return await this.getActive(keyDefinition).update(() => value);
}
}
getActive<T>(keyDefinition: KeyDefinition<T>): ActiveUserState<T> {
return this.activeUser.get(keyDefinition);
}
getGlobal<T>(keyDefinition: KeyDefinition<T>): GlobalState<T> {
return this.global.get(keyDefinition);
}
getUser<T>(userId: UserId, keyDefinition: KeyDefinition<T>): SingleUserState<T> {
return this.singleUser.get(userId, keyDefinition);
}
getDerived<TFrom, TTo, TDeps extends DerivedStateDependencies>(
parentState$: Observable<TFrom>,
deriveDefinition: DeriveDefinition<unknown, TTo, TDeps>,
dependencies: TDeps,
): DerivedState<TTo> {
return this.derived.get(parentState$, deriveDefinition, dependencies);
}
constructor(public accountService: FakeAccountService) {}
global: FakeGlobalStateProvider = new FakeGlobalStateProvider();
singleUser: FakeSingleUserStateProvider = new FakeSingleUserStateProvider();
activeUser: FakeActiveUserStateProvider = new FakeActiveUserStateProvider(this.accountService);
derived: FakeDerivedStateProvider = new FakeDerivedStateProvider();
activeUserId$: Observable<UserId> = this.activeUser.activeUserId$;
}
export class FakeDerivedStateProvider implements DerivedStateProvider {
states: Map<string, DerivedState<unknown>> = new Map();
get<TFrom, TTo, TDeps extends DerivedStateDependencies>(
parentState$: Observable<TFrom>,
deriveDefinition: DeriveDefinition<TFrom, TTo, TDeps>,
dependencies: TDeps,
): DerivedState<TTo> {
let result = this.states.get(deriveDefinition.buildCacheKey()) as DerivedState<TTo>;
if (result == null) {
result = new FakeDerivedState(parentState$, deriveDefinition, dependencies);
this.states.set(deriveDefinition.buildCacheKey(), result);
}
return result;
}
}