From a421f6e64a1f47c34806419012b3983bd0505bc6 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 17 May 2018 15:35:02 -0400 Subject: [PATCH] raw attachment saves with node form data --- package-lock.json | 21 +++++----- package.json | 2 + src/abstractions/cipher.service.ts | 3 +- src/services/cipher.service.ts | 61 +++++++++++++++++++----------- src/services/nodeApi.service.ts | 2 + 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf1699064e..3894c258a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,6 +84,15 @@ "tslib": "1.9.0" } }, + "@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "dev": true, + "requires": { + "@types/node": "8.0.19" + } + }, "@types/jasmine": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.6.tgz", @@ -383,8 +392,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.0", @@ -1249,7 +1257,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, "requires": { "delayed-stream": "1.0.0" } @@ -1759,8 +1766,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "depd": { "version": "1.1.2", @@ -2575,7 +2581,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, "requires": { "asynckit": "0.4.0", "combined-stream": "1.0.6", @@ -5642,14 +5647,12 @@ "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" }, "mime-types": { "version": "2.1.18", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, "requires": { "mime-db": "1.33.0" } diff --git a/package.json b/package.json index 200dfff01d..7ff778325a 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "test:node:watch": "concurrently -k -n TSC,Node -c yellow,cyan \"npm run build:watch\" \"nodemon -w ./dist --delay 500ms --exec jasmine\"" }, "devDependencies": { + "@types/form-data": "^2.2.1", "@types/jasmine": "^2.8.2", "@types/keytar": "^4.0.1", "@types/lunr": "2.1.5", @@ -72,6 +73,7 @@ "electron-log": "2.2.14", "electron-store": "1.3.0", "electron-updater": "2.21.4", + "form-data": "2.3.2", "keytar": "4.1.0", "lunr": "2.1.6", "node-fetch": "2.1.2", diff --git a/src/abstractions/cipher.service.ts b/src/abstractions/cipher.service.ts index 2e344dffd8..73ee81047c 100644 --- a/src/abstractions/cipher.service.ts +++ b/src/abstractions/cipher.service.ts @@ -25,7 +25,8 @@ export abstract class CipherService { updateLastUsedDate: (id: string) => Promise; saveNeverDomain: (domain: string) => Promise; saveWithServer: (cipher: Cipher) => Promise; - saveAttachmentWithServer: (cipher: Cipher, unencryptedFile: any) => Promise; + saveAttachmentWithServer: (cipher: Cipher, unencryptedFile: any) => Promise; + saveAttachmentRawWithServer: (cipher: Cipher, filename: string, data: ArrayBuffer) => Promise; upsert: (cipher: CipherData | CipherData[]) => Promise; replace: (ciphers: { [id: string]: CipherData; }) => Promise; clear: (userId: string) => Promise; diff --git a/src/services/cipher.service.ts b/src/services/cipher.service.ts index e2be81f25f..b2e7fec063 100644 --- a/src/services/cipher.service.ts +++ b/src/services/cipher.service.ts @@ -322,43 +322,58 @@ export class CipherService implements CipherServiceAbstraction { await this.upsert(data); } - saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any): Promise { - const self = this; - + saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsArrayBuffer(unencryptedFile); - reader.onload = async (evt: any) => { - const key = await self.cryptoService.getOrgKey(cipher.organizationId); - const encFileName = await self.cryptoService.encrypt(unencryptedFile.name, key); - const encData = await self.cryptoService.encryptToBytes(evt.target.result, key); - - const fd = new FormData(); - const blob = new Blob([encData], { type: 'application/octet-stream' }); - fd.append('data', blob, encFileName.encryptedString); - - let response: CipherResponse; try { - response = await self.apiService.postCipherAttachment(cipher.id, fd); + const cData = await this.saveAttachmentRawWithServer(cipher, + unencryptedFile.name, evt.target.result); + resolve(cData); } catch (e) { - reject((e as ErrorResponse).getSingleMessage()); - return; + reject(e); } - - const userId = await self.userService.getUserId(); - const data = new CipherData(response, userId, cipher.collectionIds); - this.upsert(data); - resolve(new Cipher(data)); - }; - reader.onerror = (evt) => { reject('Error reading file.'); }; }); } + async saveAttachmentRawWithServer(cipher: Cipher, filename: string, data: ArrayBuffer): Promise { + const key = await this.cryptoService.getOrgKey(cipher.organizationId); + const encFileName = await this.cryptoService.encrypt(filename, key); + const encData = await this.cryptoService.encryptToBytes(data, key); + + const fd = new FormData(); + try { + const blob = new Blob([encData], { type: 'application/octet-stream' }); + fd.append('data', blob, encFileName.encryptedString); + } catch (e) { + if (Utils.isNode) { + fd.append('data', new Buffer(encData) as any, { + filename: encFileName.encryptedString, + contentType: 'application/octet-stream', + } as any); + } else { + throw e; + } + } + + let response: CipherResponse; + try { + response = await this.apiService.postCipherAttachment(cipher.id, fd); + } catch (e) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } + + const userId = await this.userService.getUserId(); + const cData = new CipherData(response, userId, cipher.collectionIds); + this.upsert(cData); + return new Cipher(cData); + } + async upsert(cipher: CipherData | CipherData[]): Promise { const userId = await this.userService.getUserId(); let ciphers = await this.storageService.get<{ [id: string]: CipherData; }>( diff --git a/src/services/nodeApi.service.ts b/src/services/nodeApi.service.ts index e5900f2409..3815d46ec4 100644 --- a/src/services/nodeApi.service.ts +++ b/src/services/nodeApi.service.ts @@ -1,3 +1,4 @@ +import * as FormData from 'form-data'; import * as fe from 'node-fetch'; import { ApiService } from './api.service'; @@ -9,6 +10,7 @@ import { TokenService } from '../abstractions/token.service'; (global as any).Request = fe.Request; (global as any).Response = fe.Response; (global as any).Headers = fe.Headers; +(global as any).FormData = FormData; export class NodeApiService extends ApiService { constructor(tokenService: TokenService, platformUtilsService: PlatformUtilsService,