[PM-5571] Migrate enableDDG to state provider framework (#8384)

Migrate enableDuckDuckGo to state provider framework.
This commit is contained in:
Oscar Hinton 2024-03-22 18:32:03 +01:00 committed by GitHub
parent 0f375c3a0e
commit 51f46e797c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 147 additions and 41 deletions

1
.github/CODEOWNERS vendored
View File

@ -83,6 +83,7 @@ apps/web/src/translation-constants.ts @bitwarden/team-platform-dev
## Autofill team files ##
apps/browser/src/autofill @bitwarden/team-autofill-dev
apps/desktop/src/autofill @bitwarden/team-autofill-dev
libs/common/src/autofill @bitwarden/team-autofill-dev
## Component Library ##

View File

@ -23,6 +23,7 @@ import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-stat
import { DialogService } from "@bitwarden/components";
import { SetPinComponent } from "../../auth/components/set-pin.component";
import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service";
import { flagEnabled } from "../../platform/flags";
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
@ -122,6 +123,7 @@ export class SettingsComponent implements OnInit {
private userVerificationService: UserVerificationServiceAbstraction,
private desktopSettingsService: DesktopSettingsService,
private biometricStateService: BiometricStateService,
private desktopAutofillSettingsService: DesktopAutofillSettingsService,
) {
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
@ -262,11 +264,12 @@ export class SettingsComponent implements OnInit {
enableBrowserIntegration: await this.stateService.getEnableBrowserIntegration(),
enableBrowserIntegrationFingerprint:
await this.stateService.getEnableBrowserIntegrationFingerprint(),
enableDuckDuckGoBrowserIntegration: await firstValueFrom(
this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$,
),
enableHardwareAcceleration: await firstValueFrom(
this.desktopSettingsService.hardwareAcceleration$,
),
enableDuckDuckGoBrowserIntegration:
await this.stateService.getEnableDuckDuckGoBrowserIntegration(),
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
locale: await firstValueFrom(this.i18nService.userSetLocale$),
};
@ -640,7 +643,7 @@ export class SettingsComponent implements OnInit {
}
async saveDdgBrowserIntegration() {
await this.stateService.setEnableDuckDuckGoBrowserIntegration(
await this.desktopAutofillSettingsService.setEnableDuckDuckGoBrowserIntegration(
this.form.value.enableDuckDuckGoBrowserIntegration,
);

View File

@ -53,6 +53,7 @@ import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vau
import { DialogService } from "@bitwarden/components";
import { LoginGuard } from "../../auth/guards/login.guard";
import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service";
import { Account } from "../../models/account";
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
import { ElectronCryptoService } from "../../platform/services/electron-crypto.service";
@ -218,6 +219,11 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
useClass: DesktopSettingsService,
deps: [StateProvider],
},
{
provide: DesktopAutofillSettingsService,
useClass: DesktopAutofillSettingsService,
deps: [StateProvider],
},
],
})
export class ServicesModule {}

View File

@ -0,0 +1,29 @@
import { map } from "rxjs";
import {
AUTOFILL_SETTINGS_DISK,
KeyDefinition,
StateProvider,
} from "@bitwarden/common/platform/state";
const ENABLE_DUCK_DUCK_GO_BROWSER_INTEGRATION = new KeyDefinition(
AUTOFILL_SETTINGS_DISK,
"enableDuckDuckGoBrowserIntegration",
{
deserializer: (v: boolean) => v,
},
);
export class DesktopAutofillSettingsService {
private enableDuckDuckGoBrowserIntegrationState = this.stateProvider.getGlobal(
ENABLE_DUCK_DUCK_GO_BROWSER_INTEGRATION,
);
readonly enableDuckDuckGoBrowserIntegration$ =
this.enableDuckDuckGoBrowserIntegrationState.state$.pipe(map((x) => x ?? false));
constructor(private stateProvider: StateProvider) {}
async setEnableDuckDuckGoBrowserIntegration(newValue: boolean): Promise<void> {
await this.enableDuckDuckGoBrowserIntegrationState.update(() => newValue);
}
}

View File

@ -26,6 +26,7 @@ import { StateEventRegistrarService } from "@bitwarden/common/platform/state/sta
import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service";
/* eslint-enable import/no-restricted-paths */
import { DesktopAutofillSettingsService } from "./autofill/services/desktop-autofill-settings.service";
import { MenuMain } from "./main/menu/menu.main";
import { MessagingMain } from "./main/messaging.main";
import { NativeMessagingMain } from "./main/native-messaging.main";
@ -71,6 +72,7 @@ export class Main {
biometricsService: BiometricsServiceAbstraction;
nativeMessagingMain: NativeMessagingMain;
clipboardMain: ClipboardMain;
desktopAutofillSettingsService: DesktopAutofillSettingsService;
constructor() {
// Set paths for portable builds
@ -233,6 +235,8 @@ export class Main {
app.getPath("exe"),
);
this.desktopAutofillSettingsService = new DesktopAutofillSettingsService(stateProvider);
this.clipboardMain = new ClipboardMain();
this.clipboardMain.init();
@ -268,10 +272,10 @@ export class Main {
if (
(await this.stateService.getEnableBrowserIntegration()) ||
(await this.stateService.getEnableDuckDuckGoBrowserIntegration())
(await firstValueFrom(
this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$,
))
) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.nativeMessagingMain.listen();
}

View File

@ -77,14 +77,10 @@ export class MessagingMain {
break;
case "enableBrowserIntegration":
this.main.nativeMessagingMain.generateManifests();
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.main.nativeMessagingMain.listen();
break;
case "enableDuckDuckGoBrowserIntegration":
this.main.nativeMessagingMain.generateDdgManifests();
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.main.nativeMessagingMain.listen();
break;
case "disableBrowserIntegration":

View File

@ -24,7 +24,7 @@ export class NativeMessagingMain {
private exePath: string,
) {}
async listen() {
listen() {
ipc.config.id = "bitwarden";
ipc.config.retry = 1500;
const ipcSocketRoot = getIpcSocketRoot();

View File

@ -12,6 +12,7 @@ import { StateService } from "@bitwarden/common/platform/services/state.service"
import { DialogService } from "@bitwarden/components";
import { VerifyNativeMessagingDialogComponent } from "../app/components/verify-native-messaging-dialog.component";
import { DesktopAutofillSettingsService } from "../autofill/services/desktop-autofill-settings.service";
import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data";
import { EncryptedMessage } from "../models/native-messaging/encrypted-message";
import { EncryptedMessageResponse } from "../models/native-messaging/encrypted-message-response";
@ -34,6 +35,7 @@ export class NativeMessageHandlerService {
private messagingService: MessagingService,
private encryptedMessageHandlerService: EncryptedMessageHandlerService,
private dialogService: DialogService,
private desktopAutofillSettingsService: DesktopAutofillSettingsService,
) {}
async handleMessage(message: Message) {
@ -71,7 +73,9 @@ export class NativeMessageHandlerService {
try {
const remotePublicKey = Utils.fromB64ToArray(publicKey);
const ddgEnabled = await this.stateService.getEnableDuckDuckGoBrowserIntegration();
const ddgEnabled = await firstValueFrom(
this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$,
);
if (!ddgEnabled) {
this.sendResponse({

View File

@ -188,11 +188,6 @@ export abstract class StateService<T extends Account = Account> {
value: boolean,
options?: StorageOptions,
) => Promise<void>;
getEnableDuckDuckGoBrowserIntegration: (options?: StorageOptions) => Promise<boolean>;
setEnableDuckDuckGoBrowserIntegration: (
value: boolean,
options?: StorageOptions,
) => Promise<void>;
getEncryptedCiphers: (options?: StorageOptions) => Promise<{ [id: string]: CipherData }>;
setEncryptedCiphers: (
value: { [id: string]: CipherData },

View File

@ -13,6 +13,5 @@ export class GlobalState {
mainWindowSize?: number;
enableBrowserIntegration?: boolean;
enableBrowserIntegrationFingerprint?: boolean;
enableDuckDuckGoBrowserIntegration?: boolean;
deepLinkRedirectUrl?: string;
}

View File

@ -867,27 +867,6 @@ export class StateService<
);
}
async getEnableDuckDuckGoBrowserIntegration(options?: StorageOptions): Promise<boolean> {
return (
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
?.enableDuckDuckGoBrowserIntegration ?? false
);
}
async setEnableDuckDuckGoBrowserIntegration(
value: boolean,
options?: StorageOptions,
): Promise<void> {
const globals = await this.getGlobals(
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
);
globals.enableDuckDuckGoBrowserIntegration = value;
await this.saveGlobals(
globals,
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
);
}
@withPrototypeForObjectValues(CipherData)
async getEncryptedCiphers(options?: StorageOptions): Promise<{ [id: string]: CipherData }> {
return (

View File

@ -43,6 +43,7 @@ import { UserDecryptionOptionsMigrator } from "./migrations/44-move-user-decrypt
import { MergeEnvironmentState } from "./migrations/45-merge-environment-state";
import { DeleteBiometricPromptCancelledData } from "./migrations/46-delete-orphaned-biometric-prompt-data";
import { MoveDesktopSettingsMigrator } from "./migrations/47-move-desktop-settings";
import { MoveDdgToStateProviderMigrator } from "./migrations/48-move-ddg-to-state-provider";
import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys";
import { RemoveLegacyEtmKeyMigrator } from "./migrations/6-remove-legacy-etm-key";
import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account";
@ -51,7 +52,8 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
import { MinVersionMigrator } from "./migrations/min-version";
export const MIN_VERSION = 3;
export const CURRENT_VERSION = 47;
export const CURRENT_VERSION = 48;
export type MinVersion = typeof MIN_VERSION;
export function createMigrationBuilder() {
@ -100,7 +102,8 @@ export function createMigrationBuilder() {
.with(UserDecryptionOptionsMigrator, 43, 44)
.with(MergeEnvironmentState, 44, 45)
.with(DeleteBiometricPromptCancelledData, 45, 46)
.with(MoveDesktopSettingsMigrator, 46, CURRENT_VERSION);
.with(MoveDesktopSettingsMigrator, 46, 47)
.with(MoveDdgToStateProviderMigrator, 47, CURRENT_VERSION);
}
export async function currentVersion(

View File

@ -0,0 +1,47 @@
import { runMigrator } from "../migration-helper.spec";
import { MoveDdgToStateProviderMigrator } from "./48-move-ddg-to-state-provider";
describe("MoveDdgToStateProviderMigrator", () => {
const migrator = new MoveDdgToStateProviderMigrator(47, 48);
it("migrate", async () => {
const output = await runMigrator(migrator, {
global: {
enableDuckDuckGoBrowserIntegration: true,
otherStuff: "otherStuff1",
},
otherStuff: "otherStuff2",
});
expect(output).toEqual({
global_autofillSettings_enableDuckDuckGoBrowserIntegration: true,
global: {
otherStuff: "otherStuff1",
},
otherStuff: "otherStuff2",
});
});
it("rollback", async () => {
const output = await runMigrator(
migrator,
{
global_autofillSettings_enableDuckDuckGoBrowserIntegration: true,
global: {
otherStuff: "otherStuff1",
},
otherStuff: "otherStuff2",
},
"rollback",
);
expect(output).toEqual({
global: {
enableDuckDuckGoBrowserIntegration: true,
otherStuff: "otherStuff1",
},
otherStuff: "otherStuff2",
});
});
});

View File

@ -0,0 +1,40 @@
import { KeyDefinitionLike, MigrationHelper } from "../migration-helper";
import { Migrator } from "../migrator";
type ExpectedGlobal = {
enableDuckDuckGoBrowserIntegration?: boolean;
};
export const DDG_KEY: KeyDefinitionLike = {
key: "enableDuckDuckGoBrowserIntegration",
stateDefinition: {
name: "autofillSettings",
},
};
export class MoveDdgToStateProviderMigrator extends Migrator<47, 48> {
async migrate(helper: MigrationHelper): Promise<void> {
// global state
const global = await helper.get<ExpectedGlobal>("global");
if (global?.enableDuckDuckGoBrowserIntegration == null) {
return;
}
await helper.setToGlobal(DDG_KEY, global.enableDuckDuckGoBrowserIntegration);
delete global.enableDuckDuckGoBrowserIntegration;
await helper.set("global", global);
}
async rollback(helper: MigrationHelper): Promise<void> {
const enableDdg = await helper.getFromGlobal<boolean>(DDG_KEY);
if (!enableDdg) {
return;
}
const global = (await helper.get<ExpectedGlobal>("global")) ?? {};
global.enableDuckDuckGoBrowserIntegration = enableDdg;
await helper.set("global", global);
await helper.removeFromGlobal(DDG_KEY);
}
}