bitwarden-estensione-browser/apps/cli/src/commands/serve.command.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

428 lines
14 KiB
TypeScript
Raw Normal View History

import * as koaMulter from "@koa/multer";
import * as koaRouter from "@koa/router";
2022-01-19 16:45:14 +01:00
import * as program from "commander";
import * as koa from "koa";
import * as koaBodyParser from "koa-bodyparser";
import * as koaJson from "koa-json";
2022-01-19 16:45:14 +01:00
2022-06-14 17:10:53 +02:00
import { KeySuffixOptions } from "@bitwarden/common/enums/keySuffixOptions";
import { Utils } from "@bitwarden/common/misc/utils";
2022-03-03 18:24:41 +01:00
Auth/ps 2298 reorg auth (#4564) * Move auth service factories to Auth team * Move authentication componenets to Auth team * Move auth guard services to Auth team * Move Duo content script to Auth team * Move auth CLI commands to Auth team * Move Desktop Account components to Auth Team * Move Desktop guards to Auth team * Move two-factor provider images to Auth team * Move web Accounts components to Auth Team * Move web settings components to Auth Team * Move web two factor images to Auth Team * Fix missed import changes for Auth Team * Fix Linting errors * Fix missed CLI imports * Fix missed Desktop imports * Revert images move * Fix missed imports in Web * Move angular lib components to Auth Team * Move angular auth guards to Auth team * Move strategy specs to Auth team * Update .eslintignore for new paths * Move lib common abstractions to Auth team * Move services to Auth team * Move common lib enums to Auth team * Move webauthn iframe to Auth team * Move lib common domain models to Auth team * Move common lib requests to Auth team * Move response models to Auth team * Clean up whitelist * Move bit web components to Auth team * Move SSO and SCIM files to Auth team * Revert move SCIM to Auth team SCIM belongs to Admin Console team * Move captcha to Auth team * Move key connector to Auth team * Move emergency access to auth team * Delete extra file * linter fixes * Move kdf config to auth team * Fix whitelist * Fix duo autoformat * Complete two factor provider request move * Fix whitelist names * Fix login capitalization * Revert hint dependency reordering * Revert hint dependency reordering * Revert hint component This components is being picked up as a move between clients * Move web hint component to Auth team * Move new files to auth team * Fix desktop build * Fix browser build
2023-02-06 22:53:37 +01:00
import { LockCommand } from "../auth/commands/lock.command";
import { UnlockCommand } from "../auth/commands/unlock.command";
2022-01-19 16:45:14 +01:00
import { Main } from "../bw";
import { Response } from "../models/response";
import { FileResponse } from "../models/response/file.response";
[SG-998] and [SG-999] Vault and Autofill team refactor (#4542) * Move DeprecatedVaultFilterService to vault folder * [libs] move VaultItemsComponent * [libs] move AddEditComponent * [libs] move AddEditCustomFields * [libs] move attachmentsComponent * [libs] folderAddEditComponent * [libs] IconComponent * [libs] PasswordRepormptComponent * [libs] PremiumComponent * [libs] ViewCustomFieldsComponent * [libs] ViewComponent * [libs] PasswordRepromptService * [libs] Move FolderService and FolderApiService abstractions * [libs] FolderService imports * [libs] PasswordHistoryComponent * [libs] move Sync and SyncNotifier abstractions * [libs] SyncService imports * [libs] fix file casing for passwordReprompt abstraction * [libs] SyncNotifier import fix * [libs] CipherServiceAbstraction * [libs] PasswordRepromptService abstraction * [libs] Fix file casing for angular passwordReprompt service * [libs] fix file casing for SyncNotifierService * [libs] CipherRepromptType * [libs] rename CipherRepromptType * [libs] CipherType * [libs] Rename CipherType * [libs] CipherData * [libs] FolderData * [libs] PasswordHistoryData * [libs] AttachmentData * [libs] CardData * [libs] FieldData * [libs] IdentityData * [libs] LocalData * [libs] LoginData * [libs] SecureNoteData * [libs] LoginUriData * [libs] Domain classes * [libs] SecureNote * [libs] Request models * [libs] Response models * [libs] View part 1 * [libs] Views part 2 * [libs] Move folder services * [libs] Views fixes * [libs] Move sync services * [libs] cipher service * [libs] Types * [libs] Sync file casing * [libs] Fix folder service import * [libs] Move spec files * [libs] casing fixes on spec files * [browser] Autofill background, clipboard, commands * [browser] Fix ContextMenusBackground casing * [browser] Rename fix * [browser] Autofill content * [browser] autofill.js * [libs] enpass importer spec fix * [browser] autofill models * [browser] autofill manifest path updates * [browser] Autofill notification files * [browser] autofill services * [browser] Fix file casing * [browser] Vault popup loose components * [browser] Vault components * [browser] Manifest fixes * [browser] Vault services * [cli] vault commands and models * [browser] File capitilization fixes * [desktop] Vault components and services * [web] vault loose components * [web] Vault components * [browser] Fix misc-utils import * [libs] Fix psono spec imports * [fix] Add comments to address lint rules
2023-01-31 22:08:37 +01:00
import { CreateCommand } from "../vault/create.command";
import { DeleteCommand } from "../vault/delete.command";
import { SyncCommand } from "../vault/sync.command";
2022-01-19 16:45:14 +01:00
import { ConfirmCommand } from "./confirm.command";
import { EditCommand } from "./edit.command";
import { GenerateCommand } from "./generate.command";
import { GetCommand } from "./get.command";
import { ListCommand } from "./list.command";
import { RestoreCommand } from "./restore.command";
import { SendCreateCommand } from "./send/create.command";
import { SendDeleteCommand } from "./send/delete.command";
import { SendEditCommand } from "./send/edit.command";
import { SendGetCommand } from "./send/get.command";
import { SendListCommand } from "./send/list.command";
import { SendRemovePasswordCommand } from "./send/remove-password.command";
2022-03-03 18:24:41 +01:00
import { ShareCommand } from "./share.command";
import { StatusCommand } from "./status.command";
2022-01-19 16:45:14 +01:00
export class ServeCommand {
private listCommand: ListCommand;
private getCommand: GetCommand;
private createCommand: CreateCommand;
private editCommand: EditCommand;
private generateCommand: GenerateCommand;
private shareCommand: ShareCommand;
private statusCommand: StatusCommand;
private syncCommand: SyncCommand;
private deleteCommand: DeleteCommand;
private confirmCommand: ConfirmCommand;
private restoreCommand: RestoreCommand;
private lockCommand: LockCommand;
private unlockCommand: UnlockCommand;
private sendCreateCommand: SendCreateCommand;
private sendDeleteCommand: SendDeleteCommand;
private sendEditCommand: SendEditCommand;
private sendGetCommand: SendGetCommand;
private sendListCommand: SendListCommand;
private sendRemovePasswordCommand: SendRemovePasswordCommand;
constructor(protected main: Main) {
this.getCommand = new GetCommand(
this.main.cipherService,
this.main.folderService,
this.main.collectionService,
this.main.totpService,
this.main.auditService,
this.main.cryptoService,
this.main.stateService,
this.main.searchService,
this.main.apiService,
this.main.organizationService
);
this.listCommand = new ListCommand(
this.main.cipherService,
this.main.folderService,
this.main.collectionService,
this.main.organizationService,
this.main.searchService,
[EC 784] Refactor organization user service (#4163) * [EC-784] Introduce OrganizationUserService and abstraction * [EC-784] Move API response models into abstraction folder * [EC-784] Register OrganizationUserService in JsLib * [EC-784] Add OrganizationUserService to CLI Main * [EC-784] Move getOrganizationUser() - Move getOrganizationUser() implementation to OrganizationUserService - Update any references to the API service in the CLI and Web projects * [EC-784] Move getOrganizationUserGroups() * [EC-784] Move and rename getOrganizationUsers() * [EC-784] Move getOrganizationUserResetPasswordDetails() * [EC-784] Move OrganizationUser API request models into abstraction folder * [EC-784] Move postOrganizationUserInvite() * [EC-784] Move postOrganizationUserReinvite() * [EC-784] Move postManyOrganizationUserReinvite() Also tweak the signature to avoid exposing the API request model * [EC-784] Move postOrganizationUserAccept() * [EC-784] Move postOrganizationUserConfirm() * [EC-784] Move postOrganizationUsersPublicKey() Also modify signature to avoid exposing API request model * [EC-784] Move postOrganizationUserBulkConfirm() * [EC-784] Move putOrganizationUser() * [EC-784] Move putOrganizationUserGroups() * [EC-784] Update abstraction method definitions to use abstract keyword * [EC-784] Move putOrganizationUserResetPasswordEnrollment() * [EC-784] Move putOrganizationUserResetPassword() * [EC-784] Move deleteOrganizationUser() * [EC-784] Move deleteManyOrganizationUsers() * [EC-784] Move revokeOrganizationUser() * [EC-784] Move revokeManyOrganizationUsers() * [EC-784] Move restoreOrganizationUser() * [EC-784] Move restoreManyOrganizationUsers() * [EC-784] Move internal OrganizationUserBulkRequest model out of service abstraction * [EC-784] Rename organizationUser folder to organization-user
2022-12-19 19:56:16 +01:00
this.main.organizationUserService,
2022-01-19 16:45:14 +01:00
this.main.apiService
);
this.createCommand = new CreateCommand(
this.main.cipherService,
this.main.folderService,
this.main.stateService,
this.main.cryptoService,
this.main.apiService,
this.main.folderApiService
2022-01-19 16:45:14 +01:00
);
this.editCommand = new EditCommand(
this.main.cipherService,
this.main.folderService,
this.main.cryptoService,
this.main.apiService,
this.main.folderApiService
2022-01-19 16:45:14 +01:00
);
this.generateCommand = new GenerateCommand(
this.main.passwordGenerationService,
this.main.stateService
);
2022-01-19 16:45:14 +01:00
this.syncCommand = new SyncCommand(this.main.syncService);
this.statusCommand = new StatusCommand(
this.main.environmentService,
this.main.syncService,
this.main.stateService,
this.main.authService
2022-01-19 16:45:14 +01:00
);
this.deleteCommand = new DeleteCommand(
this.main.cipherService,
this.main.folderService,
this.main.stateService,
this.main.apiService,
this.main.folderApiService
2022-01-19 16:45:14 +01:00
);
[EC 784] Refactor organization user service (#4163) * [EC-784] Introduce OrganizationUserService and abstraction * [EC-784] Move API response models into abstraction folder * [EC-784] Register OrganizationUserService in JsLib * [EC-784] Add OrganizationUserService to CLI Main * [EC-784] Move getOrganizationUser() - Move getOrganizationUser() implementation to OrganizationUserService - Update any references to the API service in the CLI and Web projects * [EC-784] Move getOrganizationUserGroups() * [EC-784] Move and rename getOrganizationUsers() * [EC-784] Move getOrganizationUserResetPasswordDetails() * [EC-784] Move OrganizationUser API request models into abstraction folder * [EC-784] Move postOrganizationUserInvite() * [EC-784] Move postOrganizationUserReinvite() * [EC-784] Move postManyOrganizationUserReinvite() Also tweak the signature to avoid exposing the API request model * [EC-784] Move postOrganizationUserAccept() * [EC-784] Move postOrganizationUserConfirm() * [EC-784] Move postOrganizationUsersPublicKey() Also modify signature to avoid exposing API request model * [EC-784] Move postOrganizationUserBulkConfirm() * [EC-784] Move putOrganizationUser() * [EC-784] Move putOrganizationUserGroups() * [EC-784] Update abstraction method definitions to use abstract keyword * [EC-784] Move putOrganizationUserResetPasswordEnrollment() * [EC-784] Move putOrganizationUserResetPassword() * [EC-784] Move deleteOrganizationUser() * [EC-784] Move deleteManyOrganizationUsers() * [EC-784] Move revokeOrganizationUser() * [EC-784] Move revokeManyOrganizationUsers() * [EC-784] Move restoreOrganizationUser() * [EC-784] Move restoreManyOrganizationUsers() * [EC-784] Move internal OrganizationUserBulkRequest model out of service abstraction * [EC-784] Rename organizationUser folder to organization-user
2022-12-19 19:56:16 +01:00
this.confirmCommand = new ConfirmCommand(
this.main.apiService,
this.main.cryptoService,
this.main.organizationUserService
);
2022-01-19 16:45:14 +01:00
this.restoreCommand = new RestoreCommand(this.main.cipherService);
this.shareCommand = new ShareCommand(this.main.cipherService);
this.lockCommand = new LockCommand(this.main.vaultTimeoutService);
this.unlockCommand = new UnlockCommand(
this.main.cryptoService,
this.main.stateService,
this.main.cryptoFunctionService,
this.main.apiService,
this.main.logService,
this.main.keyConnectorService,
this.main.environmentService,
this.main.syncService,
this.main.organizationApiService,
async () => await this.main.logout()
2022-01-19 16:45:14 +01:00
);
this.sendCreateCommand = new SendCreateCommand(
this.main.sendService,
this.main.stateService,
this.main.environmentService
);
this.sendDeleteCommand = new SendDeleteCommand(this.main.sendService);
this.sendGetCommand = new SendGetCommand(
this.main.sendService,
this.main.environmentService,
this.main.searchService,
this.main.cryptoService
);
this.sendEditCommand = new SendEditCommand(
this.main.sendService,
this.main.stateService,
this.sendGetCommand
);
this.sendListCommand = new SendListCommand(
this.main.sendService,
this.main.environmentService,
this.main.searchService
);
this.sendRemovePasswordCommand = new SendRemovePasswordCommand(this.main.sendService);
}
async run(options: program.OptionValues) {
const protectOrigin = !options.disableOriginProtection;
2022-01-19 16:45:14 +01:00
const port = options.port || 8087;
const hostname = options.hostname || "localhost";
this.main.logService.info(
`Starting server on ${hostname}:${port} with ${
protectOrigin ? "origin protection" : "no origin protection"
}`
);
const server = new koa();
const router = new koaRouter();
2022-01-19 16:45:14 +01:00
process.env.BW_SERVE = "true";
process.env.BW_NOINTERACTION = "true";
server
.use(async (ctx, next) => {
if (protectOrigin && ctx.headers.origin != undefined) {
ctx.status = 403;
this.main.logService.warning(
`Blocking request from "${
Utils.isNullOrEmpty(ctx.headers.origin)
? "(Origin header value missing)"
: ctx.headers.origin
}"`
);
return;
}
await next();
})
.use(koaBodyParser())
.use(koaJson({ pretty: false, param: "pretty" }));
2022-01-19 16:45:14 +01:00
router.get("/generate", async (ctx, next) => {
const response = await this.generateCommand.run(ctx.request.query);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.get("/status", async (ctx, next) => {
2022-01-19 16:45:14 +01:00
const response = await this.statusCommand.run();
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.get("/list/object/:object", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
2022-01-19 16:45:14 +01:00
let response: Response = null;
if (ctx.params.object === "send") {
response = await this.sendListCommand.run(ctx.request.query);
2022-01-19 16:45:14 +01:00
} else {
response = await this.listCommand.run(ctx.params.object, ctx.request.query);
2022-01-19 16:45:14 +01:00
}
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.get("/send/list", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
const response = await this.sendListCommand.run(ctx.request.query);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/sync", async (ctx, next) => {
const response = await this.syncCommand.run(ctx.request.query);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/lock", async (ctx, next) => {
2022-01-19 16:45:14 +01:00
const response = await this.lockCommand.run();
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/unlock", async (ctx, next) => {
2022-01-19 16:45:14 +01:00
const response = await this.unlockCommand.run(
ctx.request.body.password == null ? null : (ctx.request.body.password as string),
ctx.request.query
2022-01-19 16:45:14 +01:00
);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/confirm/:object/:id", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
const response = await this.confirmCommand.run(
ctx.params.object,
ctx.params.id,
ctx.request.query
);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/restore/:object/:id", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
const response = await this.restoreCommand.run(ctx.params.object, ctx.params.id);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/move/:id/:organizationId", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
2022-01-19 16:45:14 +01:00
const response = await this.shareCommand.run(
ctx.params.id,
ctx.params.organizationId,
ctx.request.body // TODO: Check the format of this body for an array of collection ids
2022-01-19 16:45:14 +01:00
);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/attachment", koaMulter().single("file"), async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
const response = await this.createCommand.run(
"attachment",
ctx.request.body,
ctx.request.query,
{
fileBuffer: ctx.request.file.buffer,
fileName: ctx.request.file.originalname,
}
);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/send/:id/remove-password", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
const response = await this.sendRemovePasswordCommand.run(ctx.params.id);
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.post("/object/:object", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
2022-01-19 16:45:14 +01:00
let response: Response = null;
if (ctx.params.object === "send") {
response = await this.sendCreateCommand.run(ctx.request.body, ctx.request.query);
2022-01-19 16:45:14 +01:00
} else {
response = await this.createCommand.run(
ctx.params.object,
ctx.request.body,
ctx.request.query
);
2022-01-19 16:45:14 +01:00
}
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.put("/object/:object/:id", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
2022-01-19 16:45:14 +01:00
let response: Response = null;
if (ctx.params.object === "send") {
ctx.request.body.id = ctx.params.id;
response = await this.sendEditCommand.run(ctx.request.body, ctx.request.query);
2022-01-19 16:45:14 +01:00
} else {
response = await this.editCommand.run(
ctx.params.object,
ctx.params.id,
ctx.request.body,
ctx.request.query
2022-01-19 16:45:14 +01:00
);
}
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.get("/object/:object/:id", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
2022-01-19 16:45:14 +01:00
let response: Response = null;
if (ctx.params.object === "send") {
response = await this.sendGetCommand.run(ctx.params.id, null);
2022-01-19 16:45:14 +01:00
} else {
response = await this.getCommand.run(ctx.params.object, ctx.params.id, ctx.request.query);
2022-01-19 16:45:14 +01:00
}
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
router.delete("/object/:object/:id", async (ctx, next) => {
if (await this.errorIfLocked(ctx.response)) {
await next();
return;
}
2022-01-19 16:45:14 +01:00
let response: Response = null;
if (ctx.params.object === "send") {
response = await this.sendDeleteCommand.run(ctx.params.id);
2022-01-19 16:45:14 +01:00
} else {
response = await this.deleteCommand.run(
ctx.params.object,
ctx.params.id,
ctx.request.query
);
2022-01-19 16:45:14 +01:00
}
this.processResponse(ctx.response, response);
await next();
2022-01-19 16:45:14 +01:00
});
server
.use(router.routes())
.use(router.allowedMethods())
.listen(port, hostname === "all" ? null : hostname, () => {
this.main.logService.info("Listening on " + hostname + ":" + port);
});
2022-01-19 16:45:14 +01:00
}
private processResponse(res: koa.Response, commandResponse: Response) {
2022-01-19 16:45:14 +01:00
if (!commandResponse.success) {
res.status = 400;
2022-01-19 16:45:14 +01:00
}
if (commandResponse.data instanceof FileResponse) {
res.body = commandResponse.data.data;
res.attachment(commandResponse.data.fileName);
res.set("Content-Type", "application/octet-stream");
res.set("Content-Length", commandResponse.data.data.length.toString());
2022-01-19 16:45:14 +01:00
} else {
res.body = commandResponse;
}
}
private async errorIfLocked(res: koa.Response) {
const authed = await this.main.stateService.getIsAuthenticated();
if (!authed) {
this.processResponse(res, Response.error("You are not logged in."));
return true;
}
if (await this.main.cryptoService.hasKeyInMemory()) {
return false;
} else if (await this.main.cryptoService.hasKeyStored(KeySuffixOptions.Auto)) {
// load key into memory
await this.main.cryptoService.getKey();
return false;
2022-01-19 16:45:14 +01:00
}
this.processResponse(res, Response.error("Vault is locked."));
return true;
2022-01-19 16:45:14 +01:00
}
}