mirror of
https://github.com/bitwarden/clients.git
synced 2026-07-10 21:03:56 +08: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
128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
import * as program from "commander";
|
|
import * as inquirer from "inquirer";
|
|
|
|
import { ImportService } from "@bitwarden/common/abstractions/import.service";
|
|
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
|
import { ImportType } from "@bitwarden/common/enums/importOptions";
|
|
import { Importer } from "@bitwarden/common/importers/importer";
|
|
|
|
import { Response } from "../models/response";
|
|
import { MessageResponse } from "../models/response/message.response";
|
|
import { CliUtils } from "../utils";
|
|
|
|
export class ImportCommand {
|
|
constructor(
|
|
private importService: ImportService,
|
|
private organizationService: OrganizationService
|
|
) {}
|
|
|
|
async run(
|
|
format: ImportType,
|
|
filepath: string,
|
|
options: program.OptionValues
|
|
): Promise<Response> {
|
|
const organizationId = options.organizationid;
|
|
if (organizationId != null) {
|
|
const organization = await this.organizationService.getFromState(organizationId);
|
|
|
|
if (organization == null) {
|
|
return Response.badRequest(
|
|
`You do not belong to an organization with the ID of ${organizationId}. Check the organization ID and sync your vault.`
|
|
);
|
|
}
|
|
|
|
if (!organization.canAccessImportExport) {
|
|
return Response.badRequest(
|
|
"You are not authorized to import into the provided organization."
|
|
);
|
|
}
|
|
}
|
|
|
|
if (options.formats || false) {
|
|
return await this.list();
|
|
} else {
|
|
return await this.import(format, filepath, organizationId);
|
|
}
|
|
}
|
|
|
|
private async import(format: ImportType, filepath: string, organizationId: string) {
|
|
if (format == null) {
|
|
return Response.badRequest("`format` was not provided.");
|
|
}
|
|
if (filepath == null || filepath === "") {
|
|
return Response.badRequest("`filepath` was not provided.");
|
|
}
|
|
|
|
const importer = await this.importService.getImporter(format, organizationId);
|
|
if (importer === null) {
|
|
return Response.badRequest("Proper importer type required.");
|
|
}
|
|
|
|
try {
|
|
let contents;
|
|
if (format === "1password1pux") {
|
|
contents = await CliUtils.extract1PuxContent(filepath);
|
|
} else {
|
|
contents = await CliUtils.readFile(filepath);
|
|
}
|
|
|
|
if (contents === null || contents === "") {
|
|
return Response.badRequest("Import file was empty.");
|
|
}
|
|
|
|
const response = await this.doImport(importer, contents, organizationId);
|
|
if (response.success) {
|
|
response.data = new MessageResponse("Imported " + filepath, null);
|
|
}
|
|
return response;
|
|
} catch (err) {
|
|
return Response.badRequest(err);
|
|
}
|
|
}
|
|
|
|
private async list() {
|
|
const options = this.importService
|
|
.getImportOptions()
|
|
.sort((a, b) => {
|
|
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
})
|
|
.map((option) => option.id)
|
|
.join("\n");
|
|
const res = new MessageResponse("Supported input formats:", options);
|
|
res.raw = options;
|
|
return Response.success(res);
|
|
}
|
|
|
|
private async doImport(
|
|
importer: Importer,
|
|
contents: string,
|
|
organizationId?: string
|
|
): Promise<Response> {
|
|
const err = await this.importService.import(importer, contents, organizationId);
|
|
if (err != null) {
|
|
if (err.passwordRequired) {
|
|
importer = this.importService.getImporter(
|
|
"bitwardenpasswordprotected",
|
|
organizationId,
|
|
await this.promptPassword()
|
|
);
|
|
return this.doImport(importer, contents, organizationId);
|
|
}
|
|
return Response.badRequest(err.message);
|
|
}
|
|
|
|
return Response.success();
|
|
}
|
|
|
|
private async promptPassword() {
|
|
const answer: inquirer.Answers = await inquirer.createPromptModule({
|
|
output: process.stderr,
|
|
})({
|
|
type: "password",
|
|
name: "password",
|
|
message: "Import file password:",
|
|
});
|
|
return answer.password;
|
|
}
|
|
}
|