diff --git a/src/app/organization/organizationVaultAttachmentsController.js b/src/app/organization/organizationVaultAttachmentsController.js index 99b1714e15..d6514496ec 100644 --- a/src/app/organization/organizationVaultAttachmentsController.js +++ b/src/app/organization/organizationVaultAttachmentsController.js @@ -22,77 +22,39 @@ return; } - var file = files[0]; - if (file.size > 104857600) { // 100 MB - validationService.addError(form, 'file', 'Maximum file size is 100 MB.', true); - return; - } - - var reader = new FileReader(); - reader.readAsArrayBuffer(file); - reader.onload = function (evt) { - $timeout(function () { - form.$loading = true; - }); - - var key = cryptoService.getOrgKey($scope.login.organizationId); - var encFilename = cryptoService.encrypt(file.name, key); - $scope.savePromise = cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) { - var fd = new FormData(); - var blob = new Blob([encData], { type: 'application/octet-stream' }); - fd.append('data', blob, encFilename); - return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise; - }).then(function (response) { - $analytics.eventTrack('Added Organization Attachment'); - toastr.success('The attachment has been added.'); - closing = true; - $uibModalInstance.close(true); - }); - }; - reader.onerror = function (evt) { - validationService.addError(form, 'file', 'Error reading file.', true); - }; + var key = cryptoService.getOrgKey($scope.login.organizationId); + $scope.savePromise = cipherService.encryptAttachmentFile(key, files[0]).then(function (encValue) { + var fd = new FormData(); + var blob = new Blob([encValue.data], { type: 'application/octet-stream' }); + fd.append('data', blob, encValue.fileName); + return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise; + }).then(function (response) { + $analytics.eventTrack('Added Attachment'); + toastr.success('The attachment has been added.'); + closing = true; + $uibModalInstance.close(true); + }, function (err) { + if (err) { + validationService.addError(form, 'file', err, true); + } + else { + validationService.addError(form, 'file', 'Something went wrong.', true); + } + }); } $scope.download = function (attachment) { attachment.loading = true; - - var req = new XMLHttpRequest(); - req.open('GET', attachment.url, true); - req.responseType = 'arraybuffer'; - req.onload = function (evt) { - if (!req.response) { - $timeout(function () { - attachment.loading = false; - }); - - // error - return; - } - - var key = cryptoService.getOrgKey($scope.login.organizationId); - cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) { - var blob = new Blob([decBuf]); - - // IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx - if (window.navigator.msSaveOrOpenBlob) { - window.navigator.msSaveBlob(blob, attachment.fileName); - } - else { - var a = window.document.createElement('a'); - a.href = window.URL.createObjectURL(blob); - a.download = attachment.fileName; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - } - - $timeout(function () { - attachment.loading = false; - }); + var key = cryptoService.getOrgKey($scope.login.organizationId); + cipherService.downloadAndDecryptAttachment(key, attachment, true).then(function (res) { + $timeout(function () { + attachment.loading = false; }); - }; - req.send(null); + }, function () { + $timeout(function () { + attachment.loading = false; + }); + }); }; $scope.remove = function (attachment) { diff --git a/src/app/services/apiService.js b/src/app/services/apiService.js index 6c2c58da20..1f8cde2ca8 100644 --- a/src/app/services/apiService.js +++ b/src/app/services/apiService.js @@ -47,6 +47,12 @@ headers: { 'Content-Type': undefined }, params: { id: '@id' } }, + postShareAttachment: { + url: _apiUri + '/ciphers/:id/attachment/:attachmentId/share?organizationId=:orgId', + method: 'POST', + headers: { 'Content-Type': undefined }, + params: { id: '@id', attachmentId: '@attachmentId', orgId: '@orgId' } + }, delAttachment: { url: _apiUri + '/ciphers/:id/attachment/:attachmentId/delete', method: 'POST', params: { id: '@id', attachmentId: '@attachmentId' } } }); diff --git a/src/app/services/cipherService.js b/src/app/services/cipherService.js index cdef2fa15f..b3dce7c56a 100644 --- a/src/app/services/cipherService.js +++ b/src/app/services/cipherService.js @@ -1,7 +1,7 @@ angular .module('bit.services') - .factory('cipherService', function (cryptoService, apiService, $q) { + .factory('cipherService', function (cryptoService, apiService, $q, $window) { var _service = {}; _service.decryptLogins = function (encryptedLogins) { @@ -89,6 +89,43 @@ angular }; }; + _service.downloadAndDecryptAttachment = function (key, decryptedAttachment, openDownload) { + var deferred = $q.defer(); + var req = new XMLHttpRequest(); + req.open('GET', decryptedAttachment.url, true); + req.responseType = 'arraybuffer'; + req.onload = function (evt) { + if (!req.response) { + deferred.reject('No response'); + // error + return; + } + + cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) { + if (openDownload) { + var blob = new Blob([decBuf]); + + // IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx + if ($window.navigator.msSaveOrOpenBlob) { + $window.navigator.msSaveBlob(blob, decryptedAttachment.fileName); + } + else { + var a = $window.document.createElement('a'); + a.href = $window.URL.createObjectURL(blob); + a.download = decryptedAttachment.fileName; + $window.document.body.appendChild(a); + a.click(); + $window.document.body.removeChild(a); + } + } + + deferred.resolve(new Uint8Array(decBuf)); + }); + }; + req.send(null); + return deferred.promise; + }; + _service.decryptFolders = function (encryptedFolders) { if (!encryptedFolders) throw "encryptedFolders is undefined or null"; @@ -169,14 +206,14 @@ angular return encryptedLogins; }; - _service.encryptLogin = function (unencryptedLogin, key) { + _service.encryptLogin = function (unencryptedLogin, key, attachments) { if (!unencryptedLogin) throw "unencryptedLogin is undefined or null"; if (unencryptedLogin.organizationId) { key = key || cryptoService.getOrgKey(unencryptedLogin.organizationId); } - return { + var login = { id: unencryptedLogin.id, 'type': 1, organizationId: unencryptedLogin.organizationId || null, @@ -189,6 +226,42 @@ angular notes: !unencryptedLogin.notes || unencryptedLogin.notes === '' ? null : cryptoService.encrypt(unencryptedLogin.notes, key), totp: !unencryptedLogin.totp || unencryptedLogin.totp === '' ? null : cryptoService.encrypt(unencryptedLogin.totp, key) }; + + if (unencryptedLogin.attachments && attachments) { + login.attachments = {}; + for (var i = 0; i < unencryptedLogin.attachments.length; i++) { + login.attachments[unencryptedLogin.attachments[i].id] = + cryptoService.encrypt(unencryptedLogin.attachments[i].fileName, key); + } + } + + return login; + }; + + _service.encryptAttachmentFile = function (key, unencryptedFile) { + var deferred = $q.defer(); + + if (unencryptedFile.size > 104857600) { // 100 MB + deferred.reject('Maximum file size is 100 MB.'); + return; + } + + var reader = new FileReader(); + reader.readAsArrayBuffer(unencryptedFile); + reader.onload = function (evt) { + cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) { + deferred.resolve({ + fileName: cryptoService.encrypt(unencryptedFile.name, key), + data: new Uint8Array(encData), + size: unencryptedFile.size + }); + }); + }; + reader.onerror = function (evt) { + deferred.reject('Error reading file.'); + }; + + return deferred.promise; }; _service.encryptFolders = function (unencryptedFolders, key) { diff --git a/src/app/vault/vaultAttachmentsController.js b/src/app/vault/vaultAttachmentsController.js index aff402d788..f7abc6caeb 100644 --- a/src/app/vault/vaultAttachmentsController.js +++ b/src/app/vault/vaultAttachmentsController.js @@ -24,78 +24,37 @@ return; } - var file = files[0]; - if (file.size > 104857600) { // 100 MB - validationService.addError(form, 'file', 'Maximum file size is 100 MB.', true); - return; - } - - var reader = new FileReader(); - reader.readAsArrayBuffer(file); - reader.onload = function (evt) { - $timeout(function () { - form.$loading = true; - }); - - var key = getKeyForLogin(); - - var encFilename = cryptoService.encrypt(file.name, key); - $scope.savePromise = cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) { - var fd = new FormData(); - var blob = new Blob([encData], { type: 'application/octet-stream' }); - fd.append('data', blob, encFilename); - return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise; - }).then(function (response) { - $analytics.eventTrack('Added Attachment'); - toastr.success('The attachment has been added.'); - closing = true; - $uibModalInstance.close(true); - }); - }; - reader.onerror = function (evt) { - validationService.addError(form, 'file', 'Error reading file.', true); - }; + $scope.savePromise = cipherService.encryptAttachmentFile(getKeyForLogin(), files[0]).then(function (encValue) { + var fd = new FormData(); + var blob = new Blob([encValue.data], { type: 'application/octet-stream' }); + fd.append('data', blob, encValue.fileName); + return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise; + }).then(function (response) { + $analytics.eventTrack('Added Attachment'); + toastr.success('The attachment has been added.'); + closing = true; + $uibModalInstance.close(true); + }, function (err) { + if (err) { + validationService.addError(form, 'file', err, true); + } + else { + validationService.addError(form, 'file', 'Something went wrong.', true); + } + }); } $scope.download = function (attachment) { attachment.loading = true; - var key = getKeyForLogin(); - - var req = new XMLHttpRequest(); - req.open('GET', attachment.url, true); - req.responseType = 'arraybuffer'; - req.onload = function (evt) { - if (!req.response) { - $timeout(function () { - attachment.loading = false; - }); - - // error - return; - } - - cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) { - var blob = new Blob([decBuf]); - - // IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx - if (window.navigator.msSaveOrOpenBlob) { - window.navigator.msSaveBlob(blob, attachment.fileName); - } - else { - var a = window.document.createElement('a'); - a.href = window.URL.createObjectURL(blob); - a.download = attachment.fileName; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - } - - $timeout(function () { - attachment.loading = false; - }); + cipherService.downloadAndDecryptAttachment(getKeyForLogin(), attachment, true).then(function (res) { + $timeout(function () { + attachment.loading = false; }); - }; - req.send(null); + }, function () { + $timeout(function () { + attachment.loading = false; + }); + }); }; function getKeyForLogin() { diff --git a/src/app/vault/vaultShareLoginController.js b/src/app/vault/vaultShareLoginController.js index ffe7a28be9..03bc4fc862 100644 --- a/src/app/vault/vaultShareLoginController.js +++ b/src/app/vault/vaultShareLoginController.js @@ -2,7 +2,7 @@ .module('bit.vault') .controller('vaultShareLoginController', function ($scope, apiService, $uibModalInstance, authService, cipherService, - loginId, $analytics, $state) { + loginId, $analytics, $state, cryptoService, $q) { $analytics.eventTrack('vaultShareLoginController', { category: 'Modal' }); $scope.model = {}; $scope.login = {}; @@ -111,23 +111,50 @@ $scope.submitPromise = null; $scope.submit = function (model) { - $scope.login.organizationId = model.organizationId; + var orgKey = cryptoService.getOrgKey(model.organizationId); - var request = { - collectionIds: [], - cipher: cipherService.encryptLogin($scope.login) - }; + var attachmentSharePromises = []; + if ($scope.login.attachments) { + for (var i = 0; i < $scope.login.attachments.length; i++) { + var attachment = $scope.login.attachments[i]; + var promise = cipherService.downloadAndDecryptAttachment(null, attachment, false) + .then(function (decData) { + return cryptoService.encryptToBytes(decData.buffer, orgKey); + }).then(function (encData) { + var fd = new FormData(); + var blob = new Blob([encData], { type: 'application/octet-stream' }); + var encFilename = cryptoService.encrypt(attachment.fileName, orgKey); + fd.append('data', blob, encFilename); - for (var id in $scope.selectedCollections) { - if ($scope.selectedCollections.hasOwnProperty(id)) { - request.collectionIds.push(id); + return apiService.ciphers.postShareAttachment({ + id: loginId, + attachmentId: attachment.id, + orgId: model.organizationId + }, fd).$promise; + }); + attachmentSharePromises.push(promise); } } - $scope.submitPromise = apiService.ciphers.putShare({ id: loginId }, request, function (response) { + $scope.submitPromise = $q.all(attachmentSharePromises).then(function () { + $scope.login.organizationId = model.organizationId; + + var request = { + collectionIds: [], + cipher: cipherService.encryptLogin($scope.login, null, true) + }; + + for (var id in $scope.selectedCollections) { + if ($scope.selectedCollections.hasOwnProperty(id)) { + request.collectionIds.push(id); + } + } + + return apiService.ciphers.putShare({ id: loginId }, request).$promise; + }).then(function (response) { $analytics.eventTrack('Shared Login'); $uibModalInstance.close(model.organizationId); - }).$promise; + }); }; $scope.close = function () {