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

Workers handle multiple commands. New command to feature flags

This commit is contained in:
Matt Gibson
2025-03-05 16:43:26 -08:00
parent 5c4c6cdea1
commit ad9a37875b
5 changed files with 98 additions and 24 deletions

View File

@@ -1,10 +1,13 @@
import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface";
import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { OnServerConfigChange } from "../../../platform/abstractions/config/config.service";
import { ServerConfig } from "../../../platform/abstractions/config/server-config";
import { Decryptable } from "../../../platform/interfaces/decryptable.interface";
import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
export abstract class BulkEncryptService {
export abstract class BulkEncryptService implements OnServerConfigChange {
abstract decryptItems<T extends InitializerMetadata>(
items: Decryptable<T>[],
key: SymmetricCryptoKey,
): Promise<T[]>;
abstract onServerConfigChange(newConfig: ServerConfig): void;
}

View File

@@ -12,6 +12,10 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer";
import { ServerConfig } from "../../../platform/abstractions/config/server-config";
import { buildDecryptMessage, buildSetConfigMessage } from "./encrypt.worker";
// TTL (time to live) is not strictly required but avoids tying up memory resources if inactive
const workerTTL = 60000; // 1 minute
const maxWorkers = 8;
@@ -57,6 +61,13 @@ export class BulkEncryptServiceImplementation implements BulkEncryptService {
return decryptedItems;
}
onServerConfigChange(newConfig: ServerConfig): void {
this.workers.forEach((worker) => {
const request = buildSetConfigMessage({ newConfig });
worker.postMessage(request);
});
}
/**
* Sends items to a set of web workers to decrypt them. This utilizes multiple workers to decrypt items
* faster without interrupting other operations (e.g. updating UI).
@@ -108,17 +119,18 @@ export class BulkEncryptServiceImplementation implements BulkEncryptService {
itemsForWorker.push(...items.slice(end));
}
const request = {
id: Utils.newGuid(),
const id = Utils.newGuid();
const request = buildDecryptMessage({
id,
items: itemsForWorker,
key: key,
};
});
worker.postMessage(JSON.stringify(request));
worker.postMessage(request);
results.push(
firstValueFrom(
fromEvent(worker, "message").pipe(
filter((response: MessageEvent) => response.data?.id === request.id),
filter((response: MessageEvent) => response.data?.id === id),
map((response) => JSON.parse(response.data.items)),
map((items) =>
items.map((jsonItem: Jsonify<T>) => {

View File

@@ -9,19 +9,48 @@ import { ContainerService } from "@bitwarden/common/platform/services/container.
import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer";
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
import { ServerConfig } from "../../../platform/abstractions/config/server-config";
import { LogService } from "../../../platform/abstractions/log.service";
import { EncryptServiceImplementation } from "./encrypt.service.implementation";
const workerApi: Worker = self as any;
let inited = false;
let encryptService: EncryptServiceImplementation;
let logService: LogService;
const DECRYPT_COMMAND_SHELL = Object.freeze({ command: "decrypt" });
const SET_CONFIG_COMMAND_SHELL = Object.freeze({ command: "setConfig" });
type DecryptCommandData = {
id: string;
items: Jsonify<Decryptable<any>>[];
key: Jsonify<SymmetricCryptoKey>;
};
type SetConfigCommandData = { newConfig: ServerConfig };
export function buildDecryptMessage(data: DecryptCommandData): string {
return JSON.stringify({
...data,
...DECRYPT_COMMAND_SHELL,
});
}
export function buildSetConfigMessage(data: SetConfigCommandData): string {
return JSON.stringify({
...data,
...SET_CONFIG_COMMAND_SHELL,
});
}
/**
* Bootstrap the worker environment with services required for decryption
*/
export function init() {
const cryptoFunctionService = new WebCryptoFunctionService(self);
const logService = new ConsoleLogService(false);
logService = new ConsoleLogService(false);
encryptService = new EncryptServiceImplementation(cryptoFunctionService, logService, true);
const bitwardenContainerService = new ContainerService(null, encryptService);
@@ -39,11 +68,20 @@ workerApi.addEventListener("message", async (event: { data: string }) => {
}
const request: {
id: string;
items: Jsonify<Decryptable<any>>[];
key: Jsonify<SymmetricCryptoKey>;
command: string;
} = JSON.parse(event.data);
switch (request.command) {
case DECRYPT_COMMAND_SHELL.command:
return await handleDecrypt(request as unknown as DecryptCommandData);
case SET_CONFIG_COMMAND_SHELL.command:
return await handleSetConfig(request as unknown as SetConfigCommandData);
default:
logService.error(`unknown worker command`, request.command, request);
}
});
async function handleDecrypt(request: DecryptCommandData) {
const key = SymmetricCryptoKey.fromJSON(request.key);
const items = request.items.map((jsonItem) => {
const initializer = getClassInitializer<Decryptable<any>>(jsonItem.initializerKey);
@@ -55,4 +93,8 @@ workerApi.addEventListener("message", async (event: { data: string }) => {
id: request.id,
items: JSON.stringify(result),
});
});
}
async function handleSetConfig(request: SetConfigCommandData) {
encryptService.onServerConfigChange(request.newConfig);
}

View File

@@ -1,10 +1,10 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service";
import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface";
import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { BulkEncryptService } from "../../../key-management/crypto/abstractions/bulk-encrypt.service";
import { ServerConfig } from "../../../platform/abstractions/config/server-config";
import { Decryptable } from "../../../platform/interfaces/decryptable.interface";
import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { EncryptService } from "../abstractions/encrypt.service";
/**
@@ -33,4 +33,8 @@ export class FallbackBulkEncryptService implements BulkEncryptService {
async setFeatureFlagEncryptService(featureFlagEncryptService: BulkEncryptService) {
this.featureFlagEncryptService = featureFlagEncryptService;
}
onServerConfigChange(newConfig: ServerConfig): void {
(this.featureFlagEncryptService ?? this.encryptService).onServerConfigChange(newConfig);
}
}

View File

@@ -9,7 +9,10 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer";
import { ServerConfig } from "../../../platform/abstractions/config/server-config";
import { EncryptServiceImplementation } from "./encrypt.service.implementation";
import { buildDecryptMessage, buildSetConfigMessage } from "./encrypt.worker";
// TTL (time to live) is not strictly required but avoids tying up memory resources if inactive
const workerTTL = 3 * 60000; // 3 minutes
@@ -47,17 +50,18 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple
this.restartTimeout();
const request = {
id: Utils.newGuid(),
const id = Utils.newGuid();
const request = buildDecryptMessage({
id,
items: items,
key: key,
};
});
this.worker.postMessage(JSON.stringify(request));
this.worker.postMessage(request);
return await firstValueFrom(
fromEvent(this.worker, "message").pipe(
filter((response: MessageEvent) => response.data?.id === request.id),
filter((response: MessageEvent) => response.data?.id === id),
map((response) => JSON.parse(response.data.items)),
map((items) =>
items.map((jsonItem: Jsonify<T>) => {
@@ -71,6 +75,15 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple
);
}
override onServerConfigChange(newConfig: ServerConfig): void {
super.onServerConfigChange(newConfig);
if (this.worker !== null) {
const request = buildSetConfigMessage({ newConfig });
this.worker.postMessage(request);
}
}
private clear() {
this.clear$.next();
this.worker?.terminate();