1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 05:13:29 +00:00

[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 <djsmith85@users.noreply.github.com>
This commit is contained in:
Daniel James Smith
2024-12-10 15:36:53 +01:00
committed by GitHub
parent 99008267e6
commit 065725df7a
6 changed files with 35 additions and 8 deletions

View File

@@ -1,7 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @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 { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; 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 { FileResponse } from "../models/response/file.response";
import { CliUtils } from "../utils"; import { CliUtils } from "../utils";
/**
* Used to download and save attachments
*/
export abstract class DownloadCommand { 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<FileResponse>
*/
protected async saveAttachmentToFile( protected async saveAttachmentToFile(
url: string, url: string,
key: SymmetricCryptoKey, key: SymmetricCryptoKey,
fileName: string, fileName: string,
output?: 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) { if (response.status !== 200) {
return Response.error( return Response.error(
"A " + response.status + " error occurred while downloading the attachment.", "A " + response.status + " error occurred while downloading the attachment.",

View File

@@ -60,13 +60,13 @@ export class GetCommand extends DownloadCommand {
private keyService: KeyService, private keyService: KeyService,
encryptService: EncryptService, encryptService: EncryptService,
private searchService: SearchService, private searchService: SearchService,
private apiService: ApiService, protected apiService: ApiService,
private organizationService: OrganizationService, private organizationService: OrganizationService,
private eventCollectionService: EventCollectionService, private eventCollectionService: EventCollectionService,
private accountProfileService: BillingAccountProfileStateService, private accountProfileService: BillingAccountProfileStateService,
private accountService: AccountService, private accountService: AccountService,
) { ) {
super(encryptService); super(encryptService, apiService);
} }
async run(object: string, id: string, cmdOptions: Record<string, any>): Promise<Response> { async run(object: string, id: string, cmdOptions: Record<string, any>): Promise<Response> {

View File

@@ -157,6 +157,7 @@ export class OssServeConfigurator {
this.serviceContainer.environmentService, this.serviceContainer.environmentService,
this.serviceContainer.searchService, this.serviceContainer.searchService,
this.serviceContainer.encryptService, this.serviceContainer.encryptService,
this.serviceContainer.apiService,
); );
this.sendEditCommand = new SendEditCommand( this.sendEditCommand = new SendEditCommand(
this.serviceContainer.sendService, this.serviceContainer.sendService,

View File

@@ -3,6 +3,7 @@
import { OptionValues } from "commander"; import { OptionValues } from "commander";
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
@@ -20,8 +21,9 @@ export class SendGetCommand extends DownloadCommand {
private environmentService: EnvironmentService, private environmentService: EnvironmentService,
private searchService: SearchService, private searchService: SearchService,
encryptService: EncryptService, encryptService: EncryptService,
apiService: ApiService,
) { ) {
super(encryptService); super(encryptService, apiService);
} }
async run(id: string, options: OptionValues) { async run(id: string, options: OptionValues) {

View File

@@ -4,6 +4,7 @@ import { OptionValues } from "commander";
import * as inquirer from "inquirer"; import * as inquirer from "inquirer";
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
@@ -35,8 +36,9 @@ export class SendReceiveCommand extends DownloadCommand {
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private environmentService: EnvironmentService, private environmentService: EnvironmentService,
private sendApiService: SendApiService, private sendApiService: SendApiService,
apiService: ApiService,
) { ) {
super(encryptService); super(encryptService, apiService);
} }
async run(url: string, options: OptionValues): Promise<Response> { async run(url: string, options: OptionValues): Promise<Response> {

View File

@@ -108,6 +108,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.platformUtilsService, this.serviceContainer.platformUtilsService,
this.serviceContainer.environmentService, this.serviceContainer.environmentService,
this.serviceContainer.sendApiService, this.serviceContainer.sendApiService,
this.serviceContainer.apiService,
); );
const response = await cmd.run(url, options); const response = await cmd.run(url, options);
this.processResponse(response); this.processResponse(response);
@@ -190,6 +191,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.environmentService, this.serviceContainer.environmentService,
this.serviceContainer.searchService, this.serviceContainer.searchService,
this.serviceContainer.encryptService, this.serviceContainer.encryptService,
this.serviceContainer.apiService,
); );
const response = await cmd.run(id, options); const response = await cmd.run(id, options);
this.processResponse(response); this.processResponse(response);
@@ -249,6 +251,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.environmentService, this.serviceContainer.environmentService,
this.serviceContainer.searchService, this.serviceContainer.searchService,
this.serviceContainer.encryptService, this.serviceContainer.encryptService,
this.serviceContainer.apiService,
); );
const cmd = new SendEditCommand( const cmd = new SendEditCommand(
this.serviceContainer.sendService, this.serviceContainer.sendService,