From 9ea9c3a932433ac44997df6da9f26855ae79546d Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Mon, 7 Oct 2024 13:56:02 +0200 Subject: [PATCH] [PM-11290] Enable SDK (#11378) Follow up PR to #10974, flips the compile time flags to enabled and includes some debug logic to detect if users encounter issues with the WASM bundle in preparation for active consumption of the SDK. --- apps/browser/config/base.json | 2 +- .../browser/src/background/main.background.ts | 15 ++++++++++ .../sdk/browser-sdk-client-factory.ts | 5 ++++ apps/browser/src/popup/app.component.ts | 29 ++++++++++++++++-- apps/cli/config/base.json | 2 +- .../service-container/service-container.ts | 15 ++++++++++ apps/desktop/config/base.json | 2 +- apps/desktop/src/app/app.component.ts | 29 +++++++++++++++--- apps/web/config/base.json | 2 +- apps/web/src/app/app.component.ts | 30 +++++++++++++++++-- .../src/services/jslib-services.module.ts | 7 ++++- .../platform/abstractions/sdk/sdk.service.ts | 2 ++ .../services/sdk/default-sdk.service.ts | 18 ++++++++++- 13 files changed, 144 insertions(+), 14 deletions(-) diff --git a/apps/browser/config/base.json b/apps/browser/config/base.json index 9506bda0f0..91d4830924 100644 --- a/apps/browser/config/base.json +++ b/apps/browser/config/base.json @@ -3,6 +3,6 @@ "flags": { "showPasswordless": true, "accountSwitching": false, - "sdk": false + "sdk": true } } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 5875490ff0..ec1842e105 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -732,6 +732,7 @@ export default class MainBackground { sdkClientFactory, this.environmentService, this.platformUtilsService, + this.apiService, ); this.passwordStrengthService = new PasswordStrengthService(); @@ -1330,6 +1331,20 @@ export default class MainBackground { await this.initOverlayAndTabsBackground(); + if (flagEnabled("sdk")) { + // Warn if the SDK for some reason can't be initialized + let supported = false; + try { + supported = await firstValueFrom(this.sdkService.supported$); + } catch (e) { + // Do nothing. + } + + if (!supported) { + this.sdkService.failedToInitialize().catch(this.logService.error); + } + } + return new Promise((resolve) => { setTimeout(async () => { await this.refreshBadge(); diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts index 2293a3b384..aa8bfe61c2 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts @@ -28,6 +28,11 @@ if (supported) { import("./fallback"); } +/** + * SDK client factory with a js fallback for when WASM is not supported. + * + * Works both in popup and service worker. + */ export class BrowserSdkClientFactory implements SdkClientFactory { async createSdkClient( ...args: ConstructorParameters diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 12d5b109c2..113cd736c6 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, inject } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; -import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap } from "rxjs"; +import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap, catchError, of } from "rxjs"; import { LogoutReason } from "@bitwarden/auth/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -8,7 +9,9 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { MessageListener } from "@bitwarden/common/platform/messaging"; import { UserId } from "@bitwarden/common/types/guid"; @@ -20,6 +23,7 @@ import { ToastService, } from "@bitwarden/components"; +import { flagEnabled } from "../platform/flags"; import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service"; import { initPopupClosedListener } from "../platform/services/popup-view-cache-background.service"; import { BrowserSendStateService } from "../tools/popup/services/browser-send-state.service"; @@ -62,7 +66,28 @@ export class AppComponent implements OnInit, OnDestroy { private toastService: ToastService, private accountService: AccountService, private animationControlService: AnimationControlService, - ) {} + private logService: LogService, + private sdkService: SdkService, + ) { + if (flagEnabled("sdk")) { + // Warn if the SDK for some reason can't be initialized + this.sdkService.supported$ + .pipe( + takeUntilDestroyed(), + catchError(() => { + return of(false); + }), + ) + .subscribe((supported) => { + if (!supported) { + this.logService.debug("SDK is not supported"); + this.sdkService.failedToInitialize().catch(this.logService.error); + } else { + this.logService.debug("SDK is supported"); + } + }); + } + } async ngOnInit() { initPopupClosedListener(); diff --git a/apps/cli/config/base.json b/apps/cli/config/base.json index 67f2323e94..ccf867c0dc 100644 --- a/apps/cli/config/base.json +++ b/apps/cli/config/base.json @@ -1,5 +1,5 @@ { "flags": { - "sdk": false + "sdk": true } } diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 95aa5f98b0..a249a4d3f3 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -535,6 +535,7 @@ export class ServiceContainer { sdkClientFactory, this.environmentService, this.platformUtilsService, + this.apiService, customUserAgent, ); @@ -846,5 +847,19 @@ export class ServiceContainer { } this.inited = true; + + if (flagEnabled("sdk")) { + // Warn if the SDK for some reason can't be initialized + let supported = false; + try { + supported = await firstValueFrom(this.sdkService.supported$); + } catch (e) { + // Do nothing. + } + + if (!supported) { + this.sdkService.failedToInitialize().catch(this.logService.error); + } + } } } diff --git a/apps/desktop/config/base.json b/apps/desktop/config/base.json index 5efcbc629c..5d045326d4 100644 --- a/apps/desktop/config/base.json +++ b/apps/desktop/config/base.json @@ -1,6 +1,6 @@ { "flags": { - "sdk": false + "sdk": true }, "devFlags": {} } diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 9678f65723..61da12998d 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -8,8 +8,9 @@ import { ViewChild, ViewContainerRef, } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router } from "@angular/router"; -import { filter, firstValueFrom, map, Subject, takeUntil, timeout } from "rxjs"; +import { catchError, filter, firstValueFrom, map, of, Subject, takeUntil, timeout } from "rxjs"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; import { ModalService } from "@bitwarden/angular/services/modal.service"; @@ -21,7 +22,6 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; @@ -38,6 +38,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic 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 { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SystemService } from "@bitwarden/common/platform/abstractions/system.service"; import { clearCaches } from "@bitwarden/common/platform/misc/sequentialize"; @@ -56,6 +57,7 @@ import { BiometricStateService } from "@bitwarden/key-management"; import { DeleteAccountComponent } from "../auth/delete-account.component"; import { LoginApprovalComponent } from "../auth/login/login-approval.component"; import { MenuAccount, MenuUpdateRequest } from "../main/menu/menu.updater"; +import { flagEnabled } from "../platform/flags"; import { PremiumComponent } from "../vault/app/accounts/premium.component"; import { FolderAddEditComponent } from "../vault/app/vault/folder-add-edit.component"; @@ -150,9 +152,28 @@ export class AppComponent implements OnInit, OnDestroy { private dialogService: DialogService, private biometricStateService: BiometricStateService, private stateEventRunnerService: StateEventRunnerService, - private providerService: ProviderService, private accountService: AccountService, - ) {} + private sdkService: SdkService, + ) { + if (flagEnabled("sdk")) { + // Warn if the SDK for some reason can't be initialized + this.sdkService.supported$ + .pipe( + takeUntilDestroyed(), + catchError(() => { + return of(false); + }), + ) + .subscribe((supported) => { + if (!supported) { + this.logService.debug("SDK is not supported"); + this.sdkService.failedToInitialize().catch(this.logService.error); + } else { + this.logService.debug("SDK is supported"); + } + }); + } + } ngOnInit() { this.accountService.activeAccount$.pipe(takeUntil(this.destroy$)).subscribe((account) => { diff --git a/apps/web/config/base.json b/apps/web/config/base.json index 98f91360bc..cfaf604fb0 100644 --- a/apps/web/config/base.json +++ b/apps/web/config/base.json @@ -12,7 +12,7 @@ }, "flags": { "showPasswordless": false, - "sdk": false + "sdk": true }, "devFlags": {} } diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 578bc9111c..7299c8ece2 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -1,8 +1,9 @@ import { DOCUMENT } from "@angular/common"; import { Component, Inject, NgZone, OnDestroy, OnInit } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { NavigationEnd, Router } from "@angular/router"; import * as jq from "jquery"; -import { Subject, filter, firstValueFrom, map, takeUntil, timeout } from "rxjs"; +import { Subject, filter, firstValueFrom, map, takeUntil, timeout, catchError, of } from "rxjs"; import { LogoutReason } from "@bitwarden/auth/common"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; @@ -19,7 +20,9 @@ import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broa import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateEventRunnerService } from "@bitwarden/common/platform/state"; import { SyncService } from "@bitwarden/common/platform/sync"; @@ -30,6 +33,8 @@ import { DialogService, ToastOptions, ToastService } from "@bitwarden/components import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { BiometricStateService } from "@bitwarden/key-management"; +import { flagEnabled } from "../utils/flags"; + import { PolicyListService } from "./admin-console/core/policy-list.service"; import { DisableSendPolicy, @@ -85,7 +90,28 @@ export class AppComponent implements OnDestroy, OnInit { private stateEventRunnerService: StateEventRunnerService, private organizationService: InternalOrganizationServiceAbstraction, private accountService: AccountService, - ) {} + private logService: LogService, + private sdkService: SdkService, + ) { + if (flagEnabled("sdk")) { + // Warn if the SDK for some reason can't be initialized + this.sdkService.supported$ + .pipe( + takeUntilDestroyed(), + catchError(() => { + return of(false); + }), + ) + .subscribe((supported) => { + if (!supported) { + this.logService.debug("SDK is not supported"); + this.sdkService.failedToInitialize().catch(this.logService.error); + } else { + this.logService.debug("SDK is supported"); + } + }); + } + } ngOnInit() { this.i18nService.locale$.pipe(takeUntil(this.destroy$)).subscribe((locale) => { diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index c8186e1e90..41b24a4a54 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1330,7 +1330,12 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: SdkService, useClass: DefaultSdkService, - deps: [SdkClientFactory, EnvironmentService, PlatformUtilsServiceAbstraction], + deps: [ + SdkClientFactory, + EnvironmentService, + PlatformUtilsServiceAbstraction, + ApiServiceAbstraction, + ], }), ]; diff --git a/libs/common/src/platform/abstractions/sdk/sdk.service.ts b/libs/common/src/platform/abstractions/sdk/sdk.service.ts index 5899856e5f..360f2e91a7 100644 --- a/libs/common/src/platform/abstractions/sdk/sdk.service.ts +++ b/libs/common/src/platform/abstractions/sdk/sdk.service.ts @@ -5,4 +5,6 @@ import { BitwardenClient } from "@bitwarden/sdk-internal"; export abstract class SdkService { client$: Observable; supported$: Observable; + + abstract failedToInitialize(): Promise; } diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index 0240ebc94b..d4a9cfeb7e 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -1,7 +1,8 @@ -import { concatMap, shareReplay } from "rxjs"; +import { concatMap, firstValueFrom, shareReplay } from "rxjs"; import { LogLevel, DeviceType as SdkDeviceType } from "@bitwarden/sdk-internal"; +import { ApiService } from "../../../abstractions/api.service"; import { DeviceType } from "../../../enums/device-type.enum"; import { EnvironmentService } from "../../abstractions/environment.service"; import { PlatformUtilsService } from "../../abstractions/platform-utils.service"; @@ -33,9 +34,24 @@ export class DefaultSdkService implements SdkService { private sdkClientFactory: SdkClientFactory, private environmentService: EnvironmentService, private platformUtilsService: PlatformUtilsService, + private apiService: ApiService, // Yes we shouldn't import ApiService, but it's temporary private userAgent: string = null, ) {} + async failedToInitialize(): Promise { + // Only log on cloud instances + if ( + this.platformUtilsService.isDev() || + !(await firstValueFrom(this.environmentService.environment$)).isCloud + ) { + return; + } + + return this.apiService.send("POST", "/wasm-debug", null, false, false, null, (headers) => { + headers.append("SDK-Version", "1.0.0"); + }); + } + private toDevice(device: DeviceType): SdkDeviceType { switch (device) { case DeviceType.Android: