mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
PM-919 created a new command called send download
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
@@ -24,8 +20,6 @@ export abstract class DownloadCommand {
|
||||
constructor(
|
||||
protected encryptService: EncryptService,
|
||||
protected apiService: ApiService,
|
||||
protected environmentService: EnvironmentService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -68,23 +62,4 @@ export abstract class DownloadCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected getIdAndKey(url: URL): [string, string] {
|
||||
const result = url.hash.slice(1).split("/").slice(-2);
|
||||
return [result[0], result[1]];
|
||||
}
|
||||
|
||||
protected async getApiUrl(url: URL) {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
const urls = env.getUrls();
|
||||
if (url.origin === "https://send.bitwarden.com") {
|
||||
return "https://api.bitwarden.com";
|
||||
} else if (url.origin === urls.api) {
|
||||
return url.origin;
|
||||
} else if (this.platformUtilsService.isDev() && url.origin === urls.webVault) {
|
||||
return urls.api;
|
||||
} else {
|
||||
return url.origin + "/api";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import { LoginUriExport } from "@bitwarden/common/models/export/login-uri.export
|
||||
import { LoginExport } from "@bitwarden/common/models/export/login.export";
|
||||
import { SecureNoteExport } from "@bitwarden/common/models/export/secure-note.export";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
@@ -68,10 +66,8 @@ export class GetCommand extends DownloadCommand {
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
private accountService: AccountService,
|
||||
protected environmentService: EnvironmentService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
) {
|
||||
super(encryptService, apiService, environmentService, platformUtilsService);
|
||||
super(encryptService, apiService);
|
||||
}
|
||||
|
||||
async run(object: string, id: string, cmdOptions: Record<string, any>): Promise<Response> {
|
||||
|
||||
@@ -47,6 +47,7 @@ export class OssServeConfigurator {
|
||||
private sendCreateCommand: SendCreateCommand;
|
||||
private sendDeleteCommand: SendDeleteCommand;
|
||||
private sendEditCommand: SendEditCommand;
|
||||
|
||||
private sendGetCommand: SendGetCommand;
|
||||
private sendListCommand: SendListCommand;
|
||||
private sendRemovePasswordCommand: SendRemovePasswordCommand;
|
||||
@@ -66,8 +67,6 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.platformUtilsService,
|
||||
);
|
||||
this.listCommand = new ListCommand(
|
||||
this.serviceContainer.cipherService,
|
||||
@@ -166,10 +165,6 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.searchService,
|
||||
this.serviceContainer.encryptService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.platformUtilsService,
|
||||
this.serviceContainer.keyService,
|
||||
this.serviceContainer.cryptoFunctionService,
|
||||
this.serviceContainer.sendApiService,
|
||||
);
|
||||
this.sendEditCommand = new SendEditCommand(
|
||||
this.serviceContainer.sendService,
|
||||
|
||||
@@ -1,48 +1,29 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { OptionValues } from "commander";
|
||||
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";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
import { SendAccess } from "@bitwarden/common/tools/send/models/domain/send-access";
|
||||
import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request";
|
||||
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";
|
||||
|
||||
import { DownloadCommand } from "../../../commands/download.command";
|
||||
import { Response } from "../../../models/response";
|
||||
import { SendAccessResponse } from "../models/send-access.response";
|
||||
import { SendResponse } from "../models/send.response";
|
||||
|
||||
export class SendGetCommand extends DownloadCommand {
|
||||
private decKey: SymmetricCryptoKey;
|
||||
|
||||
constructor(
|
||||
private sendService: SendService,
|
||||
protected environmentService: EnvironmentService,
|
||||
private environmentService: EnvironmentService,
|
||||
private searchService: SearchService,
|
||||
encryptService: EncryptService,
|
||||
apiService: ApiService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
private keyService: KeyService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private sendApiService: SendApiService,
|
||||
) {
|
||||
super(encryptService, apiService, environmentService, platformUtilsService);
|
||||
super(encryptService, apiService);
|
||||
}
|
||||
|
||||
async run(id: string, options: OptionValues) {
|
||||
@@ -63,7 +44,7 @@ export class SendGetCommand extends DownloadCommand {
|
||||
Response.success(new SendResponse(s, webVaultUrl));
|
||||
if (!serveCommand && options?.text != null) {
|
||||
filter = (s) => {
|
||||
return filter(s) && s.text != null;
|
||||
return s.text != null;
|
||||
};
|
||||
selector = async (s) => {
|
||||
// Write to stdout and response success so we get the text string only to stdout
|
||||
@@ -86,84 +67,8 @@ export class SendGetCommand extends DownloadCommand {
|
||||
}
|
||||
}
|
||||
|
||||
if (options?.file || options?.output || options?.raw) {
|
||||
const sendWithUrl = new SendResponse(sends, webVaultUrl);
|
||||
|
||||
const urlObject = this.createUrlObject(sendWithUrl.accessUrl);
|
||||
if (urlObject == null) {
|
||||
return Response.badRequest("Failed to parse the provided Send url");
|
||||
}
|
||||
|
||||
const apiUrl = await this.getApiUrl(urlObject);
|
||||
const [id, key] = this.getIdAndKey(urlObject);
|
||||
if (Utils.isNullOrWhitespace(id) || Utils.isNullOrWhitespace(key)) {
|
||||
return Response.badRequest("Failed to parse url, the url provided is not a valid Send url");
|
||||
}
|
||||
|
||||
const keyArray = Utils.fromUrlB64ToArray(key);
|
||||
const password =
|
||||
options.password ||
|
||||
(options.passwordfile && (await NodeUtils.readFirstLine(options.passwordfile))) ||
|
||||
(options.passwordenv && process.env[options.passwordenv]) ||
|
||||
"";
|
||||
|
||||
const sendAccessRequest = new SendAccessRequest();
|
||||
if (password !== "") {
|
||||
sendAccessRequest.password = await this.getUnlockedPassword(password, keyArray);
|
||||
}
|
||||
|
||||
const response = await this.sendRequest(apiUrl, id, keyArray, sendAccessRequest);
|
||||
if (response instanceof Response) {
|
||||
// Error scenario
|
||||
return response;
|
||||
}
|
||||
|
||||
if (options.obj != null) {
|
||||
return Response.success(new SendAccessResponse(response));
|
||||
}
|
||||
|
||||
switch (response.type) {
|
||||
case SendType.Text:
|
||||
// Write to stdout and response success so we get the text string only to stdout
|
||||
process.stdout.write(response?.text?.text);
|
||||
return Response.success();
|
||||
case SendType.File: {
|
||||
const downloadData = await this.sendApiService.getSendFileDownloadData(
|
||||
response,
|
||||
sendAccessRequest,
|
||||
apiUrl,
|
||||
);
|
||||
return await this.saveAttachmentToFile(
|
||||
downloadData.url,
|
||||
this.decKey,
|
||||
response?.file?.fileName,
|
||||
options.output,
|
||||
);
|
||||
}
|
||||
default:
|
||||
return Response.success(new SendAccessResponse(response));
|
||||
}
|
||||
}
|
||||
|
||||
return selector(sends);
|
||||
}
|
||||
private createUrlObject(url: string): URL | null {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async getUnlockedPassword(password: string, keyArray: Uint8Array) {
|
||||
const passwordHash = await this.cryptoFunctionService.pbkdf2(
|
||||
password,
|
||||
keyArray,
|
||||
"sha256",
|
||||
100000,
|
||||
);
|
||||
return Utils.fromBufferToB64(passwordHash);
|
||||
}
|
||||
|
||||
private async getSendView(id: string): Promise<SendView | SendView[]> {
|
||||
if (Utils.isGuid(id)) {
|
||||
@@ -181,40 +86,4 @@ export class SendGetCommand extends DownloadCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async sendRequest(
|
||||
url: string,
|
||||
id: string,
|
||||
key: Uint8Array,
|
||||
sendAccessRequest: SendAccessRequest,
|
||||
): Promise<Response | SendAccessView> {
|
||||
try {
|
||||
const sendResponse = await this.sendApiService.postSendAccess(id, sendAccessRequest, url);
|
||||
|
||||
const sendAccess = new SendAccess(sendResponse);
|
||||
this.decKey = await this.keyService.makeSendKey(key);
|
||||
return await sendAccess.decrypt(this.decKey);
|
||||
} catch (e) {
|
||||
if (e instanceof ErrorResponse) {
|
||||
if (e.statusCode === 401) {
|
||||
const answer: inquirer.Answers = await inquirer.createPromptModule({
|
||||
output: process.stderr,
|
||||
})({
|
||||
type: "password",
|
||||
name: "password",
|
||||
message: "Send password:",
|
||||
});
|
||||
|
||||
// reattempt with new password
|
||||
sendAccessRequest.password = await this.getUnlockedPassword(answer.password, key);
|
||||
return await this.sendRequest(url, id, key, sendAccessRequest);
|
||||
} else if (e.statusCode === 405) {
|
||||
return Response.badRequest("Bad Request");
|
||||
} else if (e.statusCode === 404) {
|
||||
return Response.notFound();
|
||||
}
|
||||
}
|
||||
return Response.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
import { OptionValues } from "commander";
|
||||
import * as inquirer from "inquirer";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
@@ -37,7 +38,7 @@ export class SendReceiveCommand extends DownloadCommand {
|
||||
private sendApiService: SendApiService,
|
||||
apiService: ApiService,
|
||||
) {
|
||||
super(encryptService, apiService, environmentService, platformUtilsService);
|
||||
super(encryptService, apiService);
|
||||
}
|
||||
|
||||
async run(url: string, options: OptionValues): Promise<Response> {
|
||||
@@ -109,6 +110,25 @@ export class SendReceiveCommand extends DownloadCommand {
|
||||
}
|
||||
}
|
||||
|
||||
protected getIdAndKey(url: URL): [string, string] {
|
||||
const result = url.hash.slice(1).split("/").slice(-2);
|
||||
return [result[0], result[1]];
|
||||
}
|
||||
|
||||
protected async getApiUrl(url: URL) {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
const urls = env.getUrls();
|
||||
if (url.origin === "https://send.bitwarden.com") {
|
||||
return "https://api.bitwarden.com";
|
||||
} else if (url.origin === urls.api) {
|
||||
return url.origin;
|
||||
} else if (this.platformUtilsService.isDev() && url.origin === urls.webVault) {
|
||||
return urls.api;
|
||||
} else {
|
||||
return url.origin + "/api";
|
||||
}
|
||||
}
|
||||
|
||||
private async getUnlockedPassword(password: string, keyArray: Uint8Array) {
|
||||
const passwordHash = await this.cryptoFunctionService.pbkdf2(
|
||||
password,
|
||||
|
||||
234
apps/cli/src/tools/send/commands/send-download.command.ts
Normal file
234
apps/cli/src/tools/send/commands/send-download.command.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { OptionValues } from "commander";
|
||||
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";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
import { SendAccess } from "@bitwarden/common/tools/send/models/domain/send-access";
|
||||
import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request";
|
||||
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";
|
||||
|
||||
import { DownloadCommand } from "../../../commands/download.command";
|
||||
import { Response } from "../../../models/response";
|
||||
import { SendAccessResponse } from "../models/send-access.response";
|
||||
import { SendResponse } from "../models/send.response";
|
||||
|
||||
export class SendDownloadCommand extends DownloadCommand {
|
||||
private decKey: SymmetricCryptoKey;
|
||||
|
||||
constructor(
|
||||
private sendService: SendService,
|
||||
protected environmentService: EnvironmentService,
|
||||
private searchService: SearchService,
|
||||
encryptService: EncryptService,
|
||||
apiService: ApiService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
private keyService: KeyService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private sendApiService: SendApiService,
|
||||
) {
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
if (options?.file || options?.output || options?.raw) {
|
||||
// generate the send url
|
||||
const sendWithUrl = new SendResponse(sends, webVaultUrl);
|
||||
|
||||
// create the url object - which will contain the id and the key
|
||||
const urlObject = this.createUrlObject(sendWithUrl.accessUrl);
|
||||
if (urlObject == null) {
|
||||
return Response.badRequest("Failed to parse the provided Send url");
|
||||
}
|
||||
|
||||
// get the url for the api and along with the id and key
|
||||
const apiUrl = await this.getApiUrl(urlObject);
|
||||
const [id, key] = this.getIdAndKey(urlObject);
|
||||
if (Utils.isNullOrWhitespace(id) || Utils.isNullOrWhitespace(key)) {
|
||||
return Response.badRequest("Failed to parse url, the url provided is not a valid Send url");
|
||||
}
|
||||
|
||||
const keyArray = Utils.fromUrlB64ToArray(key);
|
||||
const password =
|
||||
options.password ||
|
||||
(options.passwordfile && (await NodeUtils.readFirstLine(options.passwordfile))) ||
|
||||
(options.passwordenv && process.env[options.passwordenv]) ||
|
||||
"";
|
||||
|
||||
const sendAccessRequest = new SendAccessRequest();
|
||||
if (password !== "") {
|
||||
sendAccessRequest.password = await this.getUnlockedPassword(password, keyArray);
|
||||
}
|
||||
|
||||
// request the file from the server
|
||||
const response = await this.sendRequest(apiUrl, id, keyArray, sendAccessRequest);
|
||||
if (response instanceof Response) {
|
||||
// Error scenario
|
||||
return response;
|
||||
}
|
||||
|
||||
if (options.obj != null) {
|
||||
return Response.success(new SendAccessResponse(response));
|
||||
}
|
||||
|
||||
switch (response.type) {
|
||||
case SendType.Text:
|
||||
// Write to stdout and response success so we get the text string only to stdout
|
||||
process.stdout.write(response?.text?.text);
|
||||
return Response.success();
|
||||
case SendType.File: {
|
||||
const downloadData = await this.sendApiService.getSendFileDownloadData(
|
||||
response,
|
||||
sendAccessRequest,
|
||||
apiUrl,
|
||||
);
|
||||
return await this.saveAttachmentToFile(
|
||||
downloadData.url,
|
||||
this.decKey,
|
||||
response?.file?.fileName,
|
||||
options.output,
|
||||
);
|
||||
}
|
||||
default:
|
||||
return Response.success(new SendAccessResponse(response));
|
||||
}
|
||||
}
|
||||
|
||||
return selector(sends);
|
||||
}
|
||||
private createUrlObject(url: string): URL | null {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async getUnlockedPassword(password: string, keyArray: Uint8Array) {
|
||||
const passwordHash = await this.cryptoFunctionService.pbkdf2(
|
||||
password,
|
||||
keyArray,
|
||||
"sha256",
|
||||
100000,
|
||||
);
|
||||
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,
|
||||
key: Uint8Array,
|
||||
sendAccessRequest: SendAccessRequest,
|
||||
): Promise<Response | SendAccessView> {
|
||||
try {
|
||||
const sendResponse = await this.sendApiService.postSendAccess(id, sendAccessRequest, url);
|
||||
|
||||
const sendAccess = new SendAccess(sendResponse);
|
||||
this.decKey = await this.keyService.makeSendKey(key);
|
||||
return await sendAccess.decrypt(this.decKey);
|
||||
} catch (e) {
|
||||
if (e instanceof ErrorResponse) {
|
||||
if (e.statusCode === 401) {
|
||||
const answer: inquirer.Answers = await inquirer.createPromptModule({
|
||||
output: process.stderr,
|
||||
})({
|
||||
type: "password",
|
||||
name: "password",
|
||||
message: "Send password:",
|
||||
});
|
||||
|
||||
// reattempt with new password
|
||||
sendAccessRequest.password = await this.getUnlockedPassword(answer.password, key);
|
||||
return await this.sendRequest(url, id, key, sendAccessRequest);
|
||||
} else if (e.statusCode === 405) {
|
||||
return Response.badRequest("Bad Request");
|
||||
} else if (e.statusCode === 404) {
|
||||
return Response.notFound();
|
||||
}
|
||||
}
|
||||
return Response.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected getIdAndKey(url: URL): [string, string] {
|
||||
const result = url.hash.slice(1).split("/").slice(-2);
|
||||
return [result[0], result[1]];
|
||||
}
|
||||
|
||||
protected async getApiUrl(url: URL) {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
const urls = env.getUrls();
|
||||
if (url.origin === "https://send.bitwarden.com") {
|
||||
return "https://api.bitwarden.com";
|
||||
} else if (url.origin === urls.api) {
|
||||
return url.origin;
|
||||
} else if (this.platformUtilsService.isDev() && url.origin === urls.webVault) {
|
||||
return urls.api;
|
||||
} else {
|
||||
return url.origin + "/api";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ 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";
|
||||
@@ -67,6 +68,7 @@ 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);
|
||||
|
||||
@@ -153,28 +155,69 @@ export class SendProgram extends BaseProgram {
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.platformUtilsService,
|
||||
);
|
||||
const response = await cmd.run("template", object, null);
|
||||
this.processResponse(response);
|
||||
});
|
||||
}
|
||||
|
||||
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("--file", "Specifies to return the file content of a Send", true)
|
||||
.option("--password <password>", "Password needed to access the Send.")
|
||||
.option("--passwordenv <passwordenv>", "Environment variable storing the Send's password")
|
||||
.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)
|
||||
.option("--output <output>", "Output directory or filename for attachment.")
|
||||
.option("--text", "Specifies to return the text content of a Send")
|
||||
.on("--help", () => {
|
||||
writeLn("");
|
||||
@@ -189,10 +232,7 @@ export class SendProgram extends BaseProgram {
|
||||
writeLn("");
|
||||
writeLn(" bw send get searchText");
|
||||
writeLn(" bw send get id");
|
||||
writeLn(" bw send get id --text");
|
||||
writeLn(" bw send get id --file");
|
||||
writeLn(" bw send get id --file --output ../Photos/photo.jpg");
|
||||
writeLn(" bw send get id --file --raw");
|
||||
writeLn(" bw send get searchText --text");
|
||||
writeLn("", true);
|
||||
})
|
||||
.action(async (id: string, options: OptionValues) => {
|
||||
@@ -203,10 +243,6 @@ export class SendProgram extends BaseProgram {
|
||||
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);
|
||||
@@ -267,10 +303,6 @@ export class SendProgram extends BaseProgram {
|
||||
this.serviceContainer.searchService,
|
||||
this.serviceContainer.encryptService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.platformUtilsService,
|
||||
this.serviceContainer.keyService,
|
||||
this.serviceContainer.cryptoFunctionService,
|
||||
this.serviceContainer.sendApiService,
|
||||
);
|
||||
const cmd = new SendEditCommand(
|
||||
this.serviceContainer.sendService,
|
||||
|
||||
@@ -188,8 +188,6 @@ export class VaultProgram extends BaseProgram {
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.platformUtilsService,
|
||||
);
|
||||
const response = await command.run(object, id, cmd);
|
||||
this.processResponse(response);
|
||||
|
||||
Reference in New Issue
Block a user