1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-21 11:35:34 +01:00

[PM-11766] Introduce SDK client (#10974)

Integrate the SDK into our other clients.
This commit is contained in:
Oscar Hinton 2024-10-07 13:20:50 +02:00 committed by GitHub
parent 37faccb7e9
commit c88c5bf1ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 424 additions and 34 deletions

View File

@ -2,6 +2,7 @@
"devFlags": {},
"flags": {
"showPasswordless": true,
"accountSwitching": false
"accountSwitching": false,
"sdk": false
}
}

View File

@ -90,6 +90,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import {
AbstractStorageService,
@ -122,6 +123,8 @@ import { FileUploadService } from "@bitwarden/common/platform/services/file-uplo
import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StateService } from "@bitwarden/common/platform/services/state.service";
import { SystemService } from "@bitwarden/common/platform/services/system.service";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
@ -228,6 +231,7 @@ import AutofillService from "../autofill/services/autofill.service";
import { SafariApp } from "../browser/safariApp";
import { BackgroundBrowserBiometricsService } from "../key-management/biometrics/background-browser-biometrics.service";
import { BrowserApi } from "../platform/browser/browser-api";
import { flagEnabled } from "../platform/flags";
import { UpdateBadge } from "../platform/listeners/update-badge";
/* eslint-disable no-restricted-imports */
import { ChromeMessageSender } from "../platform/messaging/chrome-message.sender";
@ -245,6 +249,7 @@ import { LocalBackedSessionStorageService } from "../platform/services/local-bac
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service";
import { BrowserSdkClientFactory } from "../platform/services/sdk/browser-sdk-client-factory";
import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
import { ForegroundTaskSchedulerService } from "../platform/services/task-scheduler/foreground-task-scheduler.service";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
@ -364,6 +369,7 @@ export default class MainBackground {
syncServiceListener: SyncServiceListener;
themeStateService: DefaultThemeStateService;
autoSubmitLoginBackground: AutoSubmitLoginBackground;
sdkService: SdkService;
onUpdatedRan: boolean;
onReplacedRan: boolean;
@ -719,6 +725,15 @@ export default class MainBackground {
this.stateProvider,
);
const sdkClientFactory = flagEnabled("sdk")
? new BrowserSdkClientFactory()
: new NoopSdkClientFactory();
this.sdkService = new DefaultSdkService(
sdkClientFactory,
this.environmentService,
this.platformUtilsService,
);
this.passwordStrengthService = new PasswordStrengthService();
this.passwordGenerationService = legacyPasswordGenerationServiceFactory(

View File

@ -39,8 +39,7 @@
}
],
"background": {
"service_worker": "background.js",
"type": "module"
"service_worker": "background.js"
},
"action": {
"default_icon": {

View File

@ -0,0 +1,37 @@
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import type { BitwardenClient } from "@bitwarden/sdk-internal";
// https://stackoverflow.com/a/47880734
const supported = (() => {
try {
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
const module = new WebAssembly.Module(
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
);
if (module instanceof WebAssembly.Module) {
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
}
}
} catch (e) {
// ignore
}
return false;
})();
if (supported) {
// eslint-disable-next-line no-console
console.debug("WebAssembly is supported in this environment");
import("./wasm");
} else {
// eslint-disable-next-line no-console
console.debug("WebAssembly is not supported in this environment");
import("./fallback");
}
export class BrowserSdkClientFactory implements SdkClientFactory {
async createSdkClient(
...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient> {
return Promise.resolve((globalThis as any).init_sdk(...args));
}
}

View File

@ -0,0 +1,8 @@
import * as sdk from "@bitwarden/sdk-internal";
import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js";
(globalThis as any).init_sdk = (...args: ConstructorParameters<typeof sdk.BitwardenClient>) => {
(sdk as any).init(wasm);
return new sdk.BitwardenClient(...args);
};

View File

@ -0,0 +1,8 @@
import * as sdk from "@bitwarden/sdk-internal";
import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm";
(globalThis as any).init_sdk = (...args: ConstructorParameters<typeof sdk.BitwardenClient>) => {
(sdk as any).init(wasm);
return new sdk.BitwardenClient(...args);
};

View File

@ -58,6 +58,7 @@ import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/ke
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import {
AbstractStorageService,
@ -66,9 +67,11 @@ import {
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
// eslint-disable-next-line no-restricted-imports -- Used for dependency injection
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
import { flagEnabled } from "@bitwarden/common/platform/misc/flags";
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
import {
@ -114,6 +117,7 @@ import BrowserLocalStorageService from "../../platform/services/browser-local-st
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
import I18nService from "../../platform/services/i18n.service";
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
import { BrowserSdkClientFactory } from "../../platform/services/sdk/browser-sdk-client-factory";
import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
@ -572,6 +576,11 @@ const safeProviders: SafeProvider[] = [
useClass: ForegroundLockService,
deps: [MessageSender, MessageListener],
}),
safeProvider({
provide: SdkClientFactory,
useClass: flagEnabled("sdk") ? BrowserSdkClientFactory : NoopSdkClientFactory,
deps: [],
}),
];
@NgModule({

View File

@ -122,7 +122,7 @@ const moduleRules = [
loader: "@ngtools/webpack",
},
{
test: /\.wasm$/,
test: /argon2(-simd)?\.wasm$/,
loader: "base64-loader",
type: "javascript/auto",
},
@ -320,9 +320,12 @@ const mainConfig = {
clean: true,
},
module: {
noParse: /\.wasm$/,
noParse: /argon2(-simd)?\.wasm$/,
rules: moduleRules,
},
experiments: {
asyncWebAssembly: true,
},
plugins: plugins,
};
@ -395,12 +398,15 @@ if (manifestVersion == 2) {
loader: "ts-loader",
},
{
test: /\.wasm$/,
test: /argon2(-simd)?\.wasm$/,
loader: "base64-loader",
type: "javascript/auto",
},
],
noParse: /\.wasm$/,
noParse: /argon2(-simd)?\.wasm$/,
},
experiments: {
asyncWebAssembly: true,
},
resolve: {
extensions: [".ts", ".js"],

View File

@ -0,0 +1,5 @@
{
"flags": {
"sdk": false
}
}

View File

@ -1,7 +1,27 @@
function load(envName) {
const base = require("./base.json");
const env = loadConfig(envName);
const local = loadConfig("local");
return {
...loadConfig(envName),
...loadConfig("local"),
...base,
...env,
...local,
dev: {
...base.dev,
...env.dev,
...local.dev,
},
flags: {
...base.flags,
...env.flags,
...local.flags,
},
devFlags: {
...base.devFlags,
...env.devFlags,
...local.devFlags,
},
};
}

View File

@ -10,7 +10,11 @@ module.exports = {
preset: "ts-jest",
testEnvironment: "node",
setupFilesAfterEnv: ["<rootDir>/test.setup.ts"],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, {
prefix: "<rootDir>/",
}),
moduleNameMapper: {
"@bitwarden/common/platform/services/sdk/default-sdk-client-factory":
"<rootDir>/../../libs/common/spec/jest-sdk-client-factory",
...pathsToModuleNameMapper(compilerOptions?.paths || {}, {
prefix: "<rootDir>/",
}),
},
};

View File

@ -64,6 +64,7 @@ import {
RegionConfig,
} from "@bitwarden/common/platform/abstractions/environment.service";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { MessageSender } from "@bitwarden/common/platform/messaging";
@ -86,6 +87,9 @@ import { KeyGenerationService } from "@bitwarden/common/platform/services/key-ge
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";
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StateService } from "@bitwarden/common/platform/services/state.service";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
@ -151,6 +155,7 @@ import {
VaultExportServiceAbstraction,
} from "@bitwarden/vault-export-core";
import { flagEnabled } from "../platform/flags";
import { CliPlatformUtilsService } from "../platform/services/cli-platform-utils.service";
import { ConsoleLogService } from "../platform/services/console-log.service";
import { I18nService } from "../platform/services/i18n.service";
@ -249,6 +254,7 @@ export class ServiceContainer {
userAutoUnlockKeyService: UserAutoUnlockKeyService;
kdfConfigService: KdfConfigServiceAbstraction;
taskSchedulerService: TaskSchedulerService;
sdkService: SdkService;
constructor() {
let p = null;
@ -522,6 +528,16 @@ export class ServiceContainer {
this.globalStateProvider,
);
const sdkClientFactory = flagEnabled("sdk")
? new DefaultSdkClientFactory()
: new NoopSdkClientFactory();
this.sdkService = new DefaultSdkService(
sdkClientFactory,
this.environmentService,
this.platformUtilsService,
customUserAgent,
);
this.passwordStrengthService = new PasswordStrengthService();
this.passwordGenerationService = legacyPasswordGenerationServiceFactory(

View File

@ -3,7 +3,7 @@
"pretty": true,
"moduleResolution": "node",
"target": "ES2016",
"module": "es6",
"module": "ES2020",
"noImplicitAny": true,
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,

View File

@ -37,8 +37,10 @@ const plugins = [
contextRegExp: /node-fetch/,
}),
new webpack.EnvironmentPlugin({
ENV: ENV,
BWCLI_ENV: ENV,
FLAGS: envConfig.flags,
DEV_FLAGS: envConfig.devFlags,
}),
new webpack.IgnorePlugin({
resourceRegExp: /canvas/,
@ -79,6 +81,9 @@ const webpackConfig = {
allowlist: [/@bitwarden/],
}),
],
experiments: {
asyncWebAssembly: true,
},
};
module.exports = webpackConfig;

View File

@ -1,4 +1,6 @@
{
"devFlags": {},
"flags": {}
"flags": {
"sdk": false
},
"devFlags": {}
}

View File

@ -52,6 +52,7 @@ import {
} from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
@ -60,6 +61,8 @@ import { Message, MessageListener, MessageSender } from "@bitwarden/common/platf
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { SystemService } from "@bitwarden/common/platform/services/system.service";
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
@ -73,6 +76,7 @@ import { BiometricStateService, BiometricsService } from "@bitwarden/key-managem
import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service";
import { ElectronBiometricsService } from "../../key-management/biometrics/electron-biometrics.service";
import { flagEnabled } from "../../platform/flags";
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
import { ElectronCryptoService } from "../../platform/services/electron-crypto.service";
import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service";
@ -302,6 +306,11 @@ const safeProviders: SafeProvider[] = [
InternalUserDecryptionOptionsServiceAbstraction,
],
}),
safeProvider({
provide: SdkClientFactory,
useClass: flagEnabled("sdk") ? DefaultSdkClientFactory : NoopSdkClientFactory,
deps: [],
}),
];
@NgModule({

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'self' 'unsafe-inline';
content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline';
img-src 'self' data: *; child-src *; frame-src *; connect-src *;"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />

View File

@ -42,7 +42,7 @@ const common = {
type: "asset/resource",
},
{
test: /\.wasm$/,
test: /argon2(-simd)?\.wasm$/,
loader: "base64-loader",
type: "javascript/auto",
},
@ -143,11 +143,15 @@ const renderer = {
parser: { system: true },
},
{
test: /\.wasm$/,
test: /argon2(-simd)?\.wasm$/,
loader: "base64-loader",
type: "javascript/auto",
},
],
noParse: /argon2(-simd)?\.wasm$/,
},
experiments: {
asyncWebAssembly: true,
},
plugins: [
new AngularWebpackPlugin({

View File

@ -11,6 +11,8 @@
"allowedHosts": "auto"
},
"flags": {
"showPasswordless": false
}
"showPasswordless": false,
"sdk": false
},
"devFlags": {}
}

View File

@ -9,11 +9,19 @@ module.exports = {
...sharedConfig,
preset: "jest-preset-angular",
setupFilesAfterEnv: ["<rootDir>/test.setup.ts"],
moduleNameMapper: pathsToModuleNameMapper(
// lets us use @bitwarden/common/spec in web tests
{ "@bitwarden/common/spec": ["../../libs/common/spec"], ...(compilerOptions?.paths ?? {}) },
{
prefix: "<rootDir>/",
},
),
moduleNameMapper: {
// Replace ESM SDK with Node compatible SDK
"@bitwarden/common/platform/services/sdk/default-sdk-client-factory":
"<rootDir>/../../libs/common/spec/jest-sdk-client-factory",
...pathsToModuleNameMapper(
{
// lets us use @bitwarden/common/spec in web tests
"@bitwarden/common/spec": ["../../libs/common/spec"],
...(compilerOptions?.paths ?? {}),
},
{
prefix: "<rootDir>/",
},
),
},
};

View File

@ -51,6 +51,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { AppIdService as DefaultAppIdService } from "@bitwarden/common/platform/services/app-id.service";
@ -58,6 +59,7 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
/* eslint-disable import/no-restricted-paths -- Implementation for memory storage */
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
@ -72,6 +74,7 @@ import { VaultTimeout, VaultTimeoutStringType } from "@bitwarden/common/types/va
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { BiometricsService } from "@bitwarden/key-management";
import { flagEnabled } from "../../utils/flags";
import { PolicyListService } from "../admin-console/core/policy-list.service";
import {
WebSetPasswordJitService,
@ -84,6 +87,7 @@ import { I18nService } from "../core/i18n.service";
import { WebBiometricsService } from "../key-management/web-biometric.service";
import { WebEnvironmentService } from "../platform/web-environment.service";
import { WebMigrationRunner } from "../platform/web-migration-runner";
import { WebSdkClientFactory } from "../platform/web-sdk-client-factory";
import { WebStorageServiceProvider } from "../platform/web-storage-service.provider";
import { EventService } from "./event.service";
@ -245,6 +249,11 @@ const safeProviders: SafeProvider[] = [
useClass: DefaultCollectionAdminService,
deps: [ApiService, CryptoServiceAbstraction, EncryptService, CollectionService],
}),
safeProvider({
provide: SdkClientFactory,
useClass: flagEnabled("sdk") ? WebSdkClientFactory : NoopSdkClientFactory,
deps: [],
}),
];
@NgModule({

View File

@ -0,0 +1,42 @@
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import * as sdk from "@bitwarden/sdk-internal";
/**
* SDK client factory with a js fallback for when WASM is not supported.
*/
export class WebSdkClientFactory implements SdkClientFactory {
async createSdkClient(
...args: ConstructorParameters<typeof sdk.BitwardenClient>
): Promise<sdk.BitwardenClient> {
const module = await load();
(sdk as any).init(module);
return Promise.resolve(new sdk.BitwardenClient(...args));
}
}
// https://stackoverflow.com/a/47880734
const supported = (() => {
try {
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
const module = new WebAssembly.Module(
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
);
if (module instanceof WebAssembly.Module) {
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
}
}
} catch (e) {
// ignore
}
return false;
})();
async function load() {
if (supported) {
return await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm");
} else {
return await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js");
}
}

View File

@ -78,7 +78,7 @@ const moduleRules = [
loader: "@ngtools/webpack",
},
{
test: /\.wasm$/,
test: /argon2(-simd)?\.wasm$/,
loader: "base64-loader",
type: "javascript/auto",
},
@ -324,6 +324,7 @@ const webpackConfig = {
mode: NODE_ENV,
devtool: "source-map",
devServer: devServer,
target: "web",
entry: {
"app/polyfills": "./src/polyfills.ts",
"app/main": "./src/main.ts",
@ -383,9 +384,12 @@ const webpackConfig = {
clean: true,
},
module: {
noParse: /\.wasm$/,
noParse: /argon2(-simd)?\.wasm$/,
rules: moduleRules,
},
experiments: {
asyncWebAssembly: true,
},
plugins: plugins,
};

View File

@ -10,7 +10,11 @@ module.exports = {
preset: "ts-jest",
testEnvironment: "node",
setupFilesAfterEnv: ["<rootDir>/../../apps/cli/test.setup.ts"],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, {
prefix: "<rootDir>/",
}),
moduleNameMapper: {
"@bitwarden/common/platform/services/sdk/default-sdk-client-factory":
"<rootDir>/../../libs/common/spec/jest-sdk-client-factory",
...pathsToModuleNameMapper(compilerOptions?.paths || {}, {
prefix: "<rootDir>/",
}),
},
};

View File

@ -3,7 +3,7 @@
"pretty": true,
"moduleResolution": "node",
"target": "ES2016",
"module": "es6",
"module": "ES2020",
"noImplicitAny": true,
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,

View File

@ -151,6 +151,8 @@ import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwar
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { ValidationService as ValidationServiceAbstraction } from "@bitwarden/common/platform/abstractions/validation.service";
@ -179,6 +181,7 @@ import { KeyGenerationService } from "@bitwarden/common/platform/services/key-ge
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { NoopNotificationsService } from "@bitwarden/common/platform/services/noop-notifications.service";
import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { StateService } from "@bitwarden/common/platform/services/state.service";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
@ -1324,6 +1327,11 @@ const safeProviders: SafeProvider[] = [
useExisting: NoopViewCacheService,
deps: [],
}),
safeProvider({
provide: SdkService,
useClass: DefaultSdkService,
deps: [SdkClientFactory, EnvironmentService, PlatformUtilsServiceAbstraction],
}),
];
@NgModule({

View File

@ -0,0 +1,9 @@
import { ClientSettings, LogLevel, BitwardenClient } from "@bitwarden/sdk-internal";
import { SdkClientFactory } from "../src/platform/abstractions/sdk/sdk-client-factory";
export class DefaultSdkClientFactory implements SdkClientFactory {
createSdkClient(settings?: ClientSettings, log_level?: LogLevel): Promise<BitwardenClient> {
throw new Error("Method not implemented.");
}
}

View File

@ -0,0 +1,10 @@
import type { BitwardenClient } from "@bitwarden/sdk-internal";
/**
* Factory for creating SDK clients.
*/
export abstract class SdkClientFactory {
abstract createSdkClient(
...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient>;
}

View File

@ -0,0 +1,8 @@
import { Observable } from "rxjs";
import { BitwardenClient } from "@bitwarden/sdk-internal";
export abstract class SdkService {
client$: Observable<BitwardenClient>;
supported$: Observable<boolean>;
}

View File

@ -2,6 +2,7 @@
// eslint-disable-next-line @typescript-eslint/ban-types
export type SharedFlags = {
showPasswordless?: boolean;
sdk?: boolean;
};
// required to avoid linting errors when there are no flags
@ -28,7 +29,7 @@ function getFlags<T>(envFlags: string | T): T {
* @returns The value of the flag
*/
export function flagEnabled<Flags extends SharedFlags>(flag: keyof Flags): boolean {
const flags = getFlags<Flags>(process.env.FLAGS);
const flags = getFlags<Flags>(process.env.FLAGS) ?? ({} as Flags);
return flags[flag] == null || !!flags[flag];
}

View File

@ -0,0 +1,19 @@
import * as sdk from "@bitwarden/sdk-internal";
import * as module from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm";
import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
/**
* Directly imports the Bitwarden SDK and initializes it.
*
* **Warning**: This requires WASM support and will fail if the environment does not support it.
*/
export class DefaultSdkClientFactory implements SdkClientFactory {
async createSdkClient(
...args: ConstructorParameters<typeof sdk.BitwardenClient>
): Promise<sdk.BitwardenClient> {
(sdk as any).init(module);
return Promise.resolve(new sdk.BitwardenClient(...args));
}
}

View File

@ -0,0 +1,95 @@
import { concatMap, shareReplay } from "rxjs";
import { LogLevel, DeviceType as SdkDeviceType } from "@bitwarden/sdk-internal";
import { DeviceType } from "../../../enums/device-type.enum";
import { EnvironmentService } from "../../abstractions/environment.service";
import { PlatformUtilsService } from "../../abstractions/platform-utils.service";
import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
import { SdkService } from "../../abstractions/sdk/sdk.service";
export class DefaultSdkService implements SdkService {
client$ = this.environmentService.environment$.pipe(
concatMap(async (env) => {
const settings = {
apiUrl: env.getApiUrl(),
identityUrl: env.getIdentityUrl(),
deviceType: this.toDevice(this.platformUtilsService.getDevice()),
userAgent: this.userAgent ?? navigator.userAgent,
};
return await this.sdkClientFactory.createSdkClient(settings, LogLevel.Info);
}),
shareReplay({ refCount: true, bufferSize: 1 }),
);
supported$ = this.client$.pipe(
concatMap(async (client) => {
return client.echo("bitwarden wasm!") === "bitwarden wasm!";
}),
);
constructor(
private sdkClientFactory: SdkClientFactory,
private environmentService: EnvironmentService,
private platformUtilsService: PlatformUtilsService,
private userAgent: string = null,
) {}
private toDevice(device: DeviceType): SdkDeviceType {
switch (device) {
case DeviceType.Android:
return "Android";
case DeviceType.iOS:
return "iOS";
case DeviceType.ChromeExtension:
return "ChromeExtension";
case DeviceType.FirefoxExtension:
return "FirefoxExtension";
case DeviceType.OperaExtension:
return "OperaExtension";
case DeviceType.EdgeExtension:
return "EdgeExtension";
case DeviceType.WindowsDesktop:
return "WindowsDesktop";
case DeviceType.MacOsDesktop:
return "MacOsDesktop";
case DeviceType.LinuxDesktop:
return "LinuxDesktop";
case DeviceType.ChromeBrowser:
return "ChromeBrowser";
case DeviceType.FirefoxBrowser:
return "FirefoxBrowser";
case DeviceType.OperaBrowser:
return "OperaBrowser";
case DeviceType.EdgeBrowser:
return "EdgeBrowser";
case DeviceType.IEBrowser:
return "IEBrowser";
case DeviceType.UnknownBrowser:
return "UnknownBrowser";
case DeviceType.AndroidAmazon:
return "AndroidAmazon";
case DeviceType.UWP:
return "UWP";
case DeviceType.SafariBrowser:
return "SafariBrowser";
case DeviceType.VivaldiBrowser:
return "VivaldiBrowser";
case DeviceType.VivaldiExtension:
return "VivaldiExtension";
case DeviceType.SafariExtension:
return "SafariExtension";
case DeviceType.Server:
return "Server";
case DeviceType.WindowsCLI:
return "WindowsCLI";
case DeviceType.MacOsCLI:
return "MacOsCLI";
case DeviceType.LinuxCLI:
return "LinuxCLI";
default:
return "SDK";
}
}
}

View File

@ -0,0 +1,16 @@
import type { BitwardenClient } from "@bitwarden/sdk-internal";
import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
/**
* Noop SDK client factory.
*
* Used during SDK rollout to prevent bundling the SDK with some applications.
*/
export class NoopSdkClientFactory implements SdkClientFactory {
createSdkClient(
...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient> {
return Promise.reject(new Error("SDK not available"));
}
}

6
package-lock.json generated
View File

@ -24,6 +24,7 @@
"@angular/platform-browser": "16.2.12",
"@angular/platform-browser-dynamic": "16.2.12",
"@angular/router": "16.2.12",
"@bitwarden/sdk-internal": "0.1.3",
"@electron/fuses": "1.8.0",
"@koa/multer": "3.0.2",
"@koa/router": "12.0.1",
@ -4625,6 +4626,11 @@
"resolved": "libs/platform",
"link": true
},
"node_modules/@bitwarden/sdk-internal": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.1.3.tgz",
"integrity": "sha512-zk9DyYMjylVLdljeLn3OLBcD939Hg/qMNJ2FxbyjiSKtcOcgglXgYmbcS01NRFFfM9REbn+j+2fWbQo6N+8SHw=="
},
"node_modules/@bitwarden/vault": {
"resolved": "libs/vault",
"link": true

View File

@ -157,6 +157,7 @@
"@angular/platform-browser": "16.2.12",
"@angular/platform-browser-dynamic": "16.2.12",
"@angular/router": "16.2.12",
"@bitwarden/sdk-internal": "0.1.3",
"@electron/fuses": "1.8.0",
"@koa/multer": "3.0.2",
"@koa/router": "12.0.1",