From b14bb92d78526d417e083e233d48cc784e3792c8 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 16 May 2024 00:09:24 +1000 Subject: [PATCH] [AC-2579] Set up bit-cli folder (#9092) * Create bit-cli folder with configs * Add bit-cli to workspace * Refactor CLI app structure * services are managed by the ServiceContainer * programs are registered by register(Oss|Bit)Program * the app is bootstrapped by Main * Reapply changes from #9099 * Reapply changes from #8604 * Reapply changes from #9115 --- apps/cli/package.json | 12 + apps/cli/src/bw.ts | 789 +----------------- apps/cli/src/commands/serve.command.ts | 177 ++-- apps/cli/src/program.ts | 153 ++-- apps/cli/src/register-oss-programs.ts | 22 + apps/cli/src/service-container.spec.ts | 7 + apps/cli/src/service-container.ts | 772 +++++++++++++++++ apps/cli/src/tools/send/send.program.ts | 88 +- apps/cli/src/vault.program.ts | 99 ++- bitwarden_license/bit-cli/.eslintrc.json | 5 + bitwarden_license/bit-cli/jest.config.js | 16 + bitwarden_license/bit-cli/src/bw.spec.ts | 5 + bitwarden_license/bit-cli/src/bw.ts | 20 + .../bit-cli/src/register-bit-programs.ts | 10 + .../bit-cli/src/service-container.spec.ts | 7 + .../bit-cli/src/service-container.ts | 7 + bitwarden_license/bit-cli/tsconfig.json | 28 + bitwarden_license/bit-cli/tsconfig.spec.json | 4 + bitwarden_license/bit-cli/webpack.config.js | 12 + clients.code-workspace | 4 + jest.config.js | 1 + 21 files changed, 1200 insertions(+), 1038 deletions(-) create mode 100644 apps/cli/src/register-oss-programs.ts create mode 100644 apps/cli/src/service-container.spec.ts create mode 100644 apps/cli/src/service-container.ts create mode 100644 bitwarden_license/bit-cli/.eslintrc.json create mode 100644 bitwarden_license/bit-cli/jest.config.js create mode 100644 bitwarden_license/bit-cli/src/bw.spec.ts create mode 100644 bitwarden_license/bit-cli/src/bw.ts create mode 100644 bitwarden_license/bit-cli/src/register-bit-programs.ts create mode 100644 bitwarden_license/bit-cli/src/service-container.spec.ts create mode 100644 bitwarden_license/bit-cli/src/service-container.ts create mode 100644 bitwarden_license/bit-cli/tsconfig.json create mode 100644 bitwarden_license/bit-cli/tsconfig.spec.json create mode 100644 bitwarden_license/bit-cli/webpack.config.js diff --git a/apps/cli/package.json b/apps/cli/package.json index c427947bd3..4d1636c51a 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -33,6 +33,18 @@ "dist:mac": "npm run build:prod && npm run clean && npm run package:mac", "dist:lin": "npm run build:prod && npm run clean && npm run package:lin", "publish:npm": "npm run build:prod && npm publish --access public", + "build:bit": "webpack -c ../../bitwarden_license/bit-cli/webpack.config.js", + "build:bit:debug": "npm run build:bit && node --inspect ./build/bw.js", + "build:bit:watch": "webpack --watch -c ../../bitwarden_license/bit-cli/webpack.config.js", + "build:bit:prod": "cross-env NODE_ENV=production npm run build:bit", + "build:bit:prod:watch": "cross-env NODE_ENV=production npm run build:bit:watch", + "dist:bit": "npm run build:bit:prod && npm run clean && npm run package", + "dist:bit:win": "npm run build:bit:prod && npm run clean && npm run package:bit:win", + "dist:bit:mac": "npm run build:bit:prod && npm run clean && npm run package:bit:mac", + "dist:bit:lin": "npm run build:bit:prod && npm run clean && npm run package:bit:lin", + "package:bit:win": "pkg . --targets win-x64 --output ./dist/bit/windows/bw.exe", + "package:bit:mac": "pkg . --targets macos-x64 --output ./dist/bit/macos/bw", + "package:bit:lin": "pkg . --targets linux-x64 --output ./dist/bit/linux/bw", "test": "jest", "test:watch": "jest --watch", "test:watch:all": "jest --watchAll" diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index 31de198194..03ebaa7368 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -1,788 +1,17 @@ -import * as fs from "fs"; -import * as path from "path"; - import { program } from "commander"; -import * as jsdom from "jsdom"; -import { firstValueFrom } from "rxjs"; -import { - InternalUserDecryptionOptionsServiceAbstraction, - AuthRequestService, - LoginStrategyService, - LoginStrategyServiceAbstraction, - PinService, - PinServiceAbstraction, - UserDecryptionOptionsService, -} from "@bitwarden/auth/common"; -import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; -import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; -import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; -import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; -import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service"; -import { OrganizationUserServiceImplementation } from "@bitwarden/common/admin-console/services/organization-user/organization-user.service.implementation"; -import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; -import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; -import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service"; -import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; -import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; -import { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; -import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; -import { AuthService } from "@bitwarden/common/auth/services/auth.service"; -import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; -import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation"; -import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; -import { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service"; -import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; -import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service"; -import { TokenService } from "@bitwarden/common/auth/services/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service"; -import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; -import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; -import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; -import { - DefaultDomainSettingsService, - DomainSettingsService, -} from "@bitwarden/common/autofill/services/domain-settings.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; -import { ClientType } from "@bitwarden/common/enums"; -import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; -import { - BiometricStateService, - DefaultBiometricStateService, -} from "@bitwarden/common/platform/biometrics/biometric-state.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"; -import { Account } from "@bitwarden/common/platform/models/domain/account"; -import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; -import { AppIdService } from "@bitwarden/common/platform/services/app-id.service"; -import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; -import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service"; -import { ContainerService } from "@bitwarden/common/platform/services/container.service"; -import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; -import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; -import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service"; -import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.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"; -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"; -import { - ActiveUserStateProvider, - DerivedStateProvider, - GlobalStateProvider, - SingleUserStateProvider, - StateEventRunnerService, - StateProvider, -} from "@bitwarden/common/platform/state"; -/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally these should not be accessed */ -import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider"; -import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; -import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider"; -import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider"; -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"; -/* eslint-enable import/no-restricted-paths */ -import { AuditService } from "@bitwarden/common/services/audit.service"; -import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; -import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; -import { SearchService } from "@bitwarden/common/services/search.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service"; -import { - PasswordGenerationService, - PasswordGenerationServiceAbstraction, -} from "@bitwarden/common/tools/generator/password"; -import { - PasswordStrengthService, - PasswordStrengthServiceAbstraction, -} from "@bitwarden/common/tools/password-strength"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service"; -import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service"; -import { UserId } from "@bitwarden/common/types/guid"; -import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type"; -import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; -import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; -import { CollectionService } from "@bitwarden/common/vault/services/collection.service"; -import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service"; -import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; -import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service"; -import { SyncNotifierService } from "@bitwarden/common/vault/services/sync/sync-notifier.service"; -import { SyncService } from "@bitwarden/common/vault/services/sync/sync.service"; -import { TotpService } from "@bitwarden/common/vault/services/totp.service"; -import { - ImportApiService, - ImportApiServiceAbstraction, - ImportService, - ImportServiceAbstraction, -} from "@bitwarden/importer/core"; -import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; -import { - IndividualVaultExportService, - IndividualVaultExportServiceAbstraction, - OrganizationVaultExportService, - OrganizationVaultExportServiceAbstraction, - VaultExportService, - VaultExportServiceAbstraction, -} from "@bitwarden/vault-export-core"; +import { registerOssPrograms } from "./register-oss-programs"; +import { ServiceContainer } from "./service-container"; -import { CliPlatformUtilsService } from "./platform/services/cli-platform-utils.service"; -import { ConsoleLogService } from "./platform/services/console-log.service"; -import { I18nService } from "./platform/services/i18n.service"; -import { LowdbStorageService } from "./platform/services/lowdb-storage.service"; -import { NodeApiService } from "./platform/services/node-api.service"; -import { NodeEnvSecureStorageService } from "./platform/services/node-env-secure-storage.service"; -import { Program } from "./program"; -import { SendProgram } from "./tools/send/send.program"; -import { VaultProgram } from "./vault.program"; +async function main() { + const serviceContainer = new ServiceContainer(); + await serviceContainer.init(); -// Polyfills -global.DOMParser = new jsdom.JSDOM().window.DOMParser; + await registerOssPrograms(serviceContainer); -// eslint-disable-next-line -const packageJson = require("../package.json"); - -export class Main { - messagingService: MessageSender; - storageService: LowdbStorageService; - secureStorageService: NodeEnvSecureStorageService; - memoryStorageService: MemoryStorageService; - memoryStorageForStateProviders: MemoryStorageServiceForStateProviders; - i18nService: I18nService; - platformUtilsService: CliPlatformUtilsService; - cryptoService: CryptoService; - tokenService: TokenService; - appIdService: AppIdService; - apiService: NodeApiService; - environmentService: EnvironmentService; - cipherService: CipherService; - folderService: InternalFolderService; - organizationUserService: OrganizationUserService; - collectionService: CollectionService; - vaultTimeoutService: VaultTimeoutService; - masterPasswordService: InternalMasterPasswordServiceAbstraction; - vaultTimeoutSettingsService: VaultTimeoutSettingsService; - syncService: SyncService; - eventCollectionService: EventCollectionServiceAbstraction; - eventUploadService: EventUploadServiceAbstraction; - passwordGenerationService: PasswordGenerationServiceAbstraction; - passwordStrengthService: PasswordStrengthServiceAbstraction; - userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction; - totpService: TotpService; - containerService: ContainerService; - auditService: AuditService; - importService: ImportServiceAbstraction; - importApiService: ImportApiServiceAbstraction; - exportService: VaultExportServiceAbstraction; - individualExportService: IndividualVaultExportServiceAbstraction; - organizationExportService: OrganizationVaultExportServiceAbstraction; - searchService: SearchService; - keyGenerationService: KeyGenerationServiceAbstraction; - cryptoFunctionService: NodeCryptoFunctionService; - encryptService: EncryptServiceImplementation; - authService: AuthService; - policyService: PolicyService; - policyApiService: PolicyApiServiceAbstraction; - program: Program; - vaultProgram: VaultProgram; - sendProgram: SendProgram; - logService: ConsoleLogService; - sendService: SendService; - sendStateProvider: SendStateProvider; - fileUploadService: FileUploadService; - cipherFileUploadService: CipherFileUploadService; - keyConnectorService: KeyConnectorService; - userVerificationService: UserVerificationService; - pinService: PinServiceAbstraction; - stateService: StateService; - autofillSettingsService: AutofillSettingsServiceAbstraction; - domainSettingsService: DomainSettingsService; - organizationService: OrganizationService; - providerService: ProviderService; - twoFactorService: TwoFactorService; - folderApiService: FolderApiService; - userVerificationApiService: UserVerificationApiService; - organizationApiService: OrganizationApiServiceAbstraction; - syncNotifierService: SyncNotifierService; - sendApiService: SendApiService; - devicesApiService: DevicesApiServiceAbstraction; - deviceTrustService: DeviceTrustServiceAbstraction; - authRequestService: AuthRequestService; - configApiService: ConfigApiServiceAbstraction; - configService: ConfigService; - accountService: AccountService; - globalStateProvider: GlobalStateProvider; - singleUserStateProvider: SingleUserStateProvider; - activeUserStateProvider: ActiveUserStateProvider; - derivedStateProvider: DerivedStateProvider; - stateProvider: StateProvider; - loginStrategyService: LoginStrategyServiceAbstraction; - avatarService: AvatarServiceAbstraction; - stateEventRunnerService: StateEventRunnerService; - biometricStateService: BiometricStateService; - billingAccountProfileStateService: BillingAccountProfileStateService; - providerApiService: ProviderApiServiceAbstraction; - userAutoUnlockKeyService: UserAutoUnlockKeyService; - kdfConfigService: KdfConfigServiceAbstraction; - - constructor() { - let p = null; - const relativeDataDir = path.join(path.dirname(process.execPath), "bw-data"); - if (fs.existsSync(relativeDataDir)) { - p = relativeDataDir; - } else if (process.env.BITWARDENCLI_APPDATA_DIR) { - p = path.resolve(process.env.BITWARDENCLI_APPDATA_DIR); - } else if (process.platform === "darwin") { - p = path.join(process.env.HOME, "Library/Application Support/Bitwarden CLI"); - } else if (process.platform === "win32") { - p = path.join(process.env.APPDATA, "Bitwarden CLI"); - } else if (process.env.XDG_CONFIG_HOME) { - p = path.join(process.env.XDG_CONFIG_HOME, "Bitwarden CLI"); - } else { - p = path.join(process.env.HOME, ".config/Bitwarden CLI"); - } - - this.platformUtilsService = new CliPlatformUtilsService(ClientType.Cli, packageJson); - this.logService = new ConsoleLogService( - this.platformUtilsService.isDev(), - (level) => process.env.BITWARDENCLI_DEBUG !== "true" && level <= LogLevelType.Info, - ); - this.cryptoFunctionService = new NodeCryptoFunctionService(); - this.encryptService = new EncryptServiceImplementation( - this.cryptoFunctionService, - this.logService, - true, - ); - this.storageService = new LowdbStorageService(this.logService, null, p, false, true); - this.secureStorageService = new NodeEnvSecureStorageService( - this.storageService, - this.logService, - this.encryptService, - ); - - this.memoryStorageService = new MemoryStorageService(); - this.memoryStorageForStateProviders = new MemoryStorageServiceForStateProviders(); - - const storageServiceProvider = new StorageServiceProvider( - this.storageService, - this.memoryStorageForStateProviders, - ); - - this.globalStateProvider = new DefaultGlobalStateProvider(storageServiceProvider); - - const stateEventRegistrarService = new StateEventRegistrarService( - this.globalStateProvider, - storageServiceProvider, - ); - - this.stateEventRunnerService = new StateEventRunnerService( - this.globalStateProvider, - storageServiceProvider, - ); - - this.i18nService = new I18nService("en", "./locales", this.globalStateProvider); - - this.singleUserStateProvider = new DefaultSingleUserStateProvider( - storageServiceProvider, - stateEventRegistrarService, - ); - - this.messagingService = MessageSender.EMPTY; - - this.accountService = new AccountServiceImplementation( - this.messagingService, - this.logService, - this.globalStateProvider, - ); - - this.activeUserStateProvider = new DefaultActiveUserStateProvider( - this.accountService, - this.singleUserStateProvider, - ); - - this.derivedStateProvider = new DefaultDerivedStateProvider(); - - this.stateProvider = new DefaultStateProvider( - this.activeUserStateProvider, - this.singleUserStateProvider, - this.globalStateProvider, - this.derivedStateProvider, - ); - - this.environmentService = new DefaultEnvironmentService( - this.stateProvider, - this.accountService, - ); - - this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService); - - this.tokenService = new TokenService( - this.singleUserStateProvider, - this.globalStateProvider, - this.platformUtilsService.supportsSecureStorage(), - this.secureStorageService, - this.keyGenerationService, - this.encryptService, - this.logService, - ); - - const migrationRunner = new MigrationRunner( - this.storageService, - this.logService, - new MigrationBuilderService(), - ClientType.Cli, - ); - - this.stateService = new StateService( - this.storageService, - this.secureStorageService, - this.memoryStorageService, - this.logService, - new StateFactory(GlobalState, Account), - this.accountService, - this.environmentService, - this.tokenService, - migrationRunner, - ); - - this.masterPasswordService = new MasterPasswordService( - this.stateProvider, - this.stateService, - this.keyGenerationService, - this.encryptService, - ); - - this.kdfConfigService = new KdfConfigService(this.stateProvider); - - this.pinService = new PinService( - this.accountService, - this.cryptoFunctionService, - this.encryptService, - this.kdfConfigService, - this.keyGenerationService, - this.logService, - this.masterPasswordService, - this.stateProvider, - this.stateService, - ); - - this.cryptoService = new CryptoService( - this.pinService, - this.masterPasswordService, - this.keyGenerationService, - this.cryptoFunctionService, - this.encryptService, - this.platformUtilsService, - this.logService, - this.stateService, - this.accountService, - this.stateProvider, - this.kdfConfigService, - ); - - this.appIdService = new AppIdService(this.globalStateProvider); - - const customUserAgent = - "Bitwarden_CLI/" + - this.platformUtilsService.getApplicationVersionSync() + - " (" + - this.platformUtilsService.getDeviceString().toUpperCase() + - ")"; - - this.biometricStateService = new DefaultBiometricStateService(this.stateProvider); - this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider); - - this.organizationService = new OrganizationService(this.stateProvider); - this.policyService = new PolicyService(this.stateProvider, this.organizationService); - - this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService( - this.accountService, - this.pinService, - this.userDecryptionOptionsService, - this.cryptoService, - this.tokenService, - this.policyService, - this.biometricStateService, - this.stateProvider, - this.logService, - VaultTimeoutStringType.Never, // default vault timeout - ); - - this.apiService = new NodeApiService( - this.tokenService, - this.platformUtilsService, - this.environmentService, - this.appIdService, - this.vaultTimeoutSettingsService, - async (expired: boolean) => await this.logout(), - customUserAgent, - ); - - this.syncNotifierService = new SyncNotifierService(); - - this.organizationApiService = new OrganizationApiService(this.apiService, this.syncService); - - this.containerService = new ContainerService(this.cryptoService, this.encryptService); - - this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider); - - this.fileUploadService = new FileUploadService(this.logService); - - this.sendStateProvider = new SendStateProvider(this.stateProvider); - - this.sendService = new SendService( - this.cryptoService, - this.i18nService, - this.keyGenerationService, - this.sendStateProvider, - this.encryptService, - ); - - this.cipherFileUploadService = new CipherFileUploadService( - this.apiService, - this.fileUploadService, - ); - - this.sendApiService = this.sendApiService = new SendApiService( - this.apiService, - this.fileUploadService, - this.sendService, - ); - - this.searchService = new SearchService(this.logService, this.i18nService, this.stateProvider); - - this.collectionService = new CollectionService( - this.cryptoService, - this.i18nService, - this.stateProvider, - ); - - this.providerService = new ProviderService(this.stateProvider); - - this.organizationUserService = new OrganizationUserServiceImplementation(this.apiService); - - this.policyApiService = new PolicyApiService(this.policyService, this.apiService); - - this.keyConnectorService = new KeyConnectorService( - this.accountService, - this.masterPasswordService, - this.cryptoService, - this.apiService, - this.tokenService, - this.logService, - this.organizationService, - this.keyGenerationService, - async (expired: boolean) => await this.logout(), - this.stateProvider, - ); - - this.twoFactorService = new TwoFactorService( - this.i18nService, - this.platformUtilsService, - this.globalStateProvider, - ); - - this.passwordStrengthService = new PasswordStrengthService(); - - this.passwordGenerationService = new PasswordGenerationService( - this.cryptoService, - this.policyService, - this.stateService, - ); - - this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); - this.deviceTrustService = new DeviceTrustService( - this.keyGenerationService, - this.cryptoFunctionService, - this.cryptoService, - this.encryptService, - this.appIdService, - this.devicesApiService, - this.i18nService, - this.platformUtilsService, - this.stateProvider, - this.secureStorageService, - this.userDecryptionOptionsService, - this.logService, - ); - - this.authRequestService = new AuthRequestService( - this.appIdService, - this.accountService, - this.masterPasswordService, - this.cryptoService, - this.apiService, - this.stateProvider, - ); - - this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService( - this.stateProvider, - ); - - this.loginStrategyService = new LoginStrategyService( - this.accountService, - this.masterPasswordService, - this.cryptoService, - this.apiService, - this.tokenService, - this.appIdService, - this.platformUtilsService, - this.messagingService, - this.logService, - this.keyConnectorService, - this.environmentService, - this.stateService, - this.twoFactorService, - this.i18nService, - this.encryptService, - this.passwordStrengthService, - this.policyService, - this.deviceTrustService, - this.authRequestService, - this.userDecryptionOptionsService, - this.globalStateProvider, - this.billingAccountProfileStateService, - this.vaultTimeoutSettingsService, - this.kdfConfigService, - ); - - this.authService = new AuthService( - this.accountService, - this.messagingService, - this.cryptoService, - this.apiService, - this.stateService, - this.tokenService, - ); - - this.configApiService = new ConfigApiService(this.apiService, this.tokenService); - - this.configService = new DefaultConfigService( - this.configApiService, - this.environmentService, - this.logService, - this.stateProvider, - ); - - this.cipherService = new CipherService( - this.cryptoService, - this.domainSettingsService, - this.apiService, - this.i18nService, - this.searchService, - this.stateService, - this.autofillSettingsService, - this.encryptService, - this.cipherFileUploadService, - this.configService, - this.stateProvider, - ); - - this.folderService = new FolderService( - this.cryptoService, - this.i18nService, - this.cipherService, - this.stateProvider, - ); - - this.folderApiService = new FolderApiService(this.folderService, this.apiService); - - const lockedCallback = async (userId?: string) => - await this.cryptoService.clearStoredUserKey(KeySuffixOptions.Auto); - - this.userVerificationService = new UserVerificationService( - this.stateService, - this.cryptoService, - this.accountService, - this.masterPasswordService, - this.i18nService, - this.userVerificationApiService, - this.userDecryptionOptionsService, - this.pinService, - this.logService, - this.vaultTimeoutSettingsService, - this.platformUtilsService, - this.kdfConfigService, - ); - - this.vaultTimeoutService = new VaultTimeoutService( - this.accountService, - this.masterPasswordService, - this.cipherService, - this.folderService, - this.collectionService, - this.platformUtilsService, - this.messagingService, - this.searchService, - this.stateService, - this.authService, - this.vaultTimeoutSettingsService, - this.stateEventRunnerService, - lockedCallback, - null, - ); - - this.avatarService = new AvatarService(this.apiService, this.stateProvider); - - this.syncService = new SyncService( - this.masterPasswordService, - this.accountService, - this.apiService, - this.domainSettingsService, - this.folderService, - this.cipherService, - this.cryptoService, - this.collectionService, - this.messagingService, - this.policyService, - this.sendService, - this.logService, - this.keyConnectorService, - this.stateService, - this.providerService, - this.folderApiService, - this.organizationService, - this.sendApiService, - this.userDecryptionOptionsService, - this.avatarService, - async (expired: boolean) => await this.logout(), - this.billingAccountProfileStateService, - this.tokenService, - this.authService, - ); - - this.totpService = new TotpService(this.cryptoFunctionService, this.logService); - - this.importApiService = new ImportApiService(this.apiService); - - this.importService = new ImportService( - this.cipherService, - this.folderService, - this.importApiService, - this.i18nService, - this.collectionService, - this.cryptoService, - this.pinService, - ); - - this.individualExportService = new IndividualVaultExportService( - this.folderService, - this.cipherService, - this.pinService, - this.cryptoService, - this.cryptoFunctionService, - this.kdfConfigService, - ); - - this.organizationExportService = new OrganizationVaultExportService( - this.cipherService, - this.apiService, - this.pinService, - this.cryptoService, - this.cryptoFunctionService, - this.collectionService, - this.kdfConfigService, - ); - - this.exportService = new VaultExportService( - this.individualExportService, - this.organizationExportService, - ); - - this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.cryptoService); - - this.auditService = new AuditService(this.cryptoFunctionService, this.apiService); - this.program = new Program(this); - this.vaultProgram = new VaultProgram(this); - this.sendProgram = new SendProgram(this); - - this.userVerificationApiService = new UserVerificationApiService(this.apiService); - - this.eventUploadService = new EventUploadService( - this.apiService, - this.stateProvider, - this.logService, - this.authService, - ); - - this.eventCollectionService = new EventCollectionService( - this.cipherService, - this.stateProvider, - this.organizationService, - this.eventUploadService, - this.authService, - ); - - this.providerApiService = new ProviderApiService(this.apiService); - } - - async run() { - await this.init(); - - await this.program.register(); - await this.vaultProgram.register(); - await this.sendProgram.register(); - - program.parse(process.argv); - - if (process.argv.slice(2).length === 0) { - program.outputHelp(); - } - } - - async logout() { - this.authService.logOut(() => { - /* Do nothing */ - }); - const userId = (await this.stateService.getUserId()) as UserId; - await Promise.all([ - this.eventUploadService.uploadEvents(userId as UserId), - this.syncService.setLastSync(new Date(0)), - this.cryptoService.clearKeys(), - this.cipherService.clear(userId), - this.folderService.clear(userId), - this.collectionService.clear(userId as UserId), - this.passwordGenerationService.clear(), - ]); - - await this.stateEventRunnerService.handleEvent("logout", userId); - - await this.stateService.clean(); - await this.accountService.clean(userId); - process.env.BW_SESSION = null; - } - - private async init() { - await this.storageService.init(); - await this.stateService.init(); - this.containerService.attachToGlobal(global); - await this.i18nService.init(); - this.twoFactorService.init(); - - const activeAccount = await firstValueFrom(this.accountService.activeAccount$); - if (activeAccount) { - await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(activeAccount.id); - } - } + program.parse(process.argv); } -const main = new Main(); -// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. +// Node does not support top-level await statements until ES2022, esnext, etc which we don't use yet // eslint-disable-next-line @typescript-eslint/no-floating-promises -main.run(); +main(); diff --git a/apps/cli/src/commands/serve.command.ts b/apps/cli/src/commands/serve.command.ts index 7a11dc4b4a..aad205998f 100644 --- a/apps/cli/src/commands/serve.command.ts +++ b/apps/cli/src/commands/serve.command.ts @@ -11,9 +11,9 @@ import { ConfirmCommand } from "../admin-console/commands/confirm.command"; import { ShareCommand } from "../admin-console/commands/share.command"; import { LockCommand } from "../auth/commands/lock.command"; import { UnlockCommand } from "../auth/commands/unlock.command"; -import { Main } from "../bw"; import { Response } from "../models/response"; import { FileResponse } from "../models/response/file.response"; +import { ServiceContainer } from "../service-container"; import { GenerateCommand } from "../tools/generate.command"; import { SendEditCommand, @@ -55,116 +55,119 @@ export class ServeCommand { private sendListCommand: SendListCommand; private sendRemovePasswordCommand: SendRemovePasswordCommand; - constructor(protected main: Main) { + constructor(protected serviceContainer: ServiceContainer) { this.getCommand = new GetCommand( - this.main.cipherService, - this.main.folderService, - this.main.collectionService, - this.main.totpService, - this.main.auditService, - this.main.cryptoService, - this.main.stateService, - this.main.searchService, - this.main.apiService, - this.main.organizationService, - this.main.eventCollectionService, - this.main.billingAccountProfileStateService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.collectionService, + this.serviceContainer.totpService, + this.serviceContainer.auditService, + this.serviceContainer.cryptoService, + this.serviceContainer.stateService, + this.serviceContainer.searchService, + this.serviceContainer.apiService, + this.serviceContainer.organizationService, + this.serviceContainer.eventCollectionService, + this.serviceContainer.billingAccountProfileStateService, ); this.listCommand = new ListCommand( - this.main.cipherService, - this.main.folderService, - this.main.collectionService, - this.main.organizationService, - this.main.searchService, - this.main.organizationUserService, - this.main.apiService, - this.main.eventCollectionService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.collectionService, + this.serviceContainer.organizationService, + this.serviceContainer.searchService, + this.serviceContainer.organizationUserService, + this.serviceContainer.apiService, + this.serviceContainer.eventCollectionService, ); this.createCommand = new CreateCommand( - this.main.cipherService, - this.main.folderService, - this.main.cryptoService, - this.main.apiService, - this.main.folderApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.cryptoService, + this.serviceContainer.apiService, + this.serviceContainer.folderApiService, + this.serviceContainer.billingAccountProfileStateService, ); this.editCommand = new EditCommand( - this.main.cipherService, - this.main.folderService, - this.main.cryptoService, - this.main.apiService, - this.main.folderApiService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.cryptoService, + this.serviceContainer.apiService, + this.serviceContainer.folderApiService, ); this.generateCommand = new GenerateCommand( - this.main.passwordGenerationService, - this.main.stateService, + this.serviceContainer.passwordGenerationService, + this.serviceContainer.stateService, ); - this.syncCommand = new SyncCommand(this.main.syncService); + this.syncCommand = new SyncCommand(this.serviceContainer.syncService); this.statusCommand = new StatusCommand( - this.main.environmentService, - this.main.syncService, - this.main.stateService, - this.main.authService, + this.serviceContainer.environmentService, + this.serviceContainer.syncService, + this.serviceContainer.stateService, + this.serviceContainer.authService, ); this.deleteCommand = new DeleteCommand( - this.main.cipherService, - this.main.folderService, - this.main.apiService, - this.main.folderApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.apiService, + this.serviceContainer.folderApiService, + this.serviceContainer.billingAccountProfileStateService, ); this.confirmCommand = new ConfirmCommand( - this.main.apiService, - this.main.cryptoService, - this.main.organizationUserService, + this.serviceContainer.apiService, + this.serviceContainer.cryptoService, + this.serviceContainer.organizationUserService, ); - this.restoreCommand = new RestoreCommand(this.main.cipherService); - this.shareCommand = new ShareCommand(this.main.cipherService); - this.lockCommand = new LockCommand(this.main.vaultTimeoutService); + this.restoreCommand = new RestoreCommand(this.serviceContainer.cipherService); + this.shareCommand = new ShareCommand(this.serviceContainer.cipherService); + this.lockCommand = new LockCommand(this.serviceContainer.vaultTimeoutService); this.unlockCommand = new UnlockCommand( - this.main.accountService, - this.main.masterPasswordService, - this.main.cryptoService, - this.main.stateService, - this.main.cryptoFunctionService, - this.main.apiService, - this.main.logService, - this.main.keyConnectorService, - this.main.environmentService, - this.main.syncService, - this.main.organizationApiService, - async () => await this.main.logout(), - this.main.kdfConfigService, + this.serviceContainer.accountService, + this.serviceContainer.masterPasswordService, + this.serviceContainer.cryptoService, + this.serviceContainer.stateService, + this.serviceContainer.cryptoFunctionService, + this.serviceContainer.apiService, + this.serviceContainer.logService, + this.serviceContainer.keyConnectorService, + this.serviceContainer.environmentService, + this.serviceContainer.syncService, + this.serviceContainer.organizationApiService, + async () => await this.serviceContainer.logout(), + this.serviceContainer.kdfConfigService, ); this.sendCreateCommand = new SendCreateCommand( - this.main.sendService, - this.main.environmentService, - this.main.sendApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.sendService, + this.serviceContainer.environmentService, + this.serviceContainer.sendApiService, + this.serviceContainer.billingAccountProfileStateService, + ); + this.sendDeleteCommand = new SendDeleteCommand( + this.serviceContainer.sendService, + this.serviceContainer.sendApiService, ); - this.sendDeleteCommand = new SendDeleteCommand(this.main.sendService, this.main.sendApiService); this.sendGetCommand = new SendGetCommand( - this.main.sendService, - this.main.environmentService, - this.main.searchService, - this.main.cryptoService, + this.serviceContainer.sendService, + this.serviceContainer.environmentService, + this.serviceContainer.searchService, + this.serviceContainer.cryptoService, ); this.sendEditCommand = new SendEditCommand( - this.main.sendService, + this.serviceContainer.sendService, this.sendGetCommand, - this.main.sendApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.sendApiService, + this.serviceContainer.billingAccountProfileStateService, ); this.sendListCommand = new SendListCommand( - this.main.sendService, - this.main.environmentService, - this.main.searchService, + this.serviceContainer.sendService, + this.serviceContainer.environmentService, + this.serviceContainer.searchService, ); this.sendRemovePasswordCommand = new SendRemovePasswordCommand( - this.main.sendService, - this.main.sendApiService, - this.main.environmentService, + this.serviceContainer.sendService, + this.serviceContainer.sendApiService, + this.serviceContainer.environmentService, ); } @@ -172,7 +175,7 @@ export class ServeCommand { const protectOrigin = !options.disableOriginProtection; const port = options.port || 8087; const hostname = options.hostname || "localhost"; - this.main.logService.info( + this.serviceContainer.logService.info( `Starting server on ${hostname}:${port} with ${ protectOrigin ? "origin protection" : "no origin protection" }`, @@ -187,7 +190,7 @@ export class ServeCommand { .use(async (ctx, next) => { if (protectOrigin && ctx.headers.origin != undefined) { ctx.status = 403; - this.main.logService.warning( + this.serviceContainer.logService.warning( `Blocking request from "${ Utils.isNullOrEmpty(ctx.headers.origin) ? "(Origin header value missing)" @@ -407,7 +410,7 @@ export class ServeCommand { .use(router.routes()) .use(router.allowedMethods()) .listen(port, hostname === "all" ? null : hostname, () => { - this.main.logService.info("Listening on " + hostname + ":" + port); + this.serviceContainer.logService.info("Listening on " + hostname + ":" + port); }); } @@ -426,12 +429,12 @@ export class ServeCommand { } private async errorIfLocked(res: koa.Response) { - const authed = await this.main.stateService.getIsAuthenticated(); + const authed = await this.serviceContainer.stateService.getIsAuthenticated(); if (!authed) { this.processResponse(res, Response.error("You are not logged in.")); return true; } - if (await this.main.cryptoService.hasUserKey()) { + if (await this.serviceContainer.cryptoService.hasUserKey()) { return false; } this.processResponse(res, Response.error("Vault is locked.")); diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index 9664b75776..3a2858aa81 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -8,7 +8,6 @@ import { LockCommand } from "./auth/commands/lock.command"; import { LoginCommand } from "./auth/commands/login.command"; import { LogoutCommand } from "./auth/commands/logout.command"; import { UnlockCommand } from "./auth/commands/unlock.command"; -import { Main } from "./bw"; import { CompletionCommand } from "./commands/completion.command"; import { ConfigCommand } from "./commands/config.command"; import { EncodeCommand } from "./commands/encode.command"; @@ -20,6 +19,7 @@ import { ListResponse } from "./models/response/list.response"; import { MessageResponse } from "./models/response/message.response"; import { StringResponse } from "./models/response/string.response"; import { TemplateResponse } from "./models/response/template.response"; +import { ServiceContainer } from "./service-container"; import { GenerateCommand } from "./tools/generate.command"; import { CliUtils } from "./utils"; import { SyncCommand } from "./vault/sync.command"; @@ -27,7 +27,7 @@ import { SyncCommand } from "./vault/sync.command"; const writeLn = CliUtils.writeLn; export class Program { - constructor(protected main: Main) {} + constructor(protected serviceContainer: ServiceContainer) {} async register() { program @@ -38,7 +38,10 @@ export class Program { .option("--quiet", "Don't return anything to stdout.") .option("--nointeraction", "Do not prompt for interactive user input.") .option("--session ", "Pass session key instead of reading from env.") - .version(await this.main.platformUtilsService.getApplicationVersion(), "-v, --version"); + .version( + await this.serviceContainer.platformUtilsService.getApplicationVersion(), + "-v, --version", + ); program.on("option:pretty", () => { process.env.BW_PRETTY = "true"; @@ -68,9 +71,11 @@ export class Program { process.env.BW_SESSION = key; // once we have the session key, we can set the user key in memory - const activeAccount = await firstValueFrom(this.main.accountService.activeAccount$); + const activeAccount = await firstValueFrom( + this.serviceContainer.accountService.activeAccount$, + ); if (activeAccount) { - await this.main.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet( + await this.serviceContainer.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet( activeAccount.id, ); } @@ -122,7 +127,7 @@ export class Program { "Path to a file containing your password as its first line", ) .option("--check", "Check login status.", async () => { - const authed = await this.main.stateService.getIsAuthenticated(); + const authed = await this.serviceContainer.stateService.getIsAuthenticated(); if (authed) { const res = new MessageResponse("You are logged in!", null); this.processResponse(Response.success(res), true); @@ -148,24 +153,24 @@ export class Program { if (!options.check) { await this.exitIfAuthed(); const command = new LoginCommand( - this.main.loginStrategyService, - this.main.authService, - this.main.apiService, - this.main.cryptoFunctionService, - this.main.environmentService, - this.main.passwordGenerationService, - this.main.passwordStrengthService, - this.main.platformUtilsService, - this.main.stateService, - this.main.cryptoService, - this.main.policyService, - this.main.twoFactorService, - this.main.syncService, - this.main.keyConnectorService, - this.main.policyApiService, - this.main.organizationService, - async () => await this.main.logout(), - this.main.kdfConfigService, + this.serviceContainer.loginStrategyService, + this.serviceContainer.authService, + this.serviceContainer.apiService, + this.serviceContainer.cryptoFunctionService, + this.serviceContainer.environmentService, + this.serviceContainer.passwordGenerationService, + this.serviceContainer.passwordStrengthService, + this.serviceContainer.platformUtilsService, + this.serviceContainer.stateService, + this.serviceContainer.cryptoService, + this.serviceContainer.policyService, + this.serviceContainer.twoFactorService, + this.serviceContainer.syncService, + this.serviceContainer.keyConnectorService, + this.serviceContainer.policyApiService, + this.serviceContainer.organizationService, + async () => await this.serviceContainer.logout(), + this.serviceContainer.kdfConfigService, ); const response = await command.run(email, password, options); this.processResponse(response, true); @@ -184,9 +189,9 @@ export class Program { .action(async (cmd) => { await this.exitIfNotAuthed(); const command = new LogoutCommand( - this.main.authService, - this.main.i18nService, - async () => await this.main.logout(), + this.serviceContainer.authService, + this.serviceContainer.i18nService, + async () => await this.serviceContainer.logout(), ); const response = await command.run(); this.processResponse(response); @@ -204,11 +209,11 @@ export class Program { .action(async (cmd) => { await this.exitIfNotAuthed(); - if (await this.main.keyConnectorService.getUsesKeyConnector()) { + if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector()) { const logoutCommand = new LogoutCommand( - this.main.authService, - this.main.i18nService, - async () => await this.main.logout(), + this.serviceContainer.authService, + this.serviceContainer.i18nService, + async () => await this.serviceContainer.logout(), ); await logoutCommand.run(); this.processResponse( @@ -221,7 +226,7 @@ export class Program { return; } - const command = new LockCommand(this.main.vaultTimeoutService); + const command = new LockCommand(this.serviceContainer.vaultTimeoutService); const response = await command.run(); this.processResponse(response); }); @@ -246,7 +251,7 @@ export class Program { .option("--check", "Check lock status.", async () => { await this.exitIfNotAuthed(); - const authStatus = await this.main.authService.getAuthStatus(); + const authStatus = await this.serviceContainer.authService.getAuthStatus(); if (authStatus === AuthenticationStatus.Unlocked) { const res = new MessageResponse("Vault is unlocked!", null); this.processResponse(Response.success(res), true); @@ -263,19 +268,19 @@ export class Program { if (!cmd.check) { await this.exitIfNotAuthed(); const command = new UnlockCommand( - this.main.accountService, - this.main.masterPasswordService, - this.main.cryptoService, - this.main.stateService, - this.main.cryptoFunctionService, - this.main.apiService, - this.main.logService, - this.main.keyConnectorService, - this.main.environmentService, - this.main.syncService, - this.main.organizationApiService, - async () => await this.main.logout(), - this.main.kdfConfigService, + this.serviceContainer.accountService, + this.serviceContainer.masterPasswordService, + this.serviceContainer.cryptoService, + this.serviceContainer.stateService, + this.serviceContainer.cryptoFunctionService, + this.serviceContainer.apiService, + this.serviceContainer.logService, + this.serviceContainer.keyConnectorService, + this.serviceContainer.environmentService, + this.serviceContainer.syncService, + this.serviceContainer.organizationApiService, + async () => await this.serviceContainer.logout(), + this.serviceContainer.kdfConfigService, ); const response = await command.run(password, cmd); this.processResponse(response); @@ -297,7 +302,7 @@ export class Program { }) .action(async (cmd) => { await this.exitIfNotAuthed(); - const command = new SyncCommand(this.main.syncService); + const command = new SyncCommand(this.serviceContainer.syncService); const response = await command.run(cmd); this.processResponse(response); }); @@ -340,8 +345,8 @@ export class Program { }) .action(async (options) => { const command = new GenerateCommand( - this.main.passwordGenerationService, - this.main.stateService, + this.serviceContainer.passwordGenerationService, + this.serviceContainer.stateService, ); const response = await command.run(options); this.processResponse(response); @@ -401,7 +406,7 @@ export class Program { writeLn("", true); }) .action(async (setting, value, options) => { - const command = new ConfigCommand(this.main.environmentService); + const command = new ConfigCommand(this.serviceContainer.environmentService); const response = await command.run(setting, value, options); this.processResponse(response); }); @@ -423,7 +428,7 @@ export class Program { writeLn("", true); }) .action(async () => { - const command = new UpdateCommand(this.main.platformUtilsService); + const command = new UpdateCommand(this.serviceContainer.platformUtilsService); const response = await command.run(); this.processResponse(response); }); @@ -474,10 +479,10 @@ export class Program { }) .action(async () => { const command = new StatusCommand( - this.main.environmentService, - this.main.syncService, - this.main.stateService, - this.main.authService, + this.serviceContainer.environmentService, + this.serviceContainer.syncService, + this.serviceContainer.stateService, + this.serviceContainer.authService, ); const response = await command.run(); this.processResponse(response); @@ -508,7 +513,7 @@ export class Program { }) .action(async (cmd) => { await this.exitIfNotAuthed(); - const command = new ServeCommand(this.main); + const command = new ServeCommand(this.serviceContainer); await command.run(cmd); }); } @@ -598,15 +603,15 @@ export class Program { } private async exitIfAuthed() { - const authed = await this.main.stateService.getIsAuthenticated(); + const authed = await this.serviceContainer.stateService.getIsAuthenticated(); if (authed) { - const email = await this.main.stateService.getEmail(); + const email = await this.serviceContainer.stateService.getEmail(); this.processResponse(Response.error("You are already logged in as " + email + "."), true); } } private async exitIfNotAuthed() { - const authed = await this.main.stateService.getIsAuthenticated(); + const authed = await this.serviceContainer.stateService.getIsAuthenticated(); if (!authed) { this.processResponse(Response.error("You are not logged in."), true); } @@ -614,11 +619,11 @@ export class Program { protected async exitIfLocked() { await this.exitIfNotAuthed(); - if (await this.main.cryptoService.hasUserKey()) { + if (await this.serviceContainer.cryptoService.hasUserKey()) { return; } else if (process.env.BW_NOINTERACTION !== "true") { // must unlock - if (await this.main.keyConnectorService.getUsesKeyConnector()) { + if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector()) { const response = Response.error( "Your vault is locked. You must unlock your vault using your session key.\n" + "If you do not have your session key, you can get a new one by logging out and logging in again.", @@ -626,19 +631,19 @@ export class Program { this.processResponse(response, true); } else { const command = new UnlockCommand( - this.main.accountService, - this.main.masterPasswordService, - this.main.cryptoService, - this.main.stateService, - this.main.cryptoFunctionService, - this.main.apiService, - this.main.logService, - this.main.keyConnectorService, - this.main.environmentService, - this.main.syncService, - this.main.organizationApiService, - this.main.logout, - this.main.kdfConfigService, + this.serviceContainer.accountService, + this.serviceContainer.masterPasswordService, + this.serviceContainer.cryptoService, + this.serviceContainer.stateService, + this.serviceContainer.cryptoFunctionService, + this.serviceContainer.apiService, + this.serviceContainer.logService, + this.serviceContainer.keyConnectorService, + this.serviceContainer.environmentService, + this.serviceContainer.syncService, + this.serviceContainer.organizationApiService, + this.serviceContainer.logout, + this.serviceContainer.kdfConfigService, ); const response = await command.run(null, null); if (!response.success) { diff --git a/apps/cli/src/register-oss-programs.ts b/apps/cli/src/register-oss-programs.ts new file mode 100644 index 0000000000..f47aa52854 --- /dev/null +++ b/apps/cli/src/register-oss-programs.ts @@ -0,0 +1,22 @@ +import { Program } from "./program"; +import { ServiceContainer } from "./service-container"; +import { SendProgram } from "./tools/send/send.program"; +import { VaultProgram } from "./vault.program"; + +/** + * All OSS licensed programs should be registered here. + * @example + * const myProgram = new myProgram(serviceContainer); + * myProgram.register(); + * @param serviceContainer A class that instantiates services and makes them available for dependency injection + */ +export async function registerOssPrograms(serviceContainer: ServiceContainer) { + const program = new Program(serviceContainer); + await program.register(); + + const vaultProgram = new VaultProgram(serviceContainer); + await vaultProgram.register(); + + const sendProgram = new SendProgram(serviceContainer); + await sendProgram.register(); +} diff --git a/apps/cli/src/service-container.spec.ts b/apps/cli/src/service-container.spec.ts new file mode 100644 index 0000000000..26258c367c --- /dev/null +++ b/apps/cli/src/service-container.spec.ts @@ -0,0 +1,7 @@ +import { ServiceContainer } from "./service-container"; + +describe("ServiceContainer", () => { + it("instantiates", async () => { + expect(() => new ServiceContainer()).not.toThrow(); + }); +}); diff --git a/apps/cli/src/service-container.ts b/apps/cli/src/service-container.ts new file mode 100644 index 0000000000..cffdc53444 --- /dev/null +++ b/apps/cli/src/service-container.ts @@ -0,0 +1,772 @@ +import * as fs from "fs"; +import * as path from "path"; + +import * as jsdom from "jsdom"; +import { firstValueFrom } from "rxjs"; + +import { + InternalUserDecryptionOptionsServiceAbstraction, + AuthRequestService, + LoginStrategyService, + LoginStrategyServiceAbstraction, + PinService, + PinServiceAbstraction, + UserDecryptionOptionsService, +} from "@bitwarden/auth/common"; +import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service"; +import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; +import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service"; +import { OrganizationUserServiceImplementation } from "@bitwarden/common/admin-console/services/organization-user/organization-user.service.implementation"; +import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; +import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; +import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service"; +import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; +import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; +import { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; +import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; +import { AuthService } from "@bitwarden/common/auth/services/auth.service"; +import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; +import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation"; +import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; +import { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service"; +import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; +import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service"; +import { TokenService } from "@bitwarden/common/auth/services/token.service"; +import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service"; +import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; +import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; +import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; +import { + DefaultDomainSettingsService, + DomainSettingsService, +} from "@bitwarden/common/autofill/services/domain-settings.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; +import { ClientType } from "@bitwarden/common/enums"; +import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; +import { + BiometricStateService, + DefaultBiometricStateService, +} from "@bitwarden/common/platform/biometrics/biometric-state.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"; +import { Account } from "@bitwarden/common/platform/models/domain/account"; +import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; +import { AppIdService } from "@bitwarden/common/platform/services/app-id.service"; +import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; +import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service"; +import { ContainerService } from "@bitwarden/common/platform/services/container.service"; +import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; +import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; +import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service"; +import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.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"; +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"; +import { + ActiveUserStateProvider, + DerivedStateProvider, + GlobalStateProvider, + SingleUserStateProvider, + StateEventRunnerService, + StateProvider, +} from "@bitwarden/common/platform/state"; +/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally these should not be accessed */ +import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider"; +import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; +import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider"; +import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider"; +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"; +/* eslint-enable import/no-restricted-paths */ +import { AuditService } from "@bitwarden/common/services/audit.service"; +import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; +import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; +import { SearchService } from "@bitwarden/common/services/search.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; +import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service"; +import { + PasswordGenerationService, + PasswordGenerationServiceAbstraction, +} from "@bitwarden/common/tools/generator/password"; +import { + PasswordStrengthService, + PasswordStrengthServiceAbstraction, +} from "@bitwarden/common/tools/password-strength"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service"; +import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider"; +import { SendService } from "@bitwarden/common/tools/send/services/send.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type"; +import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; +import { CollectionService } from "@bitwarden/common/vault/services/collection.service"; +import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service"; +import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; +import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service"; +import { SyncNotifierService } from "@bitwarden/common/vault/services/sync/sync-notifier.service"; +import { SyncService } from "@bitwarden/common/vault/services/sync/sync.service"; +import { TotpService } from "@bitwarden/common/vault/services/totp.service"; +import { + ImportApiService, + ImportApiServiceAbstraction, + ImportService, + ImportServiceAbstraction, +} from "@bitwarden/importer/core"; +import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; +import { + IndividualVaultExportService, + IndividualVaultExportServiceAbstraction, + OrganizationVaultExportService, + OrganizationVaultExportServiceAbstraction, + VaultExportService, + VaultExportServiceAbstraction, +} from "@bitwarden/vault-export-core"; + +import { CliPlatformUtilsService } from "./platform/services/cli-platform-utils.service"; +import { ConsoleLogService } from "./platform/services/console-log.service"; +import { I18nService } from "./platform/services/i18n.service"; +import { LowdbStorageService } from "./platform/services/lowdb-storage.service"; +import { NodeApiService } from "./platform/services/node-api.service"; +import { NodeEnvSecureStorageService } from "./platform/services/node-env-secure-storage.service"; + +// Polyfills +global.DOMParser = new jsdom.JSDOM().window.DOMParser; + +// eslint-disable-next-line +const packageJson = require("../package.json"); + +/** + * Instantiates services and makes them available for dependency injection. + * Any Bitwarden-licensed services should be registered here. + */ +export class ServiceContainer { + private inited = false; + + messagingService: MessageSender; + storageService: LowdbStorageService; + secureStorageService: NodeEnvSecureStorageService; + memoryStorageService: MemoryStorageService; + memoryStorageForStateProviders: MemoryStorageServiceForStateProviders; + i18nService: I18nService; + platformUtilsService: CliPlatformUtilsService; + cryptoService: CryptoService; + tokenService: TokenService; + appIdService: AppIdService; + apiService: NodeApiService; + environmentService: EnvironmentService; + cipherService: CipherService; + folderService: InternalFolderService; + organizationUserService: OrganizationUserService; + collectionService: CollectionService; + vaultTimeoutService: VaultTimeoutService; + masterPasswordService: InternalMasterPasswordServiceAbstraction; + vaultTimeoutSettingsService: VaultTimeoutSettingsService; + syncService: SyncService; + eventCollectionService: EventCollectionServiceAbstraction; + eventUploadService: EventUploadServiceAbstraction; + passwordGenerationService: PasswordGenerationServiceAbstraction; + passwordStrengthService: PasswordStrengthServiceAbstraction; + userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction; + totpService: TotpService; + containerService: ContainerService; + auditService: AuditService; + importService: ImportServiceAbstraction; + importApiService: ImportApiServiceAbstraction; + exportService: VaultExportServiceAbstraction; + individualExportService: IndividualVaultExportServiceAbstraction; + organizationExportService: OrganizationVaultExportServiceAbstraction; + searchService: SearchService; + keyGenerationService: KeyGenerationServiceAbstraction; + cryptoFunctionService: NodeCryptoFunctionService; + encryptService: EncryptServiceImplementation; + authService: AuthService; + policyService: PolicyService; + policyApiService: PolicyApiServiceAbstraction; + logService: ConsoleLogService; + sendService: SendService; + sendStateProvider: SendStateProvider; + fileUploadService: FileUploadService; + cipherFileUploadService: CipherFileUploadService; + keyConnectorService: KeyConnectorService; + userVerificationService: UserVerificationService; + pinService: PinServiceAbstraction; + stateService: StateService; + autofillSettingsService: AutofillSettingsServiceAbstraction; + domainSettingsService: DomainSettingsService; + organizationService: OrganizationService; + providerService: ProviderService; + twoFactorService: TwoFactorService; + folderApiService: FolderApiService; + userVerificationApiService: UserVerificationApiService; + organizationApiService: OrganizationApiServiceAbstraction; + syncNotifierService: SyncNotifierService; + sendApiService: SendApiService; + devicesApiService: DevicesApiServiceAbstraction; + deviceTrustService: DeviceTrustServiceAbstraction; + authRequestService: AuthRequestService; + configApiService: ConfigApiServiceAbstraction; + configService: ConfigService; + accountService: AccountService; + globalStateProvider: GlobalStateProvider; + singleUserStateProvider: SingleUserStateProvider; + activeUserStateProvider: ActiveUserStateProvider; + derivedStateProvider: DerivedStateProvider; + stateProvider: StateProvider; + loginStrategyService: LoginStrategyServiceAbstraction; + avatarService: AvatarServiceAbstraction; + stateEventRunnerService: StateEventRunnerService; + biometricStateService: BiometricStateService; + billingAccountProfileStateService: BillingAccountProfileStateService; + providerApiService: ProviderApiServiceAbstraction; + userAutoUnlockKeyService: UserAutoUnlockKeyService; + kdfConfigService: KdfConfigServiceAbstraction; + + constructor() { + let p = null; + const relativeDataDir = path.join(path.dirname(process.execPath), "bw-data"); + if (fs.existsSync(relativeDataDir)) { + p = relativeDataDir; + } else if (process.env.BITWARDENCLI_APPDATA_DIR) { + p = path.resolve(process.env.BITWARDENCLI_APPDATA_DIR); + } else if (process.platform === "darwin") { + p = path.join(process.env.HOME, "Library/Application Support/Bitwarden CLI"); + } else if (process.platform === "win32") { + p = path.join(process.env.APPDATA, "Bitwarden CLI"); + } else if (process.env.XDG_CONFIG_HOME) { + p = path.join(process.env.XDG_CONFIG_HOME, "Bitwarden CLI"); + } else { + p = path.join(process.env.HOME, ".config/Bitwarden CLI"); + } + + this.platformUtilsService = new CliPlatformUtilsService(ClientType.Cli, packageJson); + this.logService = new ConsoleLogService( + this.platformUtilsService.isDev(), + (level) => process.env.BITWARDENCLI_DEBUG !== "true" && level <= LogLevelType.Info, + ); + this.cryptoFunctionService = new NodeCryptoFunctionService(); + this.encryptService = new EncryptServiceImplementation( + this.cryptoFunctionService, + this.logService, + true, + ); + this.storageService = new LowdbStorageService(this.logService, null, p, false, true); + this.secureStorageService = new NodeEnvSecureStorageService( + this.storageService, + this.logService, + this.encryptService, + ); + + this.memoryStorageService = new MemoryStorageService(); + this.memoryStorageForStateProviders = new MemoryStorageServiceForStateProviders(); + + const storageServiceProvider = new StorageServiceProvider( + this.storageService, + this.memoryStorageForStateProviders, + ); + + this.globalStateProvider = new DefaultGlobalStateProvider(storageServiceProvider); + + const stateEventRegistrarService = new StateEventRegistrarService( + this.globalStateProvider, + storageServiceProvider, + ); + + this.stateEventRunnerService = new StateEventRunnerService( + this.globalStateProvider, + storageServiceProvider, + ); + + this.i18nService = new I18nService("en", "./locales", this.globalStateProvider); + + this.singleUserStateProvider = new DefaultSingleUserStateProvider( + storageServiceProvider, + stateEventRegistrarService, + ); + + this.messagingService = MessageSender.EMPTY; + + this.accountService = new AccountServiceImplementation( + this.messagingService, + this.logService, + this.globalStateProvider, + ); + + this.activeUserStateProvider = new DefaultActiveUserStateProvider( + this.accountService, + this.singleUserStateProvider, + ); + + this.derivedStateProvider = new DefaultDerivedStateProvider(); + + this.stateProvider = new DefaultStateProvider( + this.activeUserStateProvider, + this.singleUserStateProvider, + this.globalStateProvider, + this.derivedStateProvider, + ); + + this.environmentService = new DefaultEnvironmentService( + this.stateProvider, + this.accountService, + ); + + this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService); + + this.tokenService = new TokenService( + this.singleUserStateProvider, + this.globalStateProvider, + this.platformUtilsService.supportsSecureStorage(), + this.secureStorageService, + this.keyGenerationService, + this.encryptService, + this.logService, + ); + + const migrationRunner = new MigrationRunner( + this.storageService, + this.logService, + new MigrationBuilderService(), + ClientType.Cli, + ); + + this.stateService = new StateService( + this.storageService, + this.secureStorageService, + this.memoryStorageService, + this.logService, + new StateFactory(GlobalState, Account), + this.accountService, + this.environmentService, + this.tokenService, + migrationRunner, + ); + + this.masterPasswordService = new MasterPasswordService( + this.stateProvider, + this.stateService, + this.keyGenerationService, + this.encryptService, + ); + + this.kdfConfigService = new KdfConfigService(this.stateProvider); + + this.pinService = new PinService( + this.accountService, + this.cryptoFunctionService, + this.encryptService, + this.kdfConfigService, + this.keyGenerationService, + this.logService, + this.masterPasswordService, + this.stateProvider, + this.stateService, + ); + + this.cryptoService = new CryptoService( + this.pinService, + this.masterPasswordService, + this.keyGenerationService, + this.cryptoFunctionService, + this.encryptService, + this.platformUtilsService, + this.logService, + this.stateService, + this.accountService, + this.stateProvider, + this.kdfConfigService, + ); + + this.appIdService = new AppIdService(this.globalStateProvider); + + const customUserAgent = + "Bitwarden_CLI/" + + this.platformUtilsService.getApplicationVersionSync() + + " (" + + this.platformUtilsService.getDeviceString().toUpperCase() + + ")"; + + this.biometricStateService = new DefaultBiometricStateService(this.stateProvider); + this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider); + + this.organizationService = new OrganizationService(this.stateProvider); + this.policyService = new PolicyService(this.stateProvider, this.organizationService); + + this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService( + this.accountService, + this.pinService, + this.userDecryptionOptionsService, + this.cryptoService, + this.tokenService, + this.policyService, + this.biometricStateService, + this.stateProvider, + this.logService, + VaultTimeoutStringType.Never, // default vault timeout + ); + + this.apiService = new NodeApiService( + this.tokenService, + this.platformUtilsService, + this.environmentService, + this.appIdService, + this.vaultTimeoutSettingsService, + async (expired: boolean) => await this.logout(), + customUserAgent, + ); + + this.syncNotifierService = new SyncNotifierService(); + + this.organizationApiService = new OrganizationApiService(this.apiService, this.syncService); + + this.containerService = new ContainerService(this.cryptoService, this.encryptService); + + this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider); + + this.fileUploadService = new FileUploadService(this.logService); + + this.sendStateProvider = new SendStateProvider(this.stateProvider); + + this.sendService = new SendService( + this.cryptoService, + this.i18nService, + this.keyGenerationService, + this.sendStateProvider, + this.encryptService, + ); + + this.cipherFileUploadService = new CipherFileUploadService( + this.apiService, + this.fileUploadService, + ); + + this.sendApiService = this.sendApiService = new SendApiService( + this.apiService, + this.fileUploadService, + this.sendService, + ); + + this.searchService = new SearchService(this.logService, this.i18nService, this.stateProvider); + + this.collectionService = new CollectionService( + this.cryptoService, + this.i18nService, + this.stateProvider, + ); + + this.providerService = new ProviderService(this.stateProvider); + + this.organizationUserService = new OrganizationUserServiceImplementation(this.apiService); + + this.policyApiService = new PolicyApiService(this.policyService, this.apiService); + + this.keyConnectorService = new KeyConnectorService( + this.accountService, + this.masterPasswordService, + this.cryptoService, + this.apiService, + this.tokenService, + this.logService, + this.organizationService, + this.keyGenerationService, + async (expired: boolean) => await this.logout(), + this.stateProvider, + ); + + this.twoFactorService = new TwoFactorService( + this.i18nService, + this.platformUtilsService, + this.globalStateProvider, + ); + + this.passwordStrengthService = new PasswordStrengthService(); + + this.passwordGenerationService = new PasswordGenerationService( + this.cryptoService, + this.policyService, + this.stateService, + ); + + this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); + this.deviceTrustService = new DeviceTrustService( + this.keyGenerationService, + this.cryptoFunctionService, + this.cryptoService, + this.encryptService, + this.appIdService, + this.devicesApiService, + this.i18nService, + this.platformUtilsService, + this.stateProvider, + this.secureStorageService, + this.userDecryptionOptionsService, + this.logService, + ); + + this.authRequestService = new AuthRequestService( + this.appIdService, + this.accountService, + this.masterPasswordService, + this.cryptoService, + this.apiService, + this.stateProvider, + ); + + this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService( + this.stateProvider, + ); + + this.loginStrategyService = new LoginStrategyService( + this.accountService, + this.masterPasswordService, + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.keyConnectorService, + this.environmentService, + this.stateService, + this.twoFactorService, + this.i18nService, + this.encryptService, + this.passwordStrengthService, + this.policyService, + this.deviceTrustService, + this.authRequestService, + this.userDecryptionOptionsService, + this.globalStateProvider, + this.billingAccountProfileStateService, + this.vaultTimeoutSettingsService, + this.kdfConfigService, + ); + + this.authService = new AuthService( + this.accountService, + this.messagingService, + this.cryptoService, + this.apiService, + this.stateService, + this.tokenService, + ); + + this.configApiService = new ConfigApiService(this.apiService, this.tokenService); + + this.configService = new DefaultConfigService( + this.configApiService, + this.environmentService, + this.logService, + this.stateProvider, + ); + + this.cipherService = new CipherService( + this.cryptoService, + this.domainSettingsService, + this.apiService, + this.i18nService, + this.searchService, + this.stateService, + this.autofillSettingsService, + this.encryptService, + this.cipherFileUploadService, + this.configService, + this.stateProvider, + ); + + this.folderService = new FolderService( + this.cryptoService, + this.i18nService, + this.cipherService, + this.stateProvider, + ); + + this.folderApiService = new FolderApiService(this.folderService, this.apiService); + + const lockedCallback = async (userId?: string) => + await this.cryptoService.clearStoredUserKey(KeySuffixOptions.Auto); + + this.userVerificationService = new UserVerificationService( + this.stateService, + this.cryptoService, + this.accountService, + this.masterPasswordService, + this.i18nService, + this.userVerificationApiService, + this.userDecryptionOptionsService, + this.pinService, + this.logService, + this.vaultTimeoutSettingsService, + this.platformUtilsService, + this.kdfConfigService, + ); + + this.vaultTimeoutService = new VaultTimeoutService( + this.accountService, + this.masterPasswordService, + this.cipherService, + this.folderService, + this.collectionService, + this.platformUtilsService, + this.messagingService, + this.searchService, + this.stateService, + this.authService, + this.vaultTimeoutSettingsService, + this.stateEventRunnerService, + lockedCallback, + null, + ); + + this.avatarService = new AvatarService(this.apiService, this.stateProvider); + + this.syncService = new SyncService( + this.masterPasswordService, + this.accountService, + this.apiService, + this.domainSettingsService, + this.folderService, + this.cipherService, + this.cryptoService, + this.collectionService, + this.messagingService, + this.policyService, + this.sendService, + this.logService, + this.keyConnectorService, + this.stateService, + this.providerService, + this.folderApiService, + this.organizationService, + this.sendApiService, + this.userDecryptionOptionsService, + this.avatarService, + async (expired: boolean) => await this.logout(), + this.billingAccountProfileStateService, + this.tokenService, + this.authService, + ); + + this.totpService = new TotpService(this.cryptoFunctionService, this.logService); + + this.importApiService = new ImportApiService(this.apiService); + + this.importService = new ImportService( + this.cipherService, + this.folderService, + this.importApiService, + this.i18nService, + this.collectionService, + this.cryptoService, + this.pinService, + ); + + this.individualExportService = new IndividualVaultExportService( + this.folderService, + this.cipherService, + this.pinService, + this.cryptoService, + this.cryptoFunctionService, + this.kdfConfigService, + ); + + this.organizationExportService = new OrganizationVaultExportService( + this.cipherService, + this.apiService, + this.pinService, + this.cryptoService, + this.cryptoFunctionService, + this.collectionService, + this.kdfConfigService, + ); + + this.exportService = new VaultExportService( + this.individualExportService, + this.organizationExportService, + ); + + this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.cryptoService); + + this.auditService = new AuditService(this.cryptoFunctionService, this.apiService); + + this.userVerificationApiService = new UserVerificationApiService(this.apiService); + + this.eventUploadService = new EventUploadService( + this.apiService, + this.stateProvider, + this.logService, + this.authService, + ); + + this.eventCollectionService = new EventCollectionService( + this.cipherService, + this.stateProvider, + this.organizationService, + this.eventUploadService, + this.authService, + ); + + this.providerApiService = new ProviderApiService(this.apiService); + } + + async logout() { + this.authService.logOut(() => { + /* Do nothing */ + }); + const userId = (await this.stateService.getUserId()) as UserId; + await Promise.all([ + this.eventUploadService.uploadEvents(userId as UserId), + this.syncService.setLastSync(new Date(0)), + this.cryptoService.clearKeys(), + this.cipherService.clear(userId), + this.folderService.clear(userId), + this.collectionService.clear(userId as UserId), + this.passwordGenerationService.clear(), + ]); + + await this.stateEventRunnerService.handleEvent("logout", userId); + + await this.stateService.clean(); + await this.accountService.clean(userId); + process.env.BW_SESSION = null; + } + + async init() { + if (this.inited) { + this.logService.warning("ServiceContainer.init called more than once"); + return; + } + + await this.storageService.init(); + await this.stateService.init(); + this.containerService.attachToGlobal(global); + await this.i18nService.init(); + this.twoFactorService.init(); + + const activeAccount = await firstValueFrom(this.accountService.activeAccount$); + if (activeAccount) { + await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(activeAccount.id); + } + + this.inited = true; + } +} diff --git a/apps/cli/src/tools/send/send.program.ts b/apps/cli/src/tools/send/send.program.ts index 269719f887..86edd28f09 100644 --- a/apps/cli/src/tools/send/send.program.ts +++ b/apps/cli/src/tools/send/send.program.ts @@ -7,7 +7,6 @@ import { program, Command, OptionValues } from "commander"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { Main } from "../../bw"; import { GetCommand } from "../../commands/get.command"; import { Response } from "../../models/response"; import { Program } from "../../program"; @@ -29,10 +28,6 @@ import { SendResponse } from "./models/send.response"; const writeLn = CliUtils.writeLn; export class SendProgram extends Program { - constructor(main: Main) { - super(main); - } - async register() { program.addCommand(this.sendCommand()); // receive is accessible both at `bw receive` and `bw send receive` @@ -105,12 +100,12 @@ export class SendProgram extends Program { }) .action(async (url: string, options: OptionValues) => { const cmd = new SendReceiveCommand( - this.main.apiService, - this.main.cryptoService, - this.main.cryptoFunctionService, - this.main.platformUtilsService, - this.main.environmentService, - this.main.sendApiService, + this.serviceContainer.apiService, + this.serviceContainer.cryptoService, + this.serviceContainer.cryptoFunctionService, + this.serviceContainer.platformUtilsService, + this.serviceContainer.environmentService, + this.serviceContainer.sendApiService, ); const response = await cmd.run(url, options); this.processResponse(response); @@ -127,9 +122,9 @@ export class SendProgram extends Program { .action(async (options: OptionValues) => { await this.exitIfLocked(); const cmd = new SendListCommand( - this.main.sendService, - this.main.environmentService, - this.main.searchService, + this.serviceContainer.sendService, + this.serviceContainer.environmentService, + this.serviceContainer.searchService, ); const response = await cmd.run(options); this.processResponse(response); @@ -142,18 +137,18 @@ export class SendProgram extends Program { .description("Get json templates for send objects") .action(async (object) => { const cmd = new GetCommand( - this.main.cipherService, - this.main.folderService, - this.main.collectionService, - this.main.totpService, - this.main.auditService, - this.main.cryptoService, - this.main.stateService, - this.main.searchService, - this.main.apiService, - this.main.organizationService, - this.main.eventCollectionService, - this.main.billingAccountProfileStateService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.collectionService, + this.serviceContainer.totpService, + this.serviceContainer.auditService, + this.serviceContainer.cryptoService, + this.serviceContainer.stateService, + this.serviceContainer.searchService, + this.serviceContainer.apiService, + this.serviceContainer.organizationService, + this.serviceContainer.eventCollectionService, + this.serviceContainer.billingAccountProfileStateService, ); const response = await cmd.run("template", object, null); this.processResponse(response); @@ -188,10 +183,10 @@ export class SendProgram extends Program { .action(async (id: string, options: OptionValues) => { await this.exitIfLocked(); const cmd = new SendGetCommand( - this.main.sendService, - this.main.environmentService, - this.main.searchService, - this.main.cryptoService, + this.serviceContainer.sendService, + this.serviceContainer.environmentService, + this.serviceContainer.searchService, + this.serviceContainer.cryptoService, ); const response = await cmd.run(id, options); this.processResponse(response); @@ -247,16 +242,16 @@ export class SendProgram extends Program { .action(async (encodedJson: string, options: OptionValues) => { await this.exitIfLocked(); const getCmd = new SendGetCommand( - this.main.sendService, - this.main.environmentService, - this.main.searchService, - this.main.cryptoService, + this.serviceContainer.sendService, + this.serviceContainer.environmentService, + this.serviceContainer.searchService, + this.serviceContainer.cryptoService, ); const cmd = new SendEditCommand( - this.main.sendService, + this.serviceContainer.sendService, getCmd, - this.main.sendApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.sendApiService, + this.serviceContainer.billingAccountProfileStateService, ); const response = await cmd.run(encodedJson, options); this.processResponse(response); @@ -269,7 +264,10 @@ export class SendProgram extends Program { .description("delete a Send") .action(async (id: string) => { await this.exitIfLocked(); - const cmd = new SendDeleteCommand(this.main.sendService, this.main.sendApiService); + const cmd = new SendDeleteCommand( + this.serviceContainer.sendService, + this.serviceContainer.sendApiService, + ); const response = await cmd.run(id); this.processResponse(response); }); @@ -282,9 +280,9 @@ export class SendProgram extends Program { .action(async (id: string) => { await this.exitIfLocked(); const cmd = new SendRemovePasswordCommand( - this.main.sendService, - this.main.sendApiService, - this.main.environmentService, + this.serviceContainer.sendService, + this.serviceContainer.sendApiService, + this.serviceContainer.environmentService, ); const response = await cmd.run(id); this.processResponse(response); @@ -323,10 +321,10 @@ export class SendProgram extends Program { private async runCreate(encodedJson: string, options: OptionValues) { await this.exitIfLocked(); const cmd = new SendCreateCommand( - this.main.sendService, - this.main.environmentService, - this.main.sendApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.sendService, + this.serviceContainer.environmentService, + this.serviceContainer.sendApiService, + this.serviceContainer.billingAccountProfileStateService, ); return await cmd.run(encodedJson, options); } diff --git a/apps/cli/src/vault.program.ts b/apps/cli/src/vault.program.ts index 1107c9aba0..52857ed542 100644 --- a/apps/cli/src/vault.program.ts +++ b/apps/cli/src/vault.program.ts @@ -2,7 +2,6 @@ import { program, Command } from "commander"; import { ConfirmCommand } from "./admin-console/commands/confirm.command"; import { ShareCommand } from "./admin-console/commands/share.command"; -import { Main } from "./bw"; import { EditCommand } from "./commands/edit.command"; import { GetCommand } from "./commands/get.command"; import { ListCommand } from "./commands/list.command"; @@ -18,10 +17,6 @@ import { DeleteCommand } from "./vault/delete.command"; const writeLn = CliUtils.writeLn; export class VaultProgram extends Program { - constructor(protected main: Main) { - super(main); - } - async register() { program .addCommand(this.listCommand()) @@ -108,14 +103,14 @@ export class VaultProgram extends Program { await this.exitIfLocked(); const command = new ListCommand( - this.main.cipherService, - this.main.folderService, - this.main.collectionService, - this.main.organizationService, - this.main.searchService, - this.main.organizationUserService, - this.main.apiService, - this.main.eventCollectionService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.collectionService, + this.serviceContainer.organizationService, + this.serviceContainer.searchService, + this.serviceContainer.organizationUserService, + this.serviceContainer.apiService, + this.serviceContainer.eventCollectionService, ); const response = await command.run(object, cmd); @@ -177,18 +172,18 @@ export class VaultProgram extends Program { await this.exitIfLocked(); const command = new GetCommand( - this.main.cipherService, - this.main.folderService, - this.main.collectionService, - this.main.totpService, - this.main.auditService, - this.main.cryptoService, - this.main.stateService, - this.main.searchService, - this.main.apiService, - this.main.organizationService, - this.main.eventCollectionService, - this.main.billingAccountProfileStateService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.collectionService, + this.serviceContainer.totpService, + this.serviceContainer.auditService, + this.serviceContainer.cryptoService, + this.serviceContainer.stateService, + this.serviceContainer.searchService, + this.serviceContainer.apiService, + this.serviceContainer.organizationService, + this.serviceContainer.eventCollectionService, + this.serviceContainer.billingAccountProfileStateService, ); const response = await command.run(object, id, cmd); this.processResponse(response); @@ -225,12 +220,12 @@ export class VaultProgram extends Program { await this.exitIfLocked(); const command = new CreateCommand( - this.main.cipherService, - this.main.folderService, - this.main.cryptoService, - this.main.apiService, - this.main.folderApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.cryptoService, + this.serviceContainer.apiService, + this.serviceContainer.folderApiService, + this.serviceContainer.billingAccountProfileStateService, ); const response = await command.run(object, encodedJson, cmd); this.processResponse(response); @@ -271,11 +266,11 @@ export class VaultProgram extends Program { await this.exitIfLocked(); const command = new EditCommand( - this.main.cipherService, - this.main.folderService, - this.main.cryptoService, - this.main.apiService, - this.main.folderApiService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.cryptoService, + this.serviceContainer.apiService, + this.serviceContainer.folderApiService, ); const response = await command.run(object, id, encodedJson, cmd); this.processResponse(response); @@ -312,11 +307,11 @@ export class VaultProgram extends Program { await this.exitIfLocked(); const command = new DeleteCommand( - this.main.cipherService, - this.main.folderService, - this.main.apiService, - this.main.folderApiService, - this.main.billingAccountProfileStateService, + this.serviceContainer.cipherService, + this.serviceContainer.folderService, + this.serviceContainer.apiService, + this.serviceContainer.folderApiService, + this.serviceContainer.billingAccountProfileStateService, ); const response = await command.run(object, id, cmd); this.processResponse(response); @@ -341,7 +336,7 @@ export class VaultProgram extends Program { } await this.exitIfLocked(); - const command = new RestoreCommand(this.main.cipherService); + const command = new RestoreCommand(this.serviceContainer.cipherService); const response = await command.run(object, id); this.processResponse(response); }); @@ -379,7 +374,7 @@ export class VaultProgram extends Program { }) .action(async (id, organizationId, encodedJson, cmd) => { await this.exitIfLocked(); - const command = new ShareCommand(this.main.cipherService); + const command = new ShareCommand(this.serviceContainer.cipherService); const response = await command.run(id, organizationId, encodedJson); this.processResponse(response); }); @@ -408,9 +403,9 @@ export class VaultProgram extends Program { await this.exitIfLocked(); const command = new ConfirmCommand( - this.main.apiService, - this.main.cryptoService, - this.main.organizationUserService, + this.serviceContainer.apiService, + this.serviceContainer.cryptoService, + this.serviceContainer.organizationUserService, ); const response = await command.run(object, id, cmd); this.processResponse(response); @@ -437,9 +432,9 @@ export class VaultProgram extends Program { .action(async (format, filepath, options) => { await this.exitIfLocked(); const command = new ImportCommand( - this.main.importService, - this.main.organizationService, - this.main.syncService, + this.serviceContainer.importService, + this.serviceContainer.organizationService, + this.serviceContainer.syncService, ); const response = await command.run(format, filepath, options); this.processResponse(response); @@ -484,9 +479,9 @@ export class VaultProgram extends Program { .action(async (options) => { await this.exitIfLocked(); const command = new ExportCommand( - this.main.exportService, - this.main.policyService, - this.main.eventCollectionService, + this.serviceContainer.exportService, + this.serviceContainer.policyService, + this.serviceContainer.eventCollectionService, ); const response = await command.run(options); this.processResponse(response); diff --git a/bitwarden_license/bit-cli/.eslintrc.json b/bitwarden_license/bit-cli/.eslintrc.json new file mode 100644 index 0000000000..10d2238837 --- /dev/null +++ b/bitwarden_license/bit-cli/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "env": { + "node": true + } +} diff --git a/bitwarden_license/bit-cli/jest.config.js b/bitwarden_license/bit-cli/jest.config.js new file mode 100644 index 0000000000..92be98cc56 --- /dev/null +++ b/bitwarden_license/bit-cli/jest.config.js @@ -0,0 +1,16 @@ +const { pathsToModuleNameMapper } = require("ts-jest"); + +const { compilerOptions } = require("./tsconfig"); + +const sharedConfig = require("../../libs/shared/jest.config.ts"); + +/** @type {import('jest').Config} */ +module.exports = { + ...sharedConfig, + preset: "ts-jest", + testEnvironment: "node", + setupFilesAfterEnv: ["/../../apps/cli/test.setup.ts"], + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/bitwarden_license/bit-cli/src/bw.spec.ts b/bitwarden_license/bit-cli/src/bw.spec.ts new file mode 100644 index 0000000000..317f9733b1 --- /dev/null +++ b/bitwarden_license/bit-cli/src/bw.spec.ts @@ -0,0 +1,5 @@ +describe("Jest", () => { + it("is set up", () => { + expect(true).toBeTruthy(); + }); +}); diff --git a/bitwarden_license/bit-cli/src/bw.ts b/bitwarden_license/bit-cli/src/bw.ts new file mode 100644 index 0000000000..d6ebcaf041 --- /dev/null +++ b/bitwarden_license/bit-cli/src/bw.ts @@ -0,0 +1,20 @@ +import { program } from "commander"; + +import { registerOssPrograms } from "@bitwarden/cli/register-oss-programs"; + +import { registerBitPrograms } from "./register-bit-programs"; +import { ServiceContainer } from "./service-container"; + +async function main() { + const serviceContainer = new ServiceContainer(); + await serviceContainer.init(); + + await registerOssPrograms(serviceContainer); + await registerBitPrograms(serviceContainer); + + program.parse(process.argv); +} + +// Node does not support top-level await statements until ES2022, esnext, etc which we don't use yet +// eslint-disable-next-line @typescript-eslint/no-floating-promises +main(); diff --git a/bitwarden_license/bit-cli/src/register-bit-programs.ts b/bitwarden_license/bit-cli/src/register-bit-programs.ts new file mode 100644 index 0000000000..859574644a --- /dev/null +++ b/bitwarden_license/bit-cli/src/register-bit-programs.ts @@ -0,0 +1,10 @@ +import { ServiceContainer } from "./service-container"; + +/** + * All Bitwarden-licensed programs should be registered here. + * @example + * const myProgram = new myProgram(serviceContainer); + * myProgram.register(); + * @param serviceContainer A class that instantiates services and makes them available for dependency injection + */ +export async function registerBitPrograms(serviceContainer: ServiceContainer) {} diff --git a/bitwarden_license/bit-cli/src/service-container.spec.ts b/bitwarden_license/bit-cli/src/service-container.spec.ts new file mode 100644 index 0000000000..26258c367c --- /dev/null +++ b/bitwarden_license/bit-cli/src/service-container.spec.ts @@ -0,0 +1,7 @@ +import { ServiceContainer } from "./service-container"; + +describe("ServiceContainer", () => { + it("instantiates", async () => { + expect(() => new ServiceContainer()).not.toThrow(); + }); +}); diff --git a/bitwarden_license/bit-cli/src/service-container.ts b/bitwarden_license/bit-cli/src/service-container.ts new file mode 100644 index 0000000000..369d54113d --- /dev/null +++ b/bitwarden_license/bit-cli/src/service-container.ts @@ -0,0 +1,7 @@ +import { ServiceContainer as OssServiceContainer } from "@bitwarden/cli/service-container"; + +/** + * Instantiates services and makes them available for dependency injection. + * Any Bitwarden-licensed services should be registered here. + */ +export class ServiceContainer extends OssServiceContainer {} diff --git a/bitwarden_license/bit-cli/tsconfig.json b/bitwarden_license/bit-cli/tsconfig.json new file mode 100644 index 0000000000..1989aa08f9 --- /dev/null +++ b/bitwarden_license/bit-cli/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "pretty": true, + "moduleResolution": "node", + "target": "ES2016", + "module": "es6", + "noImplicitAny": true, + "allowSyntheticDefaultImports": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowJs": true, + "sourceMap": true, + "baseUrl": ".", + "paths": { + "@bitwarden/cli/*": ["../../apps/cli/src/*"], + "@bitwarden/common/spec": ["../../libs/common/spec"], + "@bitwarden/auth/common": ["../../libs/auth/src/common"], + "@bitwarden/auth/angular": ["../../libs/auth/src/angular"], + "@bitwarden/common/*": ["../../libs/common/src/*"], + "@bitwarden/importer/core": ["../../libs/importer/src"], + "@bitwarden/vault-export-core": [ + "../../libs/tools/export/vault-export/vault-export-core/src" + ], + "@bitwarden/node/*": ["../../libs/node/src/*"] + } + }, + "include": ["src", "src/**/*.spec.ts"] +} diff --git a/bitwarden_license/bit-cli/tsconfig.spec.json b/bitwarden_license/bit-cli/tsconfig.spec.json new file mode 100644 index 0000000000..ef128c808c --- /dev/null +++ b/bitwarden_license/bit-cli/tsconfig.spec.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "files": ["../../apps/cli/test.setup.ts"] +} diff --git a/bitwarden_license/bit-cli/webpack.config.js b/bitwarden_license/bit-cli/webpack.config.js new file mode 100644 index 0000000000..3e991f7971 --- /dev/null +++ b/bitwarden_license/bit-cli/webpack.config.js @@ -0,0 +1,12 @@ +const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); + +// Re-use the OSS CLI webpack config +const webpackConfig = require("../../apps/cli/webpack.config"); + +// Update paths to use the bit-cli entrypoint and tsconfig +webpackConfig.entry = { bw: "../../bitwarden_license/bit-cli/src/bw.ts" }; +webpackConfig.resolve.plugins = [ + new TsconfigPathsPlugin({ configFile: "../../bitwarden_license/bit-cli/tsconfig.json" }), +]; + +module.exports = webpackConfig; diff --git a/clients.code-workspace b/clients.code-workspace index 608f57096b..c7075c04ea 100644 --- a/clients.code-workspace +++ b/clients.code-workspace @@ -16,6 +16,10 @@ "name": "cli", "path": "apps/cli", }, + { + "name": "cli (bit)", + "path": "bitwarden_license/bit-cli", + }, { "name": "desktop", "path": "apps/desktop", diff --git a/jest.config.js b/jest.config.js index a5876f3dc0..1ff25c3beb 100644 --- a/jest.config.js +++ b/jest.config.js @@ -21,6 +21,7 @@ module.exports = { "/apps/desktop/jest.config.js", "/apps/web/jest.config.js", "/bitwarden_license/bit-web/jest.config.js", + "/bitwarden_license/bit-cli/jest.config.js", "/libs/admin-console/jest.config.js", "/libs/angular/jest.config.js",