mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 23:03:32 +00:00
[PM-7541] Move Last Desktop Settings (#9310)
* Clone Initial Data In `runMigrator` - When using test cases, mutating the input data causes problems. * Migrate `minimizeOnCopy` & `browserIntegrationEnabled` * Update From Main * Move Fingerprint Setting - No Migration Yet * Add Fingerprint to Migrations * Convert Messaging to `async` * Switch to calling `Boolean` for Map Function * Catch Errors * Remove LogService
This commit is contained in:
@@ -82,13 +82,6 @@ export abstract class StateService<T extends Account = Account> {
|
||||
) => Promise<void>;
|
||||
getDuckDuckGoSharedKey: (options?: StorageOptions) => Promise<string>;
|
||||
setDuckDuckGoSharedKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBrowserIntegration: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableBrowserIntegration: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBrowserIntegrationFingerprint: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableBrowserIntegrationFingerprint: (
|
||||
value: boolean,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getEncryptedPasswordGenerationHistory: (
|
||||
options?: StorageOptions,
|
||||
) => Promise<GeneratedPasswordHistory[]>;
|
||||
@@ -99,8 +92,6 @@ export abstract class StateService<T extends Account = Account> {
|
||||
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
|
||||
getLastSync: (options?: StorageOptions) => Promise<string>;
|
||||
setLastSync: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise<boolean>;
|
||||
setMinimizeOnCopyToClipboard: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getPasswordGenerationOptions: (options?: StorageOptions) => Promise<PasswordGeneratorOptions>;
|
||||
setPasswordGenerationOptions: (
|
||||
value: PasswordGeneratorOptions,
|
||||
|
||||
@@ -143,7 +143,6 @@ export class AccountProfile {
|
||||
|
||||
export class AccountSettings {
|
||||
defaultUriMatch?: UriMatchStrategySetting;
|
||||
minimizeOnCopyToClipboard?: boolean;
|
||||
passwordGenerationOptions?: PasswordGeneratorOptions;
|
||||
usernameGenerationOptions?: UsernameGeneratorOptions;
|
||||
generatorOptions?: GeneratorOptions;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export class GlobalState {
|
||||
enableBrowserIntegration?: boolean;
|
||||
enableBrowserIntegrationFingerprint?: boolean;
|
||||
enableDuckDuckGoBrowserIntegration?: boolean;
|
||||
}
|
||||
|
||||
@@ -347,45 +347,6 @@ export class StateService<
|
||||
: await this.secureStorageService.save(DDG_SHARED_KEY, value, options);
|
||||
}
|
||||
|
||||
async getEnableBrowserIntegration(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||
?.enableBrowserIntegration ?? false
|
||||
);
|
||||
}
|
||||
|
||||
async setEnableBrowserIntegration(value: boolean, options?: StorageOptions): Promise<void> {
|
||||
const globals = await this.getGlobals(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
globals.enableBrowserIntegration = value;
|
||||
await this.saveGlobals(
|
||||
globals,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getEnableBrowserIntegrationFingerprint(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||
?.enableBrowserIntegrationFingerprint ?? false
|
||||
);
|
||||
}
|
||||
|
||||
async setEnableBrowserIntegrationFingerprint(
|
||||
value: boolean,
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const globals = await this.getGlobals(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
globals.enableBrowserIntegrationFingerprint = value;
|
||||
await this.saveGlobals(
|
||||
globals,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async setEnableDuckDuckGoBrowserIntegration(
|
||||
value: boolean,
|
||||
options?: StorageOptions,
|
||||
@@ -456,24 +417,6 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
async getMinimizeOnCopyToClipboard(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||
?.settings?.minimizeOnCopyToClipboard ?? false
|
||||
);
|
||||
}
|
||||
|
||||
async setMinimizeOnCopyToClipboard(value: boolean, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
account.settings.minimizeOnCopyToClipboard = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getPasswordGenerationOptions(options?: StorageOptions): Promise<PasswordGeneratorOptions> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||
|
||||
@@ -63,13 +63,14 @@ import { VaultTimeoutSettingsServiceStateProviderMigrator } from "./migrations/6
|
||||
import { PasswordOptionsMigrator } from "./migrations/63-migrate-password-settings";
|
||||
import { GeneratorHistoryMigrator } from "./migrations/64-migrate-generator-history";
|
||||
import { ForwarderOptionsMigrator } from "./migrations/65-migrate-forwarder-settings";
|
||||
import { MoveFinalDesktopSettingsMigrator } from "./migrations/66-move-final-desktop-settings";
|
||||
import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account";
|
||||
import { MoveStateVersionMigrator } from "./migrations/8-move-state-version";
|
||||
import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-settings-to-global";
|
||||
import { MinVersionMigrator } from "./migrations/min-version";
|
||||
|
||||
export const MIN_VERSION = 3;
|
||||
export const CURRENT_VERSION = 65;
|
||||
export const CURRENT_VERSION = 66;
|
||||
export type MinVersion = typeof MIN_VERSION;
|
||||
|
||||
export function createMigrationBuilder() {
|
||||
@@ -136,7 +137,8 @@ export function createMigrationBuilder() {
|
||||
.with(VaultTimeoutSettingsServiceStateProviderMigrator, 61, 62)
|
||||
.with(PasswordOptionsMigrator, 62, 63)
|
||||
.with(GeneratorHistoryMigrator, 63, 64)
|
||||
.with(ForwarderOptionsMigrator, 64, CURRENT_VERSION);
|
||||
.with(ForwarderOptionsMigrator, 64, 65)
|
||||
.with(MoveFinalDesktopSettingsMigrator, 65, CURRENT_VERSION);
|
||||
}
|
||||
|
||||
export async function currentVersion(
|
||||
|
||||
@@ -356,10 +356,12 @@ export async function runMigrator<
|
||||
initalData?: InitialDataHint<TUsers>,
|
||||
direction: "migrate" | "rollback" = "migrate",
|
||||
): Promise<Record<string, unknown>> {
|
||||
// Inject fake data at every level of the object
|
||||
const allInjectedData = injectData(initalData, []);
|
||||
const clonedData = JSON.parse(JSON.stringify(initalData ?? {}));
|
||||
|
||||
const fakeStorageService = new FakeStorageService(initalData);
|
||||
// Inject fake data at every level of the object
|
||||
const allInjectedData = injectData(clonedData, []);
|
||||
|
||||
const fakeStorageService = new FakeStorageService(clonedData);
|
||||
const helper = new MigrationHelper(
|
||||
migrator.fromVersion,
|
||||
fakeStorageService,
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import { runMigrator } from "../migration-helper.spec";
|
||||
|
||||
import { MoveFinalDesktopSettingsMigrator } from "./66-move-final-desktop-settings";
|
||||
|
||||
describe("MoveDesktopSettings", () => {
|
||||
const sut = new MoveFinalDesktopSettingsMigrator(65, 66);
|
||||
|
||||
const cases: {
|
||||
it: string;
|
||||
preMigration: Record<string, unknown>;
|
||||
postMigration: Record<string, unknown>;
|
||||
}[] = [
|
||||
{
|
||||
it: "moves truthy values",
|
||||
preMigration: {
|
||||
global_account_accounts: {
|
||||
user1: {},
|
||||
otherUser: {},
|
||||
},
|
||||
user1: {
|
||||
settings: {
|
||||
minimizeOnCopyToClipboard: true,
|
||||
},
|
||||
},
|
||||
otherUser: {
|
||||
settings: {
|
||||
random: "stuff",
|
||||
},
|
||||
},
|
||||
global: {
|
||||
enableBrowserIntegration: true,
|
||||
enableBrowserIntegrationFingerprint: true,
|
||||
},
|
||||
},
|
||||
postMigration: {
|
||||
global_account_accounts: {
|
||||
user1: {},
|
||||
otherUser: {},
|
||||
},
|
||||
global: {},
|
||||
user1: {
|
||||
settings: {},
|
||||
},
|
||||
otherUser: {
|
||||
settings: {
|
||||
random: "stuff",
|
||||
},
|
||||
},
|
||||
global_desktopSettings_browserIntegrationEnabled: true,
|
||||
user_user1_desktopSettings_minimizeOnCopy: true,
|
||||
global_desktopSettings_browserIntegrationFingerprintEnabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
it: "moves falsey values",
|
||||
preMigration: {
|
||||
global_account_accounts: {
|
||||
user1: {},
|
||||
otherUser: {},
|
||||
},
|
||||
user1: {
|
||||
settings: {
|
||||
minimizeOnCopyToClipboard: false,
|
||||
},
|
||||
},
|
||||
otherUser: {
|
||||
settings: {
|
||||
random: "stuff",
|
||||
},
|
||||
},
|
||||
global: {
|
||||
enableBrowserIntegration: false,
|
||||
enableBrowserIntegrationFingerprint: false,
|
||||
},
|
||||
},
|
||||
postMigration: {
|
||||
global_account_accounts: {
|
||||
user1: {},
|
||||
otherUser: {},
|
||||
},
|
||||
global: {},
|
||||
user1: {
|
||||
settings: {},
|
||||
},
|
||||
otherUser: {
|
||||
settings: {
|
||||
random: "stuff",
|
||||
},
|
||||
},
|
||||
global_desktopSettings_browserIntegrationEnabled: false,
|
||||
user_user1_desktopSettings_minimizeOnCopy: false,
|
||||
global_desktopSettings_browserIntegrationFingerprintEnabled: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
it: "does not move non-existant values",
|
||||
preMigration: {
|
||||
global_account_accounts: {
|
||||
user1: {},
|
||||
otherUser: {},
|
||||
},
|
||||
user1: {
|
||||
settings: {},
|
||||
},
|
||||
otherUser: {
|
||||
settings: {
|
||||
random: "stuff",
|
||||
},
|
||||
},
|
||||
global: {},
|
||||
},
|
||||
postMigration: {
|
||||
global_account_accounts: {
|
||||
user1: {},
|
||||
otherUser: {},
|
||||
},
|
||||
global: {},
|
||||
user1: {
|
||||
settings: {},
|
||||
},
|
||||
otherUser: {
|
||||
settings: {
|
||||
random: "stuff",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe("migrate", () => {
|
||||
it.each(cases)("$it", async ({ preMigration, postMigration }) => {
|
||||
const actualOutput = await runMigrator(sut, preMigration, "migrate");
|
||||
expect(actualOutput).toEqual(postMigration);
|
||||
});
|
||||
});
|
||||
|
||||
describe("rollback", () => {
|
||||
it.each(cases)("$it", async ({ postMigration, preMigration }) => {
|
||||
const actualOutput = await runMigrator(sut, postMigration, "rollback");
|
||||
expect(actualOutput).toEqual(preMigration);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
import { KeyDefinitionLike, MigrationHelper, StateDefinitionLike } from "../migration-helper";
|
||||
import { Migrator } from "../migrator";
|
||||
|
||||
type ExpectedGlobal = {
|
||||
enableBrowserIntegration?: boolean;
|
||||
enableBrowserIntegrationFingerprint?: boolean;
|
||||
};
|
||||
|
||||
type ExpectedAccount = {
|
||||
settings?: {
|
||||
minimizeOnCopyToClipboard?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const DESKTOP_SETTINGS_DISK: StateDefinitionLike = {
|
||||
name: "desktopSettings",
|
||||
};
|
||||
|
||||
const BROWSER_INTEGRATION_ENABLED: KeyDefinitionLike = {
|
||||
key: "browserIntegrationEnabled",
|
||||
stateDefinition: DESKTOP_SETTINGS_DISK,
|
||||
};
|
||||
|
||||
const BROWSER_INTEGRATION_FINGERPRINT_ENABLED: KeyDefinitionLike = {
|
||||
key: "browserIntegrationFingerprintEnabled",
|
||||
stateDefinition: DESKTOP_SETTINGS_DISK,
|
||||
};
|
||||
|
||||
const MINIMIZE_ON_COPY: KeyDefinitionLike = {
|
||||
key: "minimizeOnCopy",
|
||||
stateDefinition: DESKTOP_SETTINGS_DISK,
|
||||
};
|
||||
|
||||
export class MoveFinalDesktopSettingsMigrator extends Migrator<65, 66> {
|
||||
async migrate(helper: MigrationHelper): Promise<void> {
|
||||
const legacyGlobal = await helper.get<ExpectedGlobal>("global");
|
||||
const enableBrowserIntegrationValue = legacyGlobal?.enableBrowserIntegration;
|
||||
const enableBrowserIntegrationFingerprintValue =
|
||||
legacyGlobal?.enableBrowserIntegrationFingerprint;
|
||||
|
||||
let updatedGlobal = false;
|
||||
|
||||
if (enableBrowserIntegrationValue != null) {
|
||||
await helper.setToGlobal(BROWSER_INTEGRATION_ENABLED, enableBrowserIntegrationValue);
|
||||
delete legacyGlobal.enableBrowserIntegration;
|
||||
updatedGlobal = true;
|
||||
}
|
||||
|
||||
if (enableBrowserIntegrationValue != null) {
|
||||
await helper.setToGlobal(
|
||||
BROWSER_INTEGRATION_FINGERPRINT_ENABLED,
|
||||
enableBrowserIntegrationFingerprintValue,
|
||||
);
|
||||
delete legacyGlobal.enableBrowserIntegrationFingerprint;
|
||||
updatedGlobal = true;
|
||||
}
|
||||
|
||||
if (updatedGlobal) {
|
||||
await helper.set("global", legacyGlobal);
|
||||
}
|
||||
|
||||
async function migrateAccount(userId: string, account: ExpectedAccount) {
|
||||
const minimizeOnCopyToClipboardValue = account?.settings?.minimizeOnCopyToClipboard;
|
||||
|
||||
if (minimizeOnCopyToClipboardValue != null) {
|
||||
await helper.setToUser(userId, MINIMIZE_ON_COPY, minimizeOnCopyToClipboardValue);
|
||||
delete account.settings.minimizeOnCopyToClipboard;
|
||||
await helper.set(userId, account);
|
||||
}
|
||||
}
|
||||
|
||||
const accounts = await helper.getAccounts<ExpectedAccount>();
|
||||
|
||||
await Promise.all(accounts.map(({ userId, account }) => migrateAccount(userId, account)));
|
||||
}
|
||||
|
||||
async rollback(helper: MigrationHelper): Promise<void> {
|
||||
const browserIntegrationEnabledValue = await helper.getFromGlobal<boolean>(
|
||||
BROWSER_INTEGRATION_ENABLED,
|
||||
);
|
||||
|
||||
const browserIntegrationFingerprintEnabled = await helper.getFromGlobal<boolean>(
|
||||
BROWSER_INTEGRATION_FINGERPRINT_ENABLED,
|
||||
);
|
||||
|
||||
if (browserIntegrationEnabledValue != null) {
|
||||
let legacyGlobal = await helper.get<ExpectedGlobal>("global");
|
||||
legacyGlobal ??= {};
|
||||
legacyGlobal.enableBrowserIntegration = browserIntegrationEnabledValue;
|
||||
await helper.set("global", legacyGlobal);
|
||||
await helper.removeFromGlobal(BROWSER_INTEGRATION_ENABLED);
|
||||
}
|
||||
|
||||
if (browserIntegrationFingerprintEnabled != null) {
|
||||
let legacyGlobal = await helper.get<ExpectedGlobal>("global");
|
||||
legacyGlobal ??= {};
|
||||
legacyGlobal.enableBrowserIntegrationFingerprint = browserIntegrationFingerprintEnabled;
|
||||
await helper.set("global", legacyGlobal);
|
||||
await helper.removeFromGlobal(BROWSER_INTEGRATION_FINGERPRINT_ENABLED);
|
||||
}
|
||||
|
||||
async function rollbackAccount(userId: string, account: ExpectedAccount) {
|
||||
const minimizeOnCopyToClipboardValue = await helper.getFromUser<boolean>(
|
||||
userId,
|
||||
MINIMIZE_ON_COPY,
|
||||
);
|
||||
|
||||
if (minimizeOnCopyToClipboardValue != null) {
|
||||
account ??= { settings: {} };
|
||||
account.settings.minimizeOnCopyToClipboard = minimizeOnCopyToClipboardValue;
|
||||
await helper.set(userId, account);
|
||||
await helper.removeFromUser(userId, MINIMIZE_ON_COPY);
|
||||
}
|
||||
}
|
||||
|
||||
const accounts = await helper.getAccounts();
|
||||
await Promise.all(accounts.map(({ userId, account }) => rollbackAccount(userId, account)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user