1
0
mirror of https://github.com/bitwarden/cli synced 2025-12-16 00:03:23 +00:00

Add send to cli (#222)

* Add list all sends and filter by search term

* Add get send templates

* Add AccessUrl to send responses

* Add Send to Get command

* Add missing command options to login

These options are already coded to work in the command, but commander
did not know about the options.

* Upgrade Commander to 7.0.0

This is needed to enable the subcommand chaining required by Send.

This commit also adds get send and send receive functionality. get send
will be moved to send get along with send list and any other send commands.

* Use api url for send access url

* Move send commands to send subcommands

* Use webvault access url everywhere

Production instances all have api url located at `baseUrl/api`.
Receive command will parse the webvault url and alter it to an api url.

* Move create and receive commands to send directory

* Separate program concerns

program holds authentication/general program concerns
vault.program holds commands related to the vault
send.program holds commands related to Bitwarden Send

* Fix up imports and lint items

* Add edit command

* Use browser-hrtime

* Add send examples to help text

* Clean up receive help text

* correct help text

* Add delete command

* Code review Cleanup

* Scheme on send receive help text

* PR review items

Move buffer to array buffer to jslib
delete with server
some formatting fixes

* Add remove password command

This is the simplest way to enable removing passwords without
resorting to weird type parsing of piped in Send JSONs in edit

* Default hidden to false like web

* Do not allow password updates that aren't strings or are empty

* Delete appveyor.yml.flagged-for-delete

* Correctly order imports and include tslint rule

* fix npm globbing problem

https://stackoverflow.com/a/34594501
globs work differently in package.json. Encasing the globs in
single quotes expands them in shell rather than in npm

* Remove double slash in path

* Trigger github rebuild
This commit is contained in:
Matt Gibson
2021-02-03 11:44:33 -06:00
committed by GitHub
parent b88091c41f
commit 57f7cf607a
36 changed files with 1759 additions and 726 deletions

View File

@@ -1,5 +1,4 @@
import * as program from 'commander';
import * as fet from 'node-fetch';
import { CipherType } from 'jslib/enums/cipherType';
@@ -8,8 +7,10 @@ import { AuditService } from 'jslib/abstractions/audit.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { SearchService } from 'jslib/abstractions/search.service';
import { SendService } from 'jslib/abstractions/send.service';
import { TotpService } from 'jslib/abstractions/totp.service';
import { UserService } from 'jslib/abstractions/user.service';
@@ -40,24 +41,32 @@ import { CollectionResponse } from '../models/response/collectionResponse';
import { FolderResponse } from '../models/response/folderResponse';
import { OrganizationCollectionResponse } from '../models/response/organizationCollectionResponse';
import { OrganizationResponse } from '../models/response/organizationResponse';
import { SendFileResponse } from '../models/response/sendFileResponse';
import { SendResponse } from '../models/response/sendResponse';
import { SendTextResponse } from '../models/response/sendTextResponse';
import { TemplateResponse } from '../models/response/templateResponse';
import { OrganizationCollectionRequest } from '../models/request/organizationCollectionRequest';
import { SelectionReadOnly } from '../models/selectionReadOnly';
import { DownloadCommand } from './download.command';
import { CliUtils } from '../utils';
import { Utils } from 'jslib/misc/utils';
export class GetCommand {
export class GetCommand extends DownloadCommand {
constructor(private cipherService: CipherService, private folderService: FolderService,
private collectionService: CollectionService, private totpService: TotpService,
private auditService: AuditService, private cryptoService: CryptoService,
private auditService: AuditService, cryptoService: CryptoService,
private userService: UserService, private searchService: SearchService,
private apiService: ApiService) { }
private apiService: ApiService, private sendService: SendService,
private environmentService: EnvironmentService) {
super(cryptoService);
}
async run(object: string, id: string, cmd: program.Command): Promise<Response> {
async run(object: string, id: string, options: program.OptionValues): Promise<Response> {
if (id != null) {
id = id.toLowerCase();
}
@@ -76,13 +85,13 @@ export class GetCommand {
case 'exposed':
return await this.getExposed(id);
case 'attachment':
return await this.getAttachment(id, cmd);
return await this.getAttachment(id, options);
case 'folder':
return await this.getFolder(id);
case 'collection':
return await this.getCollection(id);
case 'org-collection':
return await this.getOrganizationCollection(id, cmd);
return await this.getOrganizationCollection(id, options);
case 'organization':
return await this.getOrganization(id);
case 'template':
@@ -241,12 +250,12 @@ export class GetCommand {
return Response.success(res);
}
private async getAttachment(id: string, cmd: program.Command) {
if (cmd.itemid == null || cmd.itemid === '') {
private async getAttachment(id: string, options: program.OptionValues) {
if (options.itemid == null || options.itemid === '') {
return Response.badRequest('--itemid <itemid> required.');
}
const itemId = cmd.itemid.toLowerCase();
const itemId = options.itemid.toLowerCase();
const cipherResponse = await this.getCipher(itemId);
if (!cipherResponse.success) {
return cipherResponse;
@@ -273,24 +282,9 @@ export class GetCommand {
}
}
const response = await fet.default(new fet.Request(attachments[0].url, { headers: { cache: 'no-cache' } }));
if (response.status !== 200) {
return Response.error('A ' + response.status + ' error occurred while downloading the attachment.');
}
try {
const buf = await response.arrayBuffer();
const key = attachments[0].key != null ? attachments[0].key :
await this.cryptoService.getOrgKey(cipher.organizationId);
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
return await CliUtils.saveResultToFile(Buffer.from(decBuf), cmd.output, attachments[0].fileName);
} catch (e) {
if (typeof (e) === 'string') {
return Response.error(e);
} else {
return Response.error('An error occurred while saving the attachment.');
}
}
const key = attachments[0].key != null ? attachments[0].key :
await this.cryptoService.getOrgKey(cipher.organizationId);
return await this.saveAttachmentToFile(attachments[0].url, key, attachments[0].fileName, options.output);
}
private async getFolder(id: string) {
@@ -343,23 +337,23 @@ export class GetCommand {
return Response.success(res);
}
private async getOrganizationCollection(id: string, cmd: program.Command) {
if (cmd.organizationid == null || cmd.organizationid === '') {
private async getOrganizationCollection(id: string, options: program.OptionValues) {
if (options.organizationid == null || options.organizationid === '') {
return Response.badRequest('--organizationid <organizationid> required.');
}
if (!Utils.isGuid(id)) {
return Response.error('`' + id + '` is not a GUID.');
}
if (!Utils.isGuid(cmd.organizationid)) {
return Response.error('`' + cmd.organizationid + '` is not a GUID.');
if (!Utils.isGuid(options.organizationid)) {
return Response.error('`' + options.organizationid + '` is not a GUID.');
}
try {
const orgKey = await this.cryptoService.getOrgKey(cmd.organizationid);
const orgKey = await this.cryptoService.getOrgKey(options.organizationid);
if (orgKey == null) {
throw new Error('No encryption key for this organization.');
}
const response = await this.apiService.getCollectionDetails(cmd.organizationid, id);
const response = await this.apiService.getCollectionDetails(options.organizationid, id);
const decCollection = new CollectionView(response);
decCollection.name = await this.cryptoService.decryptToUtf8(
new CipherString(response.name), orgKey);
@@ -430,6 +424,15 @@ export class GetCommand {
case 'org-collection':
template = OrganizationCollectionRequest.template();
break;
case 'send':
template = SendResponse.template();
break;
case 'send.text':
template = SendTextResponse.template();
break;
case 'send.file':
template = SendFileResponse.template();
break;
default:
return Response.badRequest('Unknown template object.');
}