1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 21:20:27 +00:00

PM-919 created service send-download

This commit is contained in:
voommen-livefront
2025-03-27 15:56:51 -05:00
parent f69b1ff539
commit 5f7e7aef20
5 changed files with 37 additions and 103 deletions

View File

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

View File

@@ -185,6 +185,7 @@ 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 { SendDownloadService } from "../tools/send/services/send-download.service";
// Polyfills
global.DOMParser = new jsdom.JSDOM().window.DOMParser;
@@ -283,6 +284,7 @@ export class ServiceContainer {
cipherAuthorizationService: CipherAuthorizationService;
ssoUrlService: SsoUrlService;
masterPasswordApiService: MasterPasswordApiServiceAbstraction;
sendDownloadService: SendDownloadService;
constructor() {
let p = null;
@@ -849,6 +851,16 @@ export class ServiceContainer {
);
this.masterPasswordApiService = new MasterPasswordApiService(this.apiService, this.logService);
this.sendDownloadService = new SendDownloadService(
this.environmentService,
this.encryptService,
this.apiService,
this.platformUtilsService,
this.keyService,
this.cryptoFunctionService,
this.sendApiService,
);
}
async logout() {

View File

@@ -14,6 +14,7 @@ import { SendService } from "@bitwarden/common/tools/send/services/send.service.
import { DownloadCommand } from "../../../commands/download.command";
import { Response } from "../../../models/response";
import { SendResponse } from "../models/send.response";
import { SendDownloadService } from "../services/send-download.service";
export class SendGetCommand extends DownloadCommand {
constructor(
@@ -22,6 +23,7 @@ export class SendGetCommand extends DownloadCommand {
private searchService: SearchService,
encryptService: EncryptService,
apiService: ApiService,
private sendDownloadService: SendDownloadService,
) {
super(encryptService, apiService);
}
@@ -67,6 +69,10 @@ export class SendGetCommand extends DownloadCommand {
}
}
if (options?.file || options?.output || options?.raw) {
return this.sendDownloadService.download(sends, options);
}
return selector(sends);
}

View File

@@ -23,7 +23,6 @@ import {
SendReceiveCommand,
SendRemovePasswordCommand,
} from "./commands";
import { SendDownloadCommand } from "./commands/send-download.command";
import { SendFileResponse } from "./models/send-file.response";
import { SendTextResponse } from "./models/send-text.response";
import { SendResponse } from "./models/send.response";
@@ -68,7 +67,6 @@ export class SendProgram extends BaseProgram {
.addCommand(this.editCommand())
.addCommand(this.removePasswordCommand())
.addCommand(this.deleteCommand())
.addCommand(this.downloadCommand())
.action(async (data: string, options: OptionValues) => {
const encodedJson = this.makeSendJson(data, options);
@@ -161,64 +159,19 @@ export class SendProgram extends BaseProgram {
});
}
private downloadCommand(): Command {
return (
new Command("download")
.arguments("<id>")
.description("Downloads file attached to the send owned by you.")
.option("--file", "Specifies to return the file content of a Send", true)
.option("--password <password>", "Password needed to access the Send.")
.option(
"--passwordfile <passwordfile>",
"Path to a file containing the Sends password as its first line",
)
// .option("--obj", "Return the Send's json object rather than the Send's content")
.option("--output <location>", "Specify a file path to save a File-type Send to")
.option("--raw", "Return the raw content of a Send", true)
.on("--help", () => {
writeLn("");
writeLn(" Id:");
writeLn("");
writeLn(" Search term or Send's globally unique `id`.");
writeLn("");
writeLn(
" If raw output is specified and no output filename or directory is given for",
);
writeLn(" an attachment query, the attachment content is written to stdout.");
writeLn("");
writeLn(" Examples:");
writeLn("");
writeLn(" bw send download searchText");
writeLn(" bw send download id");
writeLn(" bw send download id --file");
writeLn(" bw send download id --file --output ../Photos/photo.jpg");
writeLn(" bw send download id --file --raw");
writeLn("", true);
})
.action(async (id: string, options: OptionValues) => {
await this.exitIfLocked();
const cmd = new SendDownloadCommand(
this.serviceContainer.sendService,
this.serviceContainer.environmentService,
this.serviceContainer.searchService,
this.serviceContainer.encryptService,
this.serviceContainer.apiService,
this.serviceContainer.platformUtilsService,
this.serviceContainer.keyService,
this.serviceContainer.cryptoFunctionService,
this.serviceContainer.sendApiService,
);
const response = await cmd.run(id, options);
this.processResponse(response);
})
);
}
private getCommand(): Command {
return new Command("get")
.arguments("<id>")
.description("Get Sends owned by you.")
.option("--output <output>", "Output directory or filename for attachment.")
.option("--text", "Specifies to return the text content of a Send")
.option("--file", "Specifies to return the file content of a Send", true)
.option("--password <password>", "Password needed to access the Send.")
.option(
"--passwordfile <passwordfile>",
"Path to a file containing the Sends password as its first line",
)
.option("--raw", "Return the raw content of a Send", true)
.option("--output <output>", "Output directory or filename for attachment.")
.on("--help", () => {
writeLn("");
writeLn(" Id:");
@@ -233,6 +186,9 @@ export class SendProgram extends BaseProgram {
writeLn(" bw send get searchText");
writeLn(" bw send get id");
writeLn(" bw send get searchText --text");
writeLn(" bw send download id --file");
writeLn(" bw send download id --output ../Photos/photo.jpg");
writeLn(" bw send download id --file --raw");
writeLn("", true);
})
.action(async (id: string, options: OptionValues) => {
@@ -243,6 +199,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.searchService,
this.serviceContainer.encryptService,
this.serviceContainer.apiService,
this.serviceContainer.sendDownloadService,
);
const response = await cmd.run(id, options);
this.processResponse(response);
@@ -303,6 +260,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.searchService,
this.serviceContainer.encryptService,
this.serviceContainer.apiService,
this.serviceContainer.sendDownloadService,
);
const cmd = new SendEditCommand(
this.serviceContainer.sendService,

View File

@@ -5,7 +5,6 @@ import * as inquirer from "inquirer";
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/key-management/crypto/abstractions/encrypt.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@@ -19,7 +18,6 @@ import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/s
import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { KeyService } from "@bitwarden/key-management";
import { NodeUtils } from "@bitwarden/node/node-utils";
@@ -28,13 +26,12 @@ import { Response } from "../../../models/response";
import { SendAccessResponse } from "../models/send-access.response";
import { SendResponse } from "../models/send.response";
export class SendDownloadCommand extends DownloadCommand {
// Note: DownloadCommand is actually an abstract class
export class SendDownloadService extends DownloadCommand {
private decKey: SymmetricCryptoKey;
constructor(
private sendService: SendService,
protected environmentService: EnvironmentService,
private searchService: SearchService,
encryptService: EncryptService,
apiService: ApiService,
protected platformUtilsService: PlatformUtilsService,
@@ -45,38 +42,14 @@ export class SendDownloadCommand extends DownloadCommand {
super(encryptService, apiService);
}
async run(id: string, options: OptionValues) {
const serveCommand = process.env.BW_SERVE === "true";
if (serveCommand && !Utils.isGuid(id)) {
return Response.badRequest("`" + id + "` is not a GUID.");
}
let sends = await this.getSendView(id);
if (sends == null) {
return Response.notFound();
}
async download(sends: SendView, options: OptionValues) {
const env = await firstValueFrom(this.environmentService.environment$);
const webVaultUrl = env.getWebVaultUrl();
const selector = async (s: SendView): Promise<Response> =>
Response.success(new SendResponse(s, webVaultUrl));
// we have multiple results. Attempt to narrow down the results
if (Array.isArray(sends)) {
// if we have multiple results, return the ids
if (sends.length > 1) {
return Response.multipleResults(sends.map((s) => s.id));
}
// if we greater than zero results pick the first one
if (sends.length > 0) {
sends = sends[0];
} else {
return Response.notFound();
}
}
// only attempt to download if requested
if (options?.file || options?.output || options?.raw) {
// generate the send url
const sendWithUrl = new SendResponse(sends, webVaultUrl);
@@ -142,6 +115,7 @@ export class SendDownloadCommand extends DownloadCommand {
return selector(sends);
}
private createUrlObject(url: string): URL | null {
try {
return new URL(url);
@@ -160,23 +134,6 @@ export class SendDownloadCommand extends DownloadCommand {
return Utils.fromBufferToB64(passwordHash);
}
private async getSendView(id: string): Promise<SendView | SendView[]> {
if (Utils.isGuid(id)) {
const send = await this.sendService.getFromState(id);
if (send != null) {
return await send.decrypt();
}
} else if (id.trim() !== "") {
let sends = await this.sendService.getAllDecryptedFromState();
sends = this.searchService.searchSends(sends, id);
if (sends.length > 1) {
return sends;
} else if (sends.length > 0) {
return sends[0];
}
}
}
private async sendRequest(
url: string,
id: string,