1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

Revert "Use global state for biometric prompt cancel storage (#8328)" (#8351)

This reverts commit 770f782a16.
This commit is contained in:
Matt Gibson
2024-03-15 10:28:34 -05:00
committed by GitHub
parent ac7d80980d
commit 8ee1965832
8 changed files with 14 additions and 141 deletions

View File

@@ -1,7 +1,7 @@
import { firstValueFrom } from "rxjs";
import { makeEncString, trackEmissions } from "../../../spec";
import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service";
import { makeEncString } from "../../../spec";
import { mockAccountServiceWith } from "../../../spec/fake-account-service";
import { FakeSingleUserState } from "../../../spec/fake-state";
import { FakeStateProvider } from "../../../spec/fake-state-provider";
import { UserId } from "../../types/guid";
@@ -23,11 +23,10 @@ describe("BiometricStateService", () => {
const userId = "userId" as UserId;
const encClientKeyHalf = makeEncString();
const encryptedClientKeyHalf = encClientKeyHalf.encryptedString;
let accountService: FakeAccountService;
const accountService = mockAccountServiceWith(userId);
let stateProvider: FakeStateProvider;
beforeEach(() => {
accountService = mockAccountServiceWith(userId);
stateProvider = new FakeStateProvider(accountService);
sut = new DefaultBiometricStateService(stateProvider);
@@ -146,13 +145,6 @@ describe("BiometricStateService", () => {
});
describe("setPromptCancelled", () => {
let existingState: Record<UserId, boolean>;
beforeEach(() => {
existingState = { ["otherUser" as UserId]: false };
stateProvider.global.getFake(PROMPT_CANCELLED).stateSubject.next(existingState);
});
test("observable is updated", async () => {
await sut.setPromptCancelled();
@@ -162,39 +154,10 @@ describe("BiometricStateService", () => {
it("updates state", async () => {
await sut.setPromptCancelled();
const nextMock = stateProvider.global.getFake(PROMPT_CANCELLED).nextMock;
expect(nextMock).toHaveBeenCalledWith({ ...existingState, [userId]: true });
const nextMock = stateProvider.activeUser.getFake(PROMPT_CANCELLED).nextMock;
expect(nextMock).toHaveBeenCalledWith([userId, true]);
expect(nextMock).toHaveBeenCalledTimes(1);
});
it("throws when called with no active user", async () => {
await accountService.switchAccount(null);
await expect(sut.setPromptCancelled()).rejects.toThrow(
"Cannot update biometric prompt cancelled state without an active user",
);
const nextMock = stateProvider.global.getFake(PROMPT_CANCELLED).nextMock;
expect(nextMock).not.toHaveBeenCalled();
});
});
describe("resetPromptCancelled", () => {
it("deletes all prompt cancelled state", async () => {
await sut.resetPromptCancelled();
const nextMock = stateProvider.global.getFake(PROMPT_CANCELLED).nextMock;
expect(nextMock).toHaveBeenCalledWith(null);
expect(nextMock).toHaveBeenCalledTimes(1);
});
it("updates observable to false", async () => {
const emissions = trackEmissions(sut.promptCancelled$);
await sut.setPromptCancelled();
await sut.resetPromptCancelled();
expect(emissions).toEqual([false, true, false]);
});
});
describe("setPromptAutomatically", () => {

View File

@@ -1,4 +1,4 @@
import { Observable, firstValueFrom, map, combineLatest } from "rxjs";
import { Observable, firstValueFrom, map } from "rxjs";
import { UserId } from "../../types/guid";
import { EncryptedString, EncString } from "../models/domain/enc-string";
@@ -107,7 +107,7 @@ export class DefaultBiometricStateService implements BiometricStateService {
private requirePasswordOnStartState: ActiveUserState<boolean>;
private encryptedClientKeyHalfState: ActiveUserState<EncryptedString | undefined>;
private dismissedRequirePasswordOnStartCalloutState: ActiveUserState<boolean>;
private promptCancelledState: GlobalState<Record<UserId, boolean>>;
private promptCancelledState: ActiveUserState<boolean>;
private promptAutomaticallyState: ActiveUserState<boolean>;
private fingerprintValidatedState: GlobalState<boolean>;
biometricUnlockEnabled$: Observable<boolean>;
@@ -138,15 +138,8 @@ export class DefaultBiometricStateService implements BiometricStateService {
this.dismissedRequirePasswordOnStartCallout$ =
this.dismissedRequirePasswordOnStartCalloutState.state$.pipe(map(Boolean));
this.promptCancelledState = this.stateProvider.getGlobal(PROMPT_CANCELLED);
this.promptCancelled$ = combineLatest([
this.stateProvider.activeUserId$,
this.promptCancelledState.state$,
]).pipe(
map(([userId, record]) => {
return record?.[userId] ?? false;
}),
);
this.promptCancelledState = this.stateProvider.getActive(PROMPT_CANCELLED);
this.promptCancelled$ = this.promptCancelledState.state$.pipe(map(Boolean));
this.promptAutomaticallyState = this.stateProvider.getActive(PROMPT_AUTOMATICALLY);
this.promptAutomatically$ = this.promptAutomaticallyState.state$.pipe(map(Boolean));
@@ -209,15 +202,6 @@ export class DefaultBiometricStateService implements BiometricStateService {
async logout(userId: UserId): Promise<void> {
await this.stateProvider.getUser(userId, ENCRYPTED_CLIENT_KEY_HALF).update(() => null);
await this.promptCancelledState.update(
(record) => {
delete record[userId];
return record;
},
{
shouldUpdate: (record) => record[userId] == true,
},
);
await this.stateProvider.getUser(userId, PROMPT_CANCELLED).update(() => null);
// Persist auto prompt setting through logout
// Persist dismissed require password on start callout through logout
@@ -228,24 +212,7 @@ export class DefaultBiometricStateService implements BiometricStateService {
}
async setPromptCancelled(): Promise<void> {
await this.promptCancelledState.update(
(record, userId) => {
record ??= {};
record[userId] = true;
return record;
},
{
combineLatestWith: this.stateProvider.activeUserId$,
shouldUpdate: (_, userId) => {
if (userId == null) {
throw new Error(
"Cannot update biometric prompt cancelled state without an active user",
);
}
return true;
},
},
);
await this.promptCancelledState.update(() => true);
}
async resetPromptCancelled(): Promise<void> {

View File

@@ -14,7 +14,7 @@ import {
describe.each([
[ENCRYPTED_CLIENT_KEY_HALF, "encryptedClientKeyHalf"],
[DISMISSED_REQUIRE_PASSWORD_ON_START_CALLOUT, true],
[PROMPT_CANCELLED, { userId1: true, userId2: false }],
[PROMPT_CANCELLED, true],
[PROMPT_AUTOMATICALLY, true],
[REQUIRE_PASSWORD_ON_START, true],
[BIOMETRIC_UNLOCK_ENABLED, true],

View File

@@ -1,4 +1,3 @@
import { UserId } from "../../types/guid";
import { EncryptedString } from "../models/domain/enc-string";
import { KeyDefinition, BIOMETRIC_SETTINGS_DISK } from "../state";
@@ -57,7 +56,7 @@ export const DISMISSED_REQUIRE_PASSWORD_ON_START_CALLOUT = new KeyDefinition<boo
* Stores whether the user has elected to cancel the biometric prompt. This is stored on disk due to process-reload
* wiping memory state. We don't want to prompt the user again if they've elected to cancel.
*/
export const PROMPT_CANCELLED = KeyDefinition.record<boolean, UserId>(
export const PROMPT_CANCELLED = new KeyDefinition<boolean>(
BIOMETRIC_SETTINGS_DISK,
"promptCancelled",
{