[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:
parent
79968c2d32
commit
ba3d21094e
|
@ -278,7 +278,7 @@ export class SettingsComponent implements OnInit {
|
|||
approveLoginRequests:
|
||||
(await this.authRequestService.getAcceptAuthRequests(this.currentUserId)) ?? false,
|
||||
clearClipboard: await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$),
|
||||
minimizeOnCopyToClipboard: await this.stateService.getMinimizeOnCopyToClipboard(),
|
||||
minimizeOnCopyToClipboard: await firstValueFrom(this.desktopSettingsService.minimizeOnCopy$),
|
||||
enableFavicons: await firstValueFrom(this.domainSettingsService.showFavicons$),
|
||||
enableTray: await firstValueFrom(this.desktopSettingsService.trayEnabled$),
|
||||
enableMinToTray: await firstValueFrom(this.desktopSettingsService.minimizeToTray$),
|
||||
|
@ -286,9 +286,12 @@ export class SettingsComponent implements OnInit {
|
|||
startToTray: await firstValueFrom(this.desktopSettingsService.startToTray$),
|
||||
openAtLogin: await firstValueFrom(this.desktopSettingsService.openAtLogin$),
|
||||
alwaysShowDock: await firstValueFrom(this.desktopSettingsService.alwaysShowDock$),
|
||||
enableBrowserIntegration: await this.stateService.getEnableBrowserIntegration(),
|
||||
enableBrowserIntegrationFingerprint:
|
||||
await this.stateService.getEnableBrowserIntegrationFingerprint(),
|
||||
enableBrowserIntegration: await firstValueFrom(
|
||||
this.desktopSettingsService.browserIntegrationEnabled$,
|
||||
),
|
||||
enableBrowserIntegrationFingerprint: await firstValueFrom(
|
||||
this.desktopSettingsService.browserIntegrationFingerprintEnabled$,
|
||||
),
|
||||
enableDuckDuckGoBrowserIntegration: await firstValueFrom(
|
||||
this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$,
|
||||
),
|
||||
|
@ -598,7 +601,10 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
|
||||
async saveMinOnCopyToClipboard() {
|
||||
await this.stateService.setMinimizeOnCopyToClipboard(this.form.value.minimizeOnCopyToClipboard);
|
||||
await this.desktopSettingsService.setMinimizeOnCopy(
|
||||
this.form.value.minimizeOnCopyToClipboard,
|
||||
this.currentUserId,
|
||||
);
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
|
@ -656,7 +662,9 @@ export class SettingsComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
await this.stateService.setEnableBrowserIntegration(this.form.value.enableBrowserIntegration);
|
||||
await this.desktopSettingsService.setBrowserIntegrationEnabled(
|
||||
this.form.value.enableBrowserIntegration,
|
||||
);
|
||||
|
||||
const errorResult = await this.nativeMessagingManifestService.generate(
|
||||
this.form.value.enableBrowserIntegration,
|
||||
|
@ -703,7 +711,7 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
|
||||
async saveBrowserIntegrationFingerprint() {
|
||||
await this.stateService.setEnableBrowserIntegrationFingerprint(
|
||||
await this.desktopSettingsService.setBrowserIntegrationFingerprintEnabled(
|
||||
this.form.value.enableBrowserIntegrationFingerprint,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,23 +3,13 @@ import * as path from "path";
|
|||
import { app } from "electron";
|
||||
import { Subject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { DefaultBiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { Message, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- For dependency creation
|
||||
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
|
||||
import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service";
|
||||
import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service";
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
|
||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||
|
@ -32,7 +22,6 @@ import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state
|
|||
import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider";
|
||||
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
|
||||
import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
/* eslint-enable import/no-restricted-paths */
|
||||
|
||||
import { DesktopAutofillSettingsService } from "./autofill/services/desktop-autofill-settings.service";
|
||||
|
@ -43,18 +32,13 @@ import { PowerMonitorMain } from "./main/power-monitor.main";
|
|||
import { TrayMain } from "./main/tray.main";
|
||||
import { UpdaterMain } from "./main/updater.main";
|
||||
import { WindowMain } from "./main/window.main";
|
||||
import { Account } from "./models/account";
|
||||
import { BiometricsService, BiometricsServiceAbstraction } from "./platform/main/biometric/index";
|
||||
import { ClipboardMain } from "./platform/main/clipboard.main";
|
||||
import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener";
|
||||
import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service";
|
||||
import { DesktopSettingsService } from "./platform/services/desktop-settings.service";
|
||||
import { ElectronLogMainService } from "./platform/services/electron-log.main.service";
|
||||
import { ELECTRON_SUPPORTS_SECURE_STORAGE } from "./platform/services/electron-platform-utils.service";
|
||||
import { ElectronStateService } from "./platform/services/electron-state.service";
|
||||
import { ElectronStorageService } from "./platform/services/electron-storage.service";
|
||||
import { I18nMainService } from "./platform/services/i18n.main.service";
|
||||
import { IllegalSecureStorageService } from "./platform/services/illegal-secure-storage.service";
|
||||
import { ElectronMainMessagingService } from "./services/electron-main-messaging.service";
|
||||
import { isMacAppStore } from "./utils";
|
||||
|
||||
|
@ -65,15 +49,10 @@ export class Main {
|
|||
memoryStorageService: MemoryStorageService;
|
||||
memoryStorageForStateProviders: MemoryStorageServiceForStateProviders;
|
||||
messagingService: MessageSender;
|
||||
stateService: StateService;
|
||||
environmentService: DefaultEnvironmentService;
|
||||
mainCryptoFunctionService: MainCryptoFunctionService;
|
||||
desktopCredentialStorageListener: DesktopCredentialStorageListener;
|
||||
desktopSettingsService: DesktopSettingsService;
|
||||
migrationRunner: MigrationRunner;
|
||||
tokenService: TokenServiceAbstraction;
|
||||
keyGenerationService: KeyGenerationServiceAbstraction;
|
||||
encryptService: EncryptService;
|
||||
|
||||
windowMain: WindowMain;
|
||||
messagingMain: MessagingMain;
|
||||
|
@ -162,31 +141,6 @@ export class Main {
|
|||
|
||||
this.environmentService = new DefaultEnvironmentService(stateProvider, accountService);
|
||||
|
||||
this.mainCryptoFunctionService = new MainCryptoFunctionService();
|
||||
this.mainCryptoFunctionService.init();
|
||||
|
||||
this.keyGenerationService = new KeyGenerationService(this.mainCryptoFunctionService);
|
||||
|
||||
this.encryptService = new EncryptServiceImplementation(
|
||||
this.mainCryptoFunctionService,
|
||||
this.logService,
|
||||
true, // log mac failures
|
||||
);
|
||||
|
||||
// Note: secure storage service is not available and should not be called in the main background process.
|
||||
const illegalSecureStorageService = new IllegalSecureStorageService();
|
||||
|
||||
this.tokenService = new TokenService(
|
||||
singleUserStateProvider,
|
||||
globalStateProvider,
|
||||
ELECTRON_SUPPORTS_SECURE_STORAGE,
|
||||
illegalSecureStorageService,
|
||||
this.keyGenerationService,
|
||||
this.encryptService,
|
||||
this.logService,
|
||||
async (logoutReason: LogoutReason, userId?: UserId) => {},
|
||||
);
|
||||
|
||||
this.migrationRunner = new MigrationRunner(
|
||||
this.storageService,
|
||||
this.logService,
|
||||
|
@ -194,21 +148,6 @@ export class Main {
|
|||
ClientType.Desktop,
|
||||
);
|
||||
|
||||
// TODO: this state service will have access to on disk storage, but not in memory storage.
|
||||
// If we could get this to work using the stateService singleton that the rest of the app uses we could save
|
||||
// ourselves from some hacks, like having to manually update the app menu vs. the menu subscribing to events.
|
||||
this.stateService = new ElectronStateService(
|
||||
this.storageService,
|
||||
null,
|
||||
this.memoryStorageService,
|
||||
this.logService,
|
||||
new StateFactory(GlobalState, Account),
|
||||
accountService, // will not broadcast logouts. This is a hack until we can remove messaging dependency
|
||||
this.environmentService,
|
||||
this.tokenService,
|
||||
this.migrationRunner,
|
||||
);
|
||||
|
||||
this.desktopSettingsService = new DesktopSettingsService(stateProvider);
|
||||
const biometricStateService = new DefaultBiometricStateService(stateProvider);
|
||||
|
||||
|
@ -220,7 +159,7 @@ export class Main {
|
|||
(arg) => this.processDeepLink(arg),
|
||||
(win) => this.trayMain.setupWindowListeners(win),
|
||||
);
|
||||
this.messagingMain = new MessagingMain(this, this.stateService, this.desktopSettingsService);
|
||||
this.messagingMain = new MessagingMain(this, this.desktopSettingsService);
|
||||
this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain);
|
||||
this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.desktopSettingsService);
|
||||
|
||||
|
@ -231,7 +170,13 @@ export class Main {
|
|||
);
|
||||
|
||||
messageSubject.asObservable().subscribe((message) => {
|
||||
this.messagingMain.onMessage(message);
|
||||
void this.messagingMain.onMessage(message).catch((err) => {
|
||||
this.logService.error(
|
||||
"Error while handling message",
|
||||
message?.command ?? "Unknown command",
|
||||
err,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
this.powerMonitorMain = new PowerMonitorMain(this.messagingService);
|
||||
|
@ -299,7 +244,7 @@ export class Main {
|
|||
await this.updaterMain.init();
|
||||
|
||||
const [browserIntegrationEnabled, ddgIntegrationEnabled] = await Promise.all([
|
||||
this.stateService.getEnableBrowserIntegration(),
|
||||
firstValueFrom(this.desktopSettingsService.browserIntegrationEnabled$),
|
||||
firstValueFrom(this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$),
|
||||
]);
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ import * as fs from "fs";
|
|||
import * as path from "path";
|
||||
|
||||
import { app, ipcMain } from "electron";
|
||||
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { Main } from "../main";
|
||||
import { DesktopSettingsService } from "../platform/services/desktop-settings.service";
|
||||
|
@ -17,7 +16,6 @@ export class MessagingMain {
|
|||
|
||||
constructor(
|
||||
private main: Main,
|
||||
private stateService: StateService,
|
||||
private desktopSettingsService: DesktopSettingsService,
|
||||
) {}
|
||||
|
||||
|
@ -29,10 +27,13 @@ export class MessagingMain {
|
|||
const loginSettings = app.getLoginItemSettings();
|
||||
await this.desktopSettingsService.setOpenAtLogin(loginSettings.openAtLogin);
|
||||
}
|
||||
ipcMain.on("messagingService", async (event: any, message: any) => this.onMessage(message));
|
||||
ipcMain.on(
|
||||
"messagingService",
|
||||
async (event: any, message: any) => await this.onMessage(message),
|
||||
);
|
||||
}
|
||||
|
||||
onMessage(message: any) {
|
||||
async onMessage(message: any) {
|
||||
switch (message.command) {
|
||||
case "scheduleNextSync":
|
||||
this.scheduleNextSync();
|
||||
|
@ -44,13 +45,14 @@ export class MessagingMain {
|
|||
this.updateTrayMenu(message.updateRequest);
|
||||
break;
|
||||
case "minimizeOnCopy":
|
||||
// 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.stateService.getMinimizeOnCopyToClipboard().then((shouldMinimize) => {
|
||||
if (shouldMinimize && this.main.windowMain.win !== null) {
|
||||
{
|
||||
const shouldMinimizeOnCopy = await firstValueFrom(
|
||||
this.desktopSettingsService.minimizeOnCopy$,
|
||||
);
|
||||
if (shouldMinimizeOnCopy && this.main.windowMain.win !== null) {
|
||||
this.main.windowMain.win.minimize();
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "showTray":
|
||||
this.main.trayMain.showTray();
|
||||
|
|
|
@ -4,7 +4,9 @@ import {
|
|||
DESKTOP_SETTINGS_DISK,
|
||||
KeyDefinition,
|
||||
StateProvider,
|
||||
UserKeyDefinition,
|
||||
} from "@bitwarden/common/platform/state";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { WindowState } from "../models/domain/window-state";
|
||||
|
||||
|
@ -48,6 +50,27 @@ const ALWAYS_ON_TOP_KEY = new KeyDefinition<boolean>(DESKTOP_SETTINGS_DISK, "alw
|
|||
deserializer: (b) => b,
|
||||
});
|
||||
|
||||
const BROWSER_INTEGRATION_ENABLED = new KeyDefinition<boolean>(
|
||||
DESKTOP_SETTINGS_DISK,
|
||||
"browserIntegrationEnabled",
|
||||
{
|
||||
deserializer: (b) => b,
|
||||
},
|
||||
);
|
||||
|
||||
const BROWSER_INTEGRATION_FINGERPRINT_ENABLED = new KeyDefinition<boolean>(
|
||||
DESKTOP_SETTINGS_DISK,
|
||||
"browserIntegrationFingerprintEnabled",
|
||||
{
|
||||
deserializer: (b) => b,
|
||||
},
|
||||
);
|
||||
|
||||
const MINIMIZE_ON_COPY = new UserKeyDefinition<boolean>(DESKTOP_SETTINGS_DISK, "minimizeOnCopy", {
|
||||
deserializer: (b) => b,
|
||||
clearOn: [], // User setting, no need to clear
|
||||
});
|
||||
|
||||
/**
|
||||
* Various settings for controlling application behavior specific to the desktop client.
|
||||
*/
|
||||
|
@ -61,41 +84,68 @@ export class DesktopSettingsService {
|
|||
/**
|
||||
* Tha applications setting for whether or not to close the application into the system tray.
|
||||
*/
|
||||
closeToTray$ = this.closeToTrayState.state$.pipe(map((value) => value ?? false));
|
||||
closeToTray$ = this.closeToTrayState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly minimizeToTrayState = this.stateProvider.getGlobal(MINIMIZE_TO_TRAY_KEY);
|
||||
/**
|
||||
* The application setting for whether or not to minimize the applicaiton into the system tray.
|
||||
*/
|
||||
minimizeToTray$ = this.minimizeToTrayState.state$.pipe(map((value) => value ?? false));
|
||||
minimizeToTray$ = this.minimizeToTrayState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly startToTrayState = this.stateProvider.getGlobal(START_TO_TRAY_KEY);
|
||||
/**
|
||||
* The application setting for whether or not to start the application into the system tray.
|
||||
*/
|
||||
startToTray$ = this.startToTrayState.state$.pipe(map((value) => value ?? false));
|
||||
startToTray$ = this.startToTrayState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly trayEnabledState = this.stateProvider.getGlobal(TRAY_ENABLED_KEY);
|
||||
/**
|
||||
* Whether or not the system tray has been enabled.
|
||||
*/
|
||||
trayEnabled$ = this.trayEnabledState.state$.pipe(map((value) => value ?? false));
|
||||
trayEnabled$ = this.trayEnabledState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly openAtLoginState = this.stateProvider.getGlobal(OPEN_AT_LOGIN_KEY);
|
||||
/**
|
||||
* The application setting for whether or not the application should open at system login.
|
||||
*/
|
||||
openAtLogin$ = this.openAtLoginState.state$.pipe(map((value) => value ?? false));
|
||||
openAtLogin$ = this.openAtLoginState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly alwaysShowDockState = this.stateProvider.getGlobal(ALWAYS_SHOW_DOCK_KEY);
|
||||
/**
|
||||
* The application setting for whether or not the application should show up in the dock.
|
||||
*/
|
||||
alwaysShowDock$ = this.alwaysShowDockState.state$.pipe(map((value) => value ?? false));
|
||||
alwaysShowDock$ = this.alwaysShowDockState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly alwaysOnTopState = this.stateProvider.getGlobal(ALWAYS_ON_TOP_KEY);
|
||||
|
||||
alwaysOnTop$ = this.alwaysOnTopState.state$.pipe(map((value) => value ?? false));
|
||||
alwaysOnTop$ = this.alwaysOnTopState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly browserIntegrationEnabledState = this.stateProvider.getGlobal(
|
||||
BROWSER_INTEGRATION_ENABLED,
|
||||
);
|
||||
|
||||
/**
|
||||
* The application setting for whether or not the browser integration is enabled.
|
||||
*/
|
||||
browserIntegrationEnabled$ = this.browserIntegrationEnabledState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly browserIntegrationFingerprintEnabledState = this.stateProvider.getGlobal(
|
||||
BROWSER_INTEGRATION_FINGERPRINT_ENABLED,
|
||||
);
|
||||
|
||||
/**
|
||||
* The application setting for whether or not the fingerprint should be verified before browser communication.
|
||||
*/
|
||||
browserIntegrationFingerprintEnabled$ =
|
||||
this.browserIntegrationFingerprintEnabledState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly minimizeOnCopyState = this.stateProvider.getActive(MINIMIZE_ON_COPY);
|
||||
|
||||
/**
|
||||
* The active users setting for whether or not the application should minimize itself
|
||||
* when a value is copied to the clipboard.
|
||||
*/
|
||||
minimizeOnCopy$ = this.minimizeOnCopyState.state$.pipe(map(Boolean));
|
||||
|
||||
constructor(private stateProvider: StateProvider) {
|
||||
this.window$ = this.windowState.state$.pipe(
|
||||
|
@ -177,4 +227,32 @@ export class DesktopSettingsService {
|
|||
async setAlwaysOnTop(value: boolean) {
|
||||
await this.alwaysOnTopState.update(() => value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting for whether or not the browser integration has been enabled.
|
||||
* @param value `true` if the integration with the browser extension is enabled,
|
||||
* `false` if it is not.
|
||||
*/
|
||||
async setBrowserIntegrationEnabled(value: boolean) {
|
||||
await this.browserIntegrationEnabledState.update(() => value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a setting for whether or not the browser fingerprint should be verified before
|
||||
* communication with the browser integration should be done.
|
||||
* @param value `true` if the fingerprint should be validated before use, `false` if it should not.
|
||||
*/
|
||||
async setBrowserIntegrationFingerprintEnabled(value: boolean) {
|
||||
await this.browserIntegrationFingerprintEnabledState.update(() => value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimize on copy value for the current user.
|
||||
* @param value `true` if the application should minimize when a value is copied,
|
||||
* `false` if it should not.
|
||||
* @param userId The user id of the user to update the setting for.
|
||||
*/
|
||||
async setMinimizeOnCopy(value: boolean, userId: UserId) {
|
||||
await this.stateProvider.getUser(userId, MINIMIZE_ON_COPY).update(() => value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se
|
|||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { KeySuffixOptions } from "@bitwarden/common/platform/enums";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
@ -23,6 +22,7 @@ import { BrowserSyncVerificationDialogComponent } from "../app/components/browse
|
|||
import { LegacyMessage } from "../models/native-messaging/legacy-message";
|
||||
import { LegacyMessageWrapper } from "../models/native-messaging/legacy-message-wrapper";
|
||||
import { Message } from "../models/native-messaging/message";
|
||||
import { DesktopSettingsService } from "../platform/services/desktop-settings.service";
|
||||
|
||||
import { NativeMessageHandlerService } from "./native-message-handler.service";
|
||||
|
||||
|
@ -40,7 +40,7 @@ export class NativeMessagingService {
|
|||
private platformUtilService: PlatformUtilsService,
|
||||
private logService: LogService,
|
||||
private messagingService: MessagingService,
|
||||
private stateService: StateService,
|
||||
private desktopSettingService: DesktopSettingsService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private nativeMessageHandler: NativeMessageHandlerService,
|
||||
private dialogService: DialogService,
|
||||
|
@ -78,7 +78,7 @@ export class NativeMessagingService {
|
|||
return;
|
||||
}
|
||||
|
||||
if (await this.stateService.getEnableBrowserIntegrationFingerprint()) {
|
||||
if (await firstValueFrom(this.desktopSettingService.browserIntegrationFingerprintEnabled$)) {
|
||||
ipc.platform.nativeMessaging.sendMessage({
|
||||
command: "verifyFingerprint",
|
||||
appId: appId,
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue