mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
* Extract files only used in cli out of libs/node Move commands from libs/node to cli Move program from libs/node to cli Move services from libs/node to cli Move specs from libs/node to cli Naming changes based on ADR 12 Rename commands Rename models/request Rename models/response Remove entries from whitelist-capital-letters.txt * Merge lowDbStorageService into base class Move logic from extended lowdbStorage.service.ts into base-lowdb-storage.service.ts Delete lowdb-storage.service.ts Rename base-lowdb-storage.service.ts to lowdb-storage.service.ts * Merge login.command with base class program.ts - changed import temporarily to make it easier to review Remove passing in clientId, set "cli" when constructing ssoRedirectUri call Remove setting callbacks, use private methods instead Remove i18nService from constructor params Add syncService, keyConnectorService and logoutCallback to constructor Merge successCallback with handleSuccessResponse Remove validatedParams callback and added private method Move options(program.OptionValues) and set in run() Delete login.command.ts * Rename base-login.command.ts to login.command.ts * Merge base.program.ts with program.ts
150 lines
5.1 KiB
TypeScript
150 lines
5.1 KiB
TypeScript
import * as fs from "fs";
|
|
import * as path from "path";
|
|
|
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
|
import { SendService } from "@bitwarden/common/abstractions/send.service";
|
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
|
import { SendType } from "@bitwarden/common/enums/sendType";
|
|
import { NodeUtils } from "@bitwarden/common/misc/nodeUtils";
|
|
|
|
import { Response } from "../../models/response";
|
|
import { SendTextResponse } from "../../models/response/send-text.response";
|
|
import { SendResponse } from "../../models/response/send.response";
|
|
import { CliUtils } from "../../utils";
|
|
|
|
export class SendCreateCommand {
|
|
constructor(
|
|
private sendService: SendService,
|
|
private stateService: StateService,
|
|
private environmentService: EnvironmentService
|
|
) {}
|
|
|
|
async run(requestJson: any, cmdOptions: Record<string, any>) {
|
|
let req: any = null;
|
|
if (process.env.BW_SERVE !== "true" && (requestJson == null || requestJson === "")) {
|
|
requestJson = await CliUtils.readStdin();
|
|
}
|
|
|
|
if (requestJson == null || requestJson === "") {
|
|
return Response.badRequest("`requestJson` was not provided.");
|
|
}
|
|
|
|
if (typeof requestJson !== "string") {
|
|
req = requestJson;
|
|
req.deletionDate = req.deletionDate == null ? null : new Date(req.deletionDate);
|
|
req.expirationDate = req.expirationDate == null ? null : new Date(req.expirationDate);
|
|
} else {
|
|
try {
|
|
const reqJson = Buffer.from(requestJson, "base64").toString();
|
|
req = SendResponse.fromJson(reqJson);
|
|
|
|
if (req == null) {
|
|
throw new Error("Null request");
|
|
}
|
|
} catch (e) {
|
|
return Response.badRequest("Error parsing the encoded request data.");
|
|
}
|
|
}
|
|
|
|
if (
|
|
req.deletionDate == null ||
|
|
isNaN(new Date(req.deletionDate).getTime()) ||
|
|
new Date(req.deletionDate) <= new Date()
|
|
) {
|
|
return Response.badRequest("Must specify a valid deletion date after the current time");
|
|
}
|
|
|
|
if (req.expirationDate != null && isNaN(new Date(req.expirationDate).getTime())) {
|
|
return Response.badRequest("Unable to parse expirationDate: " + req.expirationDate);
|
|
}
|
|
|
|
const normalizedOptions = new Options(cmdOptions);
|
|
return this.createSend(req, normalizedOptions);
|
|
}
|
|
|
|
private async createSend(req: SendResponse, options: Options) {
|
|
const filePath = req.file?.fileName ?? options.file;
|
|
const text = req.text?.text ?? options.text;
|
|
const hidden = req.text?.hidden ?? options.hidden;
|
|
const password = req.password ?? options.password;
|
|
const maxAccessCount = req.maxAccessCount ?? options.maxAccessCount;
|
|
|
|
req.key = null;
|
|
req.maxAccessCount = maxAccessCount;
|
|
|
|
switch (req.type) {
|
|
case SendType.File:
|
|
if (process.env.BW_SERVE === "true") {
|
|
return Response.error(
|
|
"Creating a file-based Send is unsupported through the `serve` command at this time."
|
|
);
|
|
}
|
|
|
|
if (!(await this.stateService.getCanAccessPremium())) {
|
|
return Response.error("Premium status is required to use this feature.");
|
|
}
|
|
|
|
if (filePath == null) {
|
|
return Response.badRequest(
|
|
"Must specify a file to Send either with the --file option or in the request JSON."
|
|
);
|
|
}
|
|
|
|
req.file.fileName = path.basename(filePath);
|
|
break;
|
|
case SendType.Text:
|
|
if (text == null) {
|
|
return Response.badRequest(
|
|
"Must specify text content to Send either with the --text option or in the request JSON."
|
|
);
|
|
}
|
|
req.text = new SendTextResponse();
|
|
req.text.text = text;
|
|
req.text.hidden = hidden;
|
|
break;
|
|
default:
|
|
return Response.badRequest(
|
|
"Unknown Send type " + SendType[req.type] + ". Valid types are: file, text"
|
|
);
|
|
}
|
|
|
|
try {
|
|
let fileBuffer: ArrayBuffer = null;
|
|
if (req.type === SendType.File) {
|
|
fileBuffer = NodeUtils.bufferToArrayBuffer(fs.readFileSync(filePath));
|
|
}
|
|
|
|
const sendView = SendResponse.toView(req);
|
|
const [encSend, fileData] = await this.sendService.encrypt(sendView, fileBuffer, password);
|
|
// Add dates from template
|
|
encSend.deletionDate = sendView.deletionDate;
|
|
encSend.expirationDate = sendView.expirationDate;
|
|
|
|
await this.sendService.saveWithServer([encSend, fileData]);
|
|
const newSend = await this.sendService.get(encSend.id);
|
|
const decSend = await newSend.decrypt();
|
|
const res = new SendResponse(decSend, this.environmentService.getWebVaultUrl());
|
|
return Response.success(res);
|
|
} catch (e) {
|
|
return Response.error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
class Options {
|
|
file: string;
|
|
text: string;
|
|
maxAccessCount: number;
|
|
password: string;
|
|
hidden: boolean;
|
|
|
|
constructor(passedOptions: Record<string, any>) {
|
|
this.file = passedOptions?.file;
|
|
this.text = passedOptions?.text;
|
|
this.password = passedOptions?.password;
|
|
this.hidden = CliUtils.convertBooleanOption(passedOptions?.hidden);
|
|
this.maxAccessCount =
|
|
passedOptions?.maxAccessCount != null ? parseInt(passedOptions.maxAccessCount, null) : null;
|
|
}
|
|
}
|