From 065725df7aa9497dd4a59c21e86ac0bf672932d2 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:36:53 +0100 Subject: [PATCH] [PM-686] [CLI] Add proxy support for file downloads (#7345) * Add proxy support for file downloads Instead of using node's native fetch we extend ApiService with NodeApiService to add support for proxies using `node-fetch` * Add comments to the DownloadCommand --------- Co-authored-by: Daniel James Smith --- apps/cli/src/commands/download.command.ts | 27 ++++++++++++++++--- apps/cli/src/commands/get.command.ts | 4 +-- apps/cli/src/oss-serve-configurator.ts | 1 + .../src/tools/send/commands/get.command.ts | 4 ++- .../tools/send/commands/receive.command.ts | 4 ++- apps/cli/src/tools/send/send.program.ts | 3 +++ 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apps/cli/src/commands/download.command.ts b/apps/cli/src/commands/download.command.ts index 5b8f1e9bee8..6a7cda2ac91 100644 --- a/apps/cli/src/commands/download.command.ts +++ b/apps/cli/src/commands/download.command.ts @@ -1,7 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import * as fet from "node-fetch"; - +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -10,16 +9,36 @@ import { Response } from "../models/response"; import { FileResponse } from "../models/response/file.response"; import { CliUtils } from "../utils"; +/** + * Used to download and save attachments + */ export abstract class DownloadCommand { - constructor(protected encryptService: EncryptService) {} + /** + * @param encryptService - Needed for decryption of the retrieved attachment + * @param apiService - Needed to override the existing nativeFetch which is available as of Node 18, to support proxies + */ + constructor( + protected encryptService: EncryptService, + protected apiService: ApiService, + ) {} + /** + * Fetches an attachment via the url, decrypts it's content and saves it to a file + * @param url - url used to retrieve the attachment + * @param key - SymmetricCryptoKey to decrypt the file contents + * @param fileName - filename used when written to disk + * @param output - If output is empty or `--raw` was passed to the initial command the content is output onto stdout + * @returns Promise + */ protected async saveAttachmentToFile( url: string, key: SymmetricCryptoKey, fileName: string, output?: string, ) { - const response = await fet.default(new fet.Request(url, { headers: { cache: "no-cache" } })); + const response = await this.apiService.nativeFetch( + new Request(url, { headers: { cache: "no-cache" } }), + ); if (response.status !== 200) { return Response.error( "A " + response.status + " error occurred while downloading the attachment.", diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index e9a981bc385..1a835befd01 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -60,13 +60,13 @@ export class GetCommand extends DownloadCommand { private keyService: KeyService, encryptService: EncryptService, private searchService: SearchService, - private apiService: ApiService, + protected apiService: ApiService, private organizationService: OrganizationService, private eventCollectionService: EventCollectionService, private accountProfileService: BillingAccountProfileStateService, private accountService: AccountService, ) { - super(encryptService); + super(encryptService, apiService); } async run(object: string, id: string, cmdOptions: Record): Promise { diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts index e60b2b88deb..71c372ef301 100644 --- a/apps/cli/src/oss-serve-configurator.ts +++ b/apps/cli/src/oss-serve-configurator.ts @@ -157,6 +157,7 @@ export class OssServeConfigurator { this.serviceContainer.environmentService, this.serviceContainer.searchService, this.serviceContainer.encryptService, + this.serviceContainer.apiService, ); this.sendEditCommand = new SendEditCommand( this.serviceContainer.sendService, diff --git a/apps/cli/src/tools/send/commands/get.command.ts b/apps/cli/src/tools/send/commands/get.command.ts index cccb761b948..0e650c8503d 100644 --- a/apps/cli/src/tools/send/commands/get.command.ts +++ b/apps/cli/src/tools/send/commands/get.command.ts @@ -3,6 +3,7 @@ import { OptionValues } from "commander"; import { firstValueFrom } from "rxjs"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; @@ -20,8 +21,9 @@ export class SendGetCommand extends DownloadCommand { private environmentService: EnvironmentService, private searchService: SearchService, encryptService: EncryptService, + apiService: ApiService, ) { - super(encryptService); + super(encryptService, apiService); } async run(id: string, options: OptionValues) { diff --git a/apps/cli/src/tools/send/commands/receive.command.ts b/apps/cli/src/tools/send/commands/receive.command.ts index d522e80b163..d27ba4f88ec 100644 --- a/apps/cli/src/tools/send/commands/receive.command.ts +++ b/apps/cli/src/tools/send/commands/receive.command.ts @@ -4,6 +4,7 @@ import { OptionValues } from "commander"; import * as inquirer from "inquirer"; import { firstValueFrom } from "rxjs"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -35,8 +36,9 @@ export class SendReceiveCommand extends DownloadCommand { private platformUtilsService: PlatformUtilsService, private environmentService: EnvironmentService, private sendApiService: SendApiService, + apiService: ApiService, ) { - super(encryptService); + super(encryptService, apiService); } async run(url: string, options: OptionValues): Promise { diff --git a/apps/cli/src/tools/send/send.program.ts b/apps/cli/src/tools/send/send.program.ts index de3b25e98b6..b59ae770380 100644 --- a/apps/cli/src/tools/send/send.program.ts +++ b/apps/cli/src/tools/send/send.program.ts @@ -108,6 +108,7 @@ export class SendProgram extends BaseProgram { this.serviceContainer.platformUtilsService, this.serviceContainer.environmentService, this.serviceContainer.sendApiService, + this.serviceContainer.apiService, ); const response = await cmd.run(url, options); this.processResponse(response); @@ -190,6 +191,7 @@ export class SendProgram extends BaseProgram { this.serviceContainer.environmentService, this.serviceContainer.searchService, this.serviceContainer.encryptService, + this.serviceContainer.apiService, ); const response = await cmd.run(id, options); this.processResponse(response); @@ -249,6 +251,7 @@ export class SendProgram extends BaseProgram { this.serviceContainer.environmentService, this.serviceContainer.searchService, this.serviceContainer.encryptService, + this.serviceContainer.apiService, ); const cmd = new SendEditCommand( this.serviceContainer.sendService,