From 0574c538e7f81b85d66c9aca5e6458a9b6bcc307 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 12 Jul 2017 09:57:08 -0400 Subject: [PATCH] upload new attachments --- src/_locales/en/messages.json | 12 +++ src/models/dataModels.js | 2 +- src/popup/app/config.js | 2 +- .../app/vault/vaultAttachmentsController.js | 72 +++++++++------ .../app/vault/vaultEditLoginController.js | 1 - .../app/vault/views/vaultAttachments.html | 71 ++++++++------- src/services/apiService.js | 41 +++++++++ src/services/loginService.js | 89 ++++++++++++++++++- 8 files changed, 225 insertions(+), 65 deletions(-) diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 81885cbd28..6ff59e8a59 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -739,8 +739,20 @@ "message": "No attachments.", "description": "No attachments." }, + "attachmentSaved": { + "message": "The attachment has been saved.", + "description": "The attachment has been saved." + }, "file": { "message": "File", "description": "File" + }, + "selectFile": { + "message": "Select a file.", + "description": "Select a file." + }, + "fileTooLarge": { + "message": "Maximum file size is 100 MB.", + "description": "Maximum file size is 100 MB." } } diff --git a/src/models/dataModels.js b/src/models/dataModels.js index cf71408b41..c271cca1e3 100644 --- a/src/models/dataModels.js +++ b/src/models/dataModels.js @@ -60,4 +60,4 @@ var AttachmentData = function (response) { this.fileName = response.fileName; this.size = response.size; this.sizeName = response.sizeName; -}; \ No newline at end of file +}; diff --git a/src/popup/app/config.js b/src/popup/app/config.js index 0c0ae0d4b9..906a67d84b 100644 --- a/src/popup/app/config.js +++ b/src/popup/app/config.js @@ -145,7 +145,7 @@ templateUrl: 'app/vault/views/vaultAttachments.html', controller: 'vaultAttachmentsController', data: { authorize: true }, - params: { animation: null, fromView: true, login: null, from: 'vault' } + params: { animation: null, fromView: true, from: 'vault' } }) .state('passwordGenerator', { diff --git a/src/popup/app/vault/vaultAttachmentsController.js b/src/popup/app/vault/vaultAttachmentsController.js index f8dfa651b5..ad6ba7c7f0 100644 --- a/src/popup/app/vault/vaultAttachmentsController.js +++ b/src/popup/app/vault/vaultAttachmentsController.js @@ -1,15 +1,44 @@ angular .module('bit.vault') - .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, folderService, - cryptoService, $q, toastr, SweetAlert, utilsService, $analytics, i18nService) { + .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, $q, toastr, + SweetAlert, utilsService, $analytics, i18nService) { $scope.i18n = i18nService; - $scope.login = $stateParams.login; utilsService.initListSectionItemListeners($(document), angular); + loginService.get($stateParams.id, function (login) { + $q.when(login.decrypt()).then(function (model) { + $scope.login = model; + }); + }); + $scope.submitPromise = null; $scope.submit = function () { - $scope.close(true); + var files = document.getElementById('file').files; + if (!files || !files.length) { + toastr.error(i18nService.selectFile, i18nService.errorsOccurred); + return; + } + + if (files[0].size > 104857600) { // 100 MB + toastr.error(i18nService.fileTooLarge, i18nService.errorsOccurred); + return deferred.promise; + } + + $scope.submitPromise = $q.when(loginService.saveAttachmentWithServer($scope.login, files[0])).then(function (login) { + $q.when(login.decrypt()).then(function (model) { + $scope.login = model; + }); + $analytics.eventTrack('Added Attachment'); + toastr.success(i18nService.attachmentSaved); + }, function (err) { + if (err) { + toastr.error(err); + } + else { + toastr.error(i18nService.errorsOccurred); + } + }); }; $scope.delete = function (attachment) { @@ -22,6 +51,10 @@ angular }, function (confirmed) { if (confirmed) { $q.when(loginService.deleteAttachmentWithServer($stateParams.id, attachment.id)).then(function () { + var index = $scope.login.attachments.indexOf(attachment); + if (index > -1) { + $scope.login.attachments.splice(index, 1); + } $analytics.eventTrack('Deleted Attachment'); toastr.success(i18nService.deletedAttachment); }); @@ -29,29 +62,14 @@ angular }); }; - $scope.close = function (allOut) { - if (!allOut) { - $state.go('editLogin', { - loginId: $stateParams.id, - animation: 'out-slide-down', - from: $stateParams.from, - fromView: $stateParams.fromView - }); + $scope.close = function () { + $state.go('editLogin', { + loginId: $stateParams.id, + animation: 'out-slide-down', + from: $stateParams.from, + fromView: $stateParams.fromView + }); - return; - } - - if ($stateParams.fromView) { - $state.go('viewLogin', { - loginId: $stateParams.id, - animation: 'out-slide-down', - from: $stateParams.from - }); - } - else { - $state.go('tabs.vault', { - animation: 'out-slide-down' - }); - } + return; }; }); diff --git a/src/popup/app/vault/vaultEditLoginController.js b/src/popup/app/vault/vaultEditLoginController.js index 5184b3d76a..68fd4a743c 100644 --- a/src/popup/app/vault/vaultEditLoginController.js +++ b/src/popup/app/vault/vaultEditLoginController.js @@ -71,7 +71,6 @@ angular $scope.attachments = function () { $state.go('attachments', { id: loginId, - login: $scope.login, animation: 'in-slide-up', from: from, fromView: fromView diff --git a/src/popup/app/vault/views/vaultAttachments.html b/src/popup/app/vault/views/vaultAttachments.html index 8358080eba..53ff9c29e4 100644 --- a/src/popup/app/vault/views/vaultAttachments.html +++ b/src/popup/app/vault/views/vaultAttachments.html @@ -1,40 +1,43 @@ -
- - -
{{i18n.attachments}}
-
-
-
-
-
-
- {{i18n.noAttachments}} -
-
- {{attachment.fileName}} - - - - - {{attachment.sizeName}} -
-
+
+
+ -
-
- {{i18n.newAttachment}} +
+ + +
+
{{i18n.attachments}}
+
+
+
+
+
+
+ {{i18n.noAttachments}} +
+
+ {{attachment.fileName}} + + + + + {{attachment.sizeName}} +
+
-
-
- - +
+
+ {{i18n.newAttachment}} +
+
+
+ + +
-
+ diff --git a/src/services/apiService.js b/src/services/apiService.js index 0c5591c8d4..9dca702f3b 100644 --- a/src/services/apiService.js +++ b/src/services/apiService.js @@ -403,6 +403,47 @@ function initApiService() { }); }; + ApiService.prototype.postCipherAttachment = function (id, formData, success, error) { + var self = this; + handleTokenState(self).then(function (token) { + $.ajax({ + type: 'POST', + url: self.baseUrl + '/ciphers/' + id + '/attachment?' + token, + data: formData, + processData: false, + contentType: false, + dataType: 'json', + success: function (response) { + success(new CipherResponse(response)); + }, + error: function (jqXHR, textStatus, errorThrown) { + handleError(error, jqXHR, false, self); + } + }); + }, function (jqXHR) { + handleError(error, jqXHR, true, self); + }); + }; + + ApiService.prototype.deleteCipherAttachment = function (id, attachmentId, success, error) { + var self = this; + handleTokenState(self).then(function (token) { + $.ajax({ + type: 'POST', + url: self.baseUrl + '/ciphers/' + id + '/attachment/' + attachmentId + '/delete?' + token, + dataType: 'text', + success: function (response) { + success(); + }, + error: function (jqXHR, textStatus, errorThrown) { + handleError(error, jqXHR, false, self); + } + }); + }, function (jqXHR) { + handleError(error, jqXHR, true, self); + }); + }; + // Helpers function handleError(errorCallback, jqXHR, tokenError, self) { diff --git a/src/services/loginService.js b/src/services/loginService.js index b2ae2c9397..043c3f7fe8 100644 --- a/src/services/loginService.js +++ b/src/services/loginService.js @@ -322,7 +322,7 @@ function initLoginService() { deferred.resolve(); }); }, function (response) { - handleError(response, deferred) + handleError(response, deferred); }); return deferred.promise; @@ -354,6 +354,93 @@ function initLoginService() { return deferred.promise; }; + LoginService.prototype.saveAttachmentWithServer = function (login, unencryptedFile) { + var deferred = Q.defer(); + var self = this; + + var key, encFileName; + var reader = new FileReader(); + reader.readAsArrayBuffer(unencryptedFile); + reader.onload = function (evt) { + self.cryptoService.getOrgKey(login.organizationId).then(function (theKey) { + key = theKey; + return self.cryptoService.encrypt(unencryptedFile.name, key); + }).then(function (fileName) { + encFileName = fileName; + return self.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.encryptedString); + + self.apiService.postCipherAttachment(login.id, fd, + function (response) { + self.userService.getUserId(function (userId) { + var data = new LoginData(response, userId); + self.upsert(data, function () { + deferred.resolve(new Login(data)); + }); + }); + }, + function (response) { + handleError(response, deferred); + }); + }); + }; + reader.onerror = function (evt) { + deferred.reject('Error reading file.'); + }; + + return deferred.promise; + }; + + LoginService.prototype.deleteAttachment = function (id, attachmentId, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + var self = this; + + self.userService.getUserId(function (userId) { + var loginsKey = 'sites_' + userId; + + chrome.storage.local.get(loginsKey, function (obj) { + var logins = obj[loginsKey]; + if (logins && id in logins && logins[id].attachments) { + for (var i = 0; i < logins[id].attachments.length; i++) { + if (logins[id].attachments[i].id === attachmentId) { + logins[id].attachments.splice(i, 1); + } + } + + obj[loginsKey] = logins; + chrome.storage.local.set(obj, function () { + self.decryptedLoginCache = null; + callback(); + }); + } + else { + callback() + } + }); + }); + }; + + LoginService.prototype.deleteAttachmentWithServer = function (id, attachmentId) { + var deferred = Q.defer(); + + var self = this; + self.apiService.deleteCipherAttachment(id, attachmentId, function () { + self.deleteAttachment(id, attachmentId, function () { + deferred.resolve(); + }); + }, function (response) { + handleError(response, deferred); + }); + + return deferred.promise; + }; + function handleError(error, deferred) { deferred.reject(error); }