diff --git a/jslib b/jslib index 43c0cbce45..4165a78277 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 43c0cbce452daff9bcc4c70866a56c8cbd548b4a +Subproject commit 4165a78277048d7b37319e63bd7e6473cbba5156 diff --git a/src/commands/edit.command.ts b/src/commands/edit.command.ts index 45784f2616..68a1de4c88 100644 --- a/src/commands/edit.command.ts +++ b/src/commands/edit.command.ts @@ -74,7 +74,7 @@ export class EditCommand { return Response.notFound(); } if (cipher.organizationId == null) { - return Response.error('Item does not belong to an organization. Consider sharing it first.'); + return Response.badRequest('Item does not belong to an organization. Consider sharing it first.'); } cipher.collectionIds = req; diff --git a/src/commands/share.command.ts b/src/commands/share.command.ts new file mode 100644 index 0000000000..2a5bc87952 --- /dev/null +++ b/src/commands/share.command.ts @@ -0,0 +1,58 @@ +import * as program from 'commander'; + +import { CipherService } from 'jslib/abstractions/cipher.service'; + +import { Response } from '../models/response'; +import { CipherResponse } from '../models/response/cipherResponse'; + +import { CliUtils } from '../utils'; + +export class ShareCommand { + constructor(private cipherService: CipherService) { } + + async run(id: string, organizationId: string, requestJson: string, cmd: program.Command): Promise { + if (requestJson == null || requestJson === '') { + requestJson = await CliUtils.readStdin(); + } + + if (requestJson == null || requestJson === '') { + return Response.badRequest('`requestJson` was not provided.'); + } + + let req: string[] = []; + try { + const reqJson = Buffer.from(requestJson, 'base64').toString(); + req = JSON.parse(reqJson); + if (req == null || req.length === 0) { + return Response.badRequest('You must provide at least one collection id for this item.'); + } + } catch (e) { + return Response.badRequest('Error parsing the encoded request data.'); + } + + if (id != null) { + id = id.toLowerCase(); + } + if (organizationId != null) { + organizationId = organizationId.toLowerCase(); + } + + const cipher = await this.cipherService.get(id); + if (cipher == null) { + return Response.notFound(); + } + if (cipher.organizationId != null) { + return Response.badRequest('This item already belongs to an organization.'); + } + const cipherView = await cipher.decrypt(); + try { + await this.cipherService.shareWithServer(cipherView, organizationId, req); + const updatedCipher = await this.cipherService.get(cipher.id); + const decCipher = await updatedCipher.decrypt(); + const res = new CipherResponse(decCipher); + return Response.success(res); + } catch (e) { + return Response.error(e); + } + } +} diff --git a/src/program.ts b/src/program.ts index 017d3e40b2..94857ace68 100644 --- a/src/program.ts +++ b/src/program.ts @@ -16,6 +16,7 @@ import { ListCommand } from './commands/list.command'; import { LockCommand } from './commands/lock.command'; import { LoginCommand } from './commands/login.command'; import { LogoutCommand } from './commands/logout.command'; +import { ShareCommand } from './commands/share.command'; import { SyncCommand } from './commands/sync.command'; import { UnlockCommand } from './commands/unlock.command'; import { UpdateCommand } from './commands/update.command'; @@ -374,6 +375,38 @@ export class Program { this.processResponse(response); }); + program + .command('share [encodedJson]') + .description('Share an item to an organization.') + .on('--help', () => { + writeLn('\n Id:'); + writeLn(''); + writeLn(' Item\'s globally unique `id`.'); + writeLn(''); + writeLn(' Organization Id:'); + writeLn(''); + writeLn(' Organization\'s globally unique `id`.'); + writeLn(''); + writeLn(' Notes:'); + writeLn(''); + writeLn(' `encodedJson` can also be piped into stdin. `encodedJson` contains ' + + 'an array of collection ids.'); + writeLn(''); + writeLn(' Examples:'); + writeLn(''); + writeLn(' bw share 4af958ce-96a7-45d9-beed-1e70fabaa27a 6d82949b-b44d-468a-adae-3f3bacb0ea32 ' + + 'WyI5NzQwNTNkMC0zYjMzLTRiOTgtODg2ZS1mZWNmNWM4ZGJhOTYiXQ=='); + writeLn(' echo \'["974053d0-3b33-4b98-886e-fecf5c8dba96"]\' | bw encode | ' + + 'bw share 4af958ce-96a7-45d9-beed-1e70fabaa27a 6d82949b-b44d-468a-adae-3f3bacb0ea32'); + writeLn('', true); + }) + .action(async (id, organizationId, encodedJson, cmd) => { + await this.exitIfLocked(); + const command = new ShareCommand(this.main.cipherService); + const response = await command.run(id, organizationId, encodedJson, cmd); + this.processResponse(response); + }); + program .command('import [format] [input]') .description('Import vault data from a file.')