1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 13:10:17 +00:00

Refactor biometrics service

Create BiometricsService that takes care of loading the platformspecifc services, hiding the implementation details
Make it clearer which dependencies are needed by a specific biometrics-service (compile-error vs runtime-error)
Add unit tests
Isolate biometrics import/exports with a barrel file
This commit is contained in:
Daniel James Smith
2023-02-08 15:18:54 +01:00
parent caaaebbc0c
commit 003cf954a7
8 changed files with 163 additions and 26 deletions

View File

@@ -7,7 +7,7 @@ import { GlobalState } from "@bitwarden/common/models/domain/global-state";
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
import { StateService } from "@bitwarden/common/services/state.service";
import { BiometricMain } from "./main/biometric/biometric.main";
import { BiometricsService, BiometricsServiceAbstraction } from "./main/biometric/index";
import { DesktopCredentialStorageListener } from "./main/desktop-credential-storage-listener";
import { MenuMain } from "./main/menu/menu.main";
import { MessagingMain } from "./main/messaging.main";
@@ -37,7 +37,7 @@ export class Main {
menuMain: MenuMain;
powerMonitorMain: PowerMonitorMain;
trayMain: TrayMain;
biometricMain: BiometricMain;
biometricsService: BiometricsServiceAbstraction;
nativeMessagingMain: NativeMessagingMain;
constructor() {
@@ -117,24 +117,16 @@ export class Main {
this.updaterMain
);
if (process.platform === "win32") {
// eslint-disable-next-line
const BiometricWindowsMain = require("./main/biometric/biometric.windows.main").default;
this.biometricMain = new BiometricWindowsMain(
this.i18nService,
this.windowMain,
this.stateService,
this.logService
);
} else if (process.platform === "darwin") {
// eslint-disable-next-line
const BiometricDarwinMain = require("./main/biometric/biometric.darwin.main").default;
this.biometricMain = new BiometricDarwinMain(this.i18nService, this.stateService);
}
this.biometricsService = new BiometricsService(
this.i18nService,
this.windowMain,
this.stateService,
this.logService
);
this.desktopCredentialStorageListener = new DesktopCredentialStorageListener(
"Bitwarden",
this.biometricMain
this.biometricsService
);
this.nativeMessagingMain = new NativeMessagingMain(
@@ -166,8 +158,8 @@ export class Main {
}
this.powerMonitorMain.init();
await this.updaterMain.init();
if (this.biometricMain != null) {
await this.biometricMain.init();
if (this.biometricsService != null) {
await this.biometricsService.init();
}
if (

View File

@@ -3,9 +3,9 @@ import { ipcMain, systemPreferences } from "electron";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { BiometricMain } from "../biometric/biometric.main";
import { BiometricsServiceAbstraction } from "./biometrics.service.abstraction";
export default class BiometricDarwinMain implements BiometricMain {
export default class BiometricDarwinMain implements BiometricsServiceAbstraction {
constructor(private i18nservice: I18nService, private stateService: StateService) {}
async init() {

View File

@@ -7,9 +7,9 @@ import { biometrics } from "@bitwarden/desktop-native";
import { WindowMain } from "../window.main";
import { BiometricMain } from "./biometric.main";
import { BiometricsServiceAbstraction } from "./biometrics.service.abstraction";
export default class BiometricWindowsMain implements BiometricMain {
export default class BiometricWindowsMain implements BiometricsServiceAbstraction {
constructor(
private i18nservice: I18nService,
private windowMain: WindowMain,

View File

@@ -1,4 +1,4 @@
export abstract class BiometricMain {
export abstract class BiometricsServiceAbstraction {
init: () => Promise<void>;
supportsBiometric: () => Promise<boolean>;
authenticateBiometric: () => Promise<boolean>;

View File

@@ -0,0 +1,83 @@
import { mock } from "jest-mock-extended";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { WindowMain } from "../window.main";
import BiometricDarwinMain from "./biometric.darwin.main";
import BiometricWindowsMain from "./biometric.windows.main";
import { BiometricsService } from "./biometrics.service";
import { BiometricsServiceAbstraction } from "./biometrics.service.abstraction";
describe("biometrics tests", function () {
const i18nService = mock<I18nService>();
const windowMain = mock<WindowMain>();
const stateService = mock<StateService>();
const logService = mock<LogService>();
it("Should call the platformspecific methods", () => {
const sut = new BiometricsService(i18nService, windowMain, stateService, logService);
const mockService = mock<BiometricsServiceAbstraction>();
(sut as any).platformSpecificService = mockService;
sut.init();
expect(mockService.init).toBeCalled();
sut.supportsBiometric();
expect(mockService.supportsBiometric).toBeCalled();
sut.authenticateBiometric();
expect(mockService.authenticateBiometric).toBeCalled();
});
describe("win32 process platform", function () {
let originalPlatform: NodeJS.Platform = null;
beforeAll(function () {
originalPlatform = process.platform;
Object.defineProperty(process, "platform", {
value: "win32",
});
});
const sut = new BiometricsService(i18nService, windowMain, stateService, logService);
it("Should create a biometrics service specific for Windows", () => {
const internalService = (sut as any).platformSpecificService;
expect(internalService).not.toBeNull();
expect(internalService).toBeInstanceOf(BiometricWindowsMain);
});
afterAll(function () {
Object.defineProperty(process, "platform", {
value: originalPlatform,
});
});
});
describe("darwin process platform", function () {
let originalPlatform: NodeJS.Platform = null;
beforeAll(function () {
originalPlatform = process.platform;
Object.defineProperty(process, "platform", {
value: "darwin",
});
});
it("Should create a biometrics service specific for MacOs", () => {
const sut = new BiometricsService(i18nService, windowMain, stateService, logService);
const internalService = (sut as any).platformSpecificService;
expect(internalService).not.toBeNull();
expect(internalService).toBeInstanceOf(BiometricDarwinMain);
});
afterAll(function () {
Object.defineProperty(process, "platform", {
value: originalPlatform,
});
});
});
});

View File

@@ -0,0 +1,57 @@
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { WindowMain } from "../window.main";
import { BiometricsServiceAbstraction } from "./biometrics.service.abstraction";
export class BiometricsService implements BiometricsServiceAbstraction {
private platformSpecificService: BiometricsServiceAbstraction;
constructor(
private i18nService: I18nService,
private windowMain: WindowMain,
private stateService: StateService,
private logService: LogService
) {
this.loadPlatformSpecificService();
}
private loadPlatformSpecificService() {
if (process.platform === "win32") {
this.loadWindowsHelloService();
} else if (process.platform === "darwin") {
this.loadMacOSService();
}
}
private loadWindowsHelloService() {
// eslint-disable-next-line
const BiometricWindowsMain = require("./biometric.windows.main").default;
this.platformSpecificService = new BiometricWindowsMain(
this.i18nService,
this.windowMain,
this.stateService,
this.logService
);
}
private loadMacOSService() {
// eslint-disable-next-line
const BiometricDarwinMain = require("./biometric.darwin.main").default;
this.platformSpecificService = new BiometricDarwinMain(this.i18nService, this.stateService);
}
async init() {
return await this.platformSpecificService.init();
}
async supportsBiometric(): Promise<boolean> {
return await this.platformSpecificService.supportsBiometric();
}
async authenticateBiometric(): Promise<boolean> {
return await this.platformSpecificService.authenticateBiometric();
}
}

View File

@@ -0,0 +1,2 @@
export * from "./biometrics.service.abstraction";
export * from "./biometrics.service";

View File

@@ -2,13 +2,16 @@ import { ipcMain } from "electron";
import { passwords } from "@bitwarden/desktop-native";
import { BiometricMain } from "./biometric/biometric.main";
import { BiometricsServiceAbstraction } from "./biometric/index";
const AuthRequiredSuffix = "_biometric";
const AuthenticatedActions = ["getPassword"];
export class DesktopCredentialStorageListener {
constructor(private serviceName: string, private biometricService: BiometricMain) {}
constructor(
private serviceName: string,
private biometricService: BiometricsServiceAbstraction
) {}
init() {
ipcMain.handle("keytar", async (event: any, message: any) => {