1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

Ps/pm 5537/move biometric unlock to state providers (#8099)

* Establish biometric unlock enabled in state providers

* Use biometric state service for biometric state values

* Migrate biometricUnlock

* Fixup Dependencies

* linter and import fixes

* Fix injection

* Fix merge

* Use boolean constructor as mapper

* Conform to documented test naming conventions

* Commit documentation suggestion

Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com>

* Fix merge commit

* Fix test names

---------

Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com>
This commit is contained in:
Matt Gibson
2024-03-01 09:17:06 -06:00
committed by GitHub
parent 53b547de7c
commit 5677d6265e
26 changed files with 443 additions and 79 deletions

View File

@@ -21,6 +21,7 @@ import { MoveBiometricPromptsToStateProviders } from "./migrations/23-move-biome
import { SmOnboardingTasksMigrator } from "./migrations/24-move-sm-onboarding-key-to-state-providers";
import { ClearClipboardDelayMigrator } from "./migrations/25-move-clear-clipboard-to-autofill-settings-state-provider";
import { BadgeSettingsMigrator } from "./migrations/26-move-badge-settings-to-state-providers";
import { MoveBiometricUnlockToStateProviders } from "./migrations/27-move-biometric-unlock-to-state-providers";
import { FixPremiumMigrator } from "./migrations/3-fix-premium";
import { RemoveEverBeenUnlockedMigrator } from "./migrations/4-remove-ever-been-unlocked";
import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys";
@@ -31,7 +32,7 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
import { MinVersionMigrator } from "./migrations/min-version";
export const MIN_VERSION = 2;
export const CURRENT_VERSION = 26;
export const CURRENT_VERSION = 27;
export type MinVersion = typeof MIN_VERSION;
export function createMigrationBuilder() {
@@ -60,7 +61,8 @@ export function createMigrationBuilder() {
.with(MoveBiometricPromptsToStateProviders, 22, 23)
.with(SmOnboardingTasksMigrator, 23, 24)
.with(ClearClipboardDelayMigrator, 24, 25)
.with(BadgeSettingsMigrator, 25, CURRENT_VERSION);
.with(BadgeSettingsMigrator, 25, 26)
.with(MoveBiometricUnlockToStateProviders, 26, CURRENT_VERSION);
}
export async function currentVersion(

View File

@@ -0,0 +1,120 @@
import { MockProxy, any } from "jest-mock-extended";
import { MigrationHelper } from "../migration-helper";
import { mockMigrationHelper } from "../migration-helper.spec";
import {
BIOMETRIC_UNLOCK_ENABLED,
MoveBiometricUnlockToStateProviders,
} from "./27-move-biometric-unlock-to-state-providers";
function exampleJSON() {
return {
global: {
otherStuff: "otherStuff1",
},
authenticatedAccounts: ["user-1", "user-2", "user-3"],
"user-1": {
settings: {
biometricUnlock: true,
otherStuff: "otherStuff2",
},
otherStuff: "otherStuff3",
},
"user-2": {
otherStuff: "otherStuff4",
},
};
}
function rollbackJSON() {
return {
"user_user-1_biometricSettings_biometricUnlockEnabled": true,
global: {
otherStuff: "otherStuff1",
},
authenticatedAccounts: ["user-1", "user-2", "user-3"],
"user-1": {
settings: {
otherStuff: "otherStuff2",
},
otherStuff: "otherStuff3",
},
"user-2": {
otherStuff: "otherStuff4",
},
};
}
describe("MoveBiometricPromptsToStateProviders migrator", () => {
let helper: MockProxy<MigrationHelper>;
let sut: MoveBiometricUnlockToStateProviders;
describe("migrate", () => {
beforeEach(() => {
helper = mockMigrationHelper(exampleJSON(), 26);
sut = new MoveBiometricUnlockToStateProviders(26, 27);
});
it("removes biometricUnlock from all accounts", async () => {
await sut.migrate(helper);
expect(helper.set).toHaveBeenCalledTimes(2);
expect(helper.set).toHaveBeenCalledWith("user-1", {
settings: {
otherStuff: "otherStuff2",
},
otherStuff: "otherStuff3",
});
expect(helper.set).toHaveBeenCalledWith("user-2", {
otherStuff: "otherStuff4",
});
});
it("sets biometricUnlock value for account that have it", async () => {
await sut.migrate(helper);
expect(helper.setToUser).toHaveBeenCalledWith("user-1", BIOMETRIC_UNLOCK_ENABLED, true);
});
it("should not call extra setToUser", async () => {
await sut.migrate(helper);
expect(helper.setToUser).toHaveBeenCalledTimes(1);
});
});
describe("rollback", () => {
beforeEach(() => {
helper = mockMigrationHelper(rollbackJSON(), 27);
sut = new MoveBiometricUnlockToStateProviders(26, 27);
});
it("nulls out new values", async () => {
await sut.rollback(helper);
expect(helper.setToUser).toHaveBeenCalledWith("user-1", BIOMETRIC_UNLOCK_ENABLED, null);
});
it("adds explicit value back to accounts", async () => {
await sut.rollback(helper);
expect(helper.set).toHaveBeenCalledTimes(1);
expect(helper.set).toHaveBeenCalledWith("user-1", {
settings: {
biometricUnlock: true,
otherStuff: "otherStuff2",
},
otherStuff: "otherStuff3",
});
});
it.each(["user-2", "user-3"])(
"does not restore values when accounts are not present",
async (userId) => {
await sut.rollback(helper);
expect(helper.set).not.toHaveBeenCalledWith(userId, any());
},
);
});
});

View File

@@ -0,0 +1,58 @@
import { KeyDefinitionLike, MigrationHelper } from "../migration-helper";
import { Migrator } from "../migrator";
type ExpectedAccountType = {
settings?: {
biometricUnlock?: boolean;
};
};
export const BIOMETRIC_UNLOCK_ENABLED: KeyDefinitionLike = {
key: "biometricUnlockEnabled",
stateDefinition: { name: "biometricSettings" },
};
export class MoveBiometricUnlockToStateProviders extends Migrator<26, 27> {
async migrate(helper: MigrationHelper): Promise<void> {
const legacyAccounts = await helper.getAccounts<ExpectedAccountType>();
await Promise.all(
legacyAccounts.map(async ({ userId, account }) => {
if (account == null) {
return;
}
// Move account data
if (account?.settings?.biometricUnlock != null) {
await helper.setToUser(
userId,
BIOMETRIC_UNLOCK_ENABLED,
account.settings.biometricUnlock,
);
}
// Delete old account data
delete account?.settings?.biometricUnlock;
await helper.set(userId, account);
}),
);
}
async rollback(helper: MigrationHelper): Promise<void> {
async function rollbackUser(userId: string, account: ExpectedAccountType) {
const biometricUnlock = await helper.getFromUser<boolean>(userId, BIOMETRIC_UNLOCK_ENABLED);
if (biometricUnlock != null) {
account ??= {};
account.settings ??= {};
account.settings.biometricUnlock = biometricUnlock;
await helper.setToUser(userId, BIOMETRIC_UNLOCK_ENABLED, null);
await helper.set(userId, account);
}
}
const accounts = await helper.getAccounts<ExpectedAccountType>();
await Promise.all(accounts.map(({ userId, account }) => rollbackUser(userId, account)));
}
}