mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 21:50:15 +00:00
Use update for to clear storage for non active users
This commit is contained in:
@@ -14,6 +14,7 @@ export class TestUserState<T> implements UserState<T> {
|
||||
|
||||
getFromState = jest.fn();
|
||||
update = jest.fn().mockImplementation(this.minimalUpdate);
|
||||
updateFor = jest.fn();
|
||||
createDerived = jest.fn();
|
||||
|
||||
next(next: T) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../types/guid";
|
||||
import { DerivedUserState } from "../services/default-user-state.provider";
|
||||
import { DerivedStateDefinition } from "../types/derived-state-definition";
|
||||
|
||||
@@ -7,11 +8,24 @@ export interface UserState<T> {
|
||||
readonly state$: Observable<T>;
|
||||
readonly getFromState: () => Promise<T>;
|
||||
/**
|
||||
*
|
||||
* Updates backing stores for the active user.
|
||||
* @param configureState function that takes the current state and returns the new state
|
||||
* @returns The new state
|
||||
*/
|
||||
readonly update: (configureState: (state: T) => T) => Promise<T>;
|
||||
/**
|
||||
* Updates backing stores for the given userId, which may or may not be active.
|
||||
* @param userId the UserId to target the update for
|
||||
* @param configureState function that takes the current state for the targeted user and returns the new state
|
||||
* @returns The new state
|
||||
*/
|
||||
readonly updateFor: (userId: UserId, configureState: (state: T) => T) => Promise<T>;
|
||||
|
||||
/**
|
||||
* Creates a derives state from the current state. Derived states are always tied to the active user.
|
||||
* @param derivedStateDefinition
|
||||
* @returns
|
||||
*/
|
||||
createDerived: <TTo>(
|
||||
derivedStateDefinition: DerivedStateDefinition<T, TTo>
|
||||
) => DerivedUserState<T, TTo>;
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { AccountService } from "../../auth/abstractions/account.service";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { EncryptService } from "../abstractions/encrypt.service";
|
||||
import {
|
||||
AbstractMemoryStorageService,
|
||||
@@ -142,6 +143,20 @@ class DefaultUserState<T> implements UserState<T> {
|
||||
return newState;
|
||||
}
|
||||
|
||||
async updateFor(userId: UserId, configureState: (state: T) => T): Promise<T> {
|
||||
if (userId == null) {
|
||||
throw new Error("Attempting to update user state, but no userId has been supplied.");
|
||||
}
|
||||
|
||||
const key = userKeyBuilder(userId, this.keyDefinition);
|
||||
const currentStore = await this.chosenStorageLocation.get<Jsonify<T>>(key);
|
||||
const currentState = this.keyDefinition.deserializer(currentStore);
|
||||
const newState = configureState(currentState);
|
||||
await this.chosenStorageLocation.save(key, newState);
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
async getFromState(): Promise<T> {
|
||||
const activeUser = await firstValueFrom(this.accountService.activeAccount$);
|
||||
if (activeUser == null || activeUser.id == null) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { TestUserState } from "../../../../spec/test-active-user-state";
|
||||
import { TestUserState } from "../../../../spec/test-user-state";
|
||||
import { CryptoService } from "../../../platform/abstractions/crypto.service";
|
||||
import { EncryptService } from "../../../platform/abstractions/encrypt.service";
|
||||
import { I18nService } from "../../../platform/abstractions/i18n.service";
|
||||
@@ -123,17 +123,7 @@ describe("Folder Service", () => {
|
||||
test("replace", async () => {
|
||||
await folderService.replace({ "2": folderData("2", "test 2") });
|
||||
|
||||
expect(await firstValueFrom(folderService.folders$)).toEqual([
|
||||
{
|
||||
id: "2",
|
||||
name: {
|
||||
decryptedValue: [],
|
||||
encryptedString: "test 2",
|
||||
encryptionType: 0,
|
||||
},
|
||||
revisionDate: null,
|
||||
},
|
||||
]);
|
||||
expect(await firstValueFrom(folderService.folders$)).toEqual([folder("2", "test 2")]);
|
||||
});
|
||||
|
||||
test("delete", async () => {
|
||||
@@ -159,29 +149,24 @@ describe("Folder Service", () => {
|
||||
it("null userId", async () => {
|
||||
await folderService.clear();
|
||||
|
||||
stateService.received(1).setEncryptedFolders(Arg.any(), Arg.any());
|
||||
|
||||
expect((await firstValueFrom(folderService.folders$)).length).toBe(0);
|
||||
expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0);
|
||||
expect(userState.update).toHaveBeenCalled();
|
||||
expect(await firstValueFrom(folderService.folders$)).toEqual(expect.arrayContaining([]));
|
||||
});
|
||||
|
||||
it("matching userId", async () => {
|
||||
stateService.getUserId().resolves("1");
|
||||
it("active userId", async () => {
|
||||
await folderService.clear("1");
|
||||
|
||||
stateService.received(1).setEncryptedFolders(Arg.any(), Arg.any());
|
||||
|
||||
expect((await firstValueFrom(folderService.folders$)).length).toBe(0);
|
||||
expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0);
|
||||
expect(userState.updateFor).toHaveBeenCalled();
|
||||
const updateCallback = userState.updateFor.mock.calls[0][1];
|
||||
expect(updateCallback({ "2": folderData("2", "test") })).toEqual(expect.objectContaining({}));
|
||||
});
|
||||
|
||||
it("missmatching userId", async () => {
|
||||
it("inactive userId", async () => {
|
||||
await folderService.clear("12");
|
||||
|
||||
stateService.received(1).setEncryptedFolders(Arg.any(), Arg.any());
|
||||
|
||||
expect((await firstValueFrom(folderService.folders$)).length).toBe(1);
|
||||
expect((await firstValueFrom(folderService.folderViews$)).length).toBe(2);
|
||||
expect(userState.updateFor).toHaveBeenCalled();
|
||||
const updateCallback = userState.updateFor.mock.calls[0][1];
|
||||
expect(updateCallback({ "2": folderData("2", "test") })).toEqual(expect.objectContaining({}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { UserState } from "../../../platform/interfaces/user-state";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { DerivedUserState } from "../../../platform/services/default-user-state.provider";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { CipherService } from "../../../vault/abstractions/cipher.service";
|
||||
import { InternalFolderService as InternalFolderServiceAbstraction } from "../../../vault/abstractions/folder/folder.service.abstraction";
|
||||
import { CipherData } from "../../../vault/models/data/cipher.data";
|
||||
@@ -115,8 +116,11 @@ export class FolderService implements InternalFolderServiceAbstraction {
|
||||
}
|
||||
|
||||
async clear(userId?: string): Promise<any> {
|
||||
// TODO: handle clear for non-active users
|
||||
await this.folderState.update((_) => null);
|
||||
if (userId == null) {
|
||||
await this.folderState.update((_) => ({}));
|
||||
} else {
|
||||
await this.folderState.updateFor(userId as UserId, (_) => ({}));
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id: string | string[]): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user