From abed4df97338753691a609d4756f2c58136c07c1 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 7 Jul 2017 15:43:24 -0400 Subject: [PATCH] attachments for org logins --- src/app/global/paidOrgRequiredController.js | 11 +- src/app/global/sideNavController.js | 6 +- .../organizationVaultAddLoginController.js | 2 +- .../organizationVaultAttachmentsController.js | 127 ++++++++++++++++++ .../organizationVaultController.js | 33 ++++- .../organizationVaultEditLoginController.js | 2 +- .../organization/views/organizationVault.html | 5 + src/app/vault/vaultAttachmentsController.js | 2 +- src/app/vault/vaultController.js | 23 +++- src/app/vault/views/vault.html | 4 +- src/app/vault/views/vaultAttachments.html | 28 ++-- src/app/views/paidOrgRequired.html | 2 +- src/index.html | 1 + 13 files changed, 218 insertions(+), 28 deletions(-) create mode 100644 src/app/organization/organizationVaultAttachmentsController.js diff --git a/src/app/global/paidOrgRequiredController.js b/src/app/global/paidOrgRequiredController.js index 3f2ed1794e..9d2abb74d8 100644 --- a/src/app/global/paidOrgRequiredController.js +++ b/src/app/global/paidOrgRequiredController.js @@ -1,10 +1,19 @@ angular .module('bit.global') - .controller('paidOrgRequiredController', function ($scope, $state, $uibModalInstance, $analytics, $uibModalStack, orgId) { + .controller('paidOrgRequiredController', function ($scope, $state, $uibModalInstance, $analytics, $uibModalStack, orgId, + constants, authService) { $analytics.eventTrack('paidOrgRequiredController', { category: 'Modal' }); + authService.getUserProfile().then(function (profile) { + $scope.admin = profile.organizations[orgId].type !== constants.orgUserType.user + }); + $scope.go = function () { + if (!$scope.admin) { + return; + } + $analytics.eventTrack('Get Paid Org'); $state.go('backend.org.billing', { orgId: orgId }).then(function () { $uibModalStack.dismissAll(); diff --git a/src/app/global/sideNavController.js b/src/app/global/sideNavController.js index f591ca2b80..94c9895a48 100644 --- a/src/app/global/sideNavController.js +++ b/src/app/global/sideNavController.js @@ -1,7 +1,7 @@ angular .module('bit.global') - .controller('sideNavController', function ($scope, $state, authService, toastr, $analytics) { + .controller('sideNavController', function ($scope, $state, authService, toastr, $analytics, constants) { $scope.$state = $state; $scope.params = $state.params; $scope.orgs = []; @@ -31,7 +31,7 @@ angular }); $scope.viewOrganization = function (org) { - if (org.type === 2) { // 2 = User + if (org.type === constants.orgUserType.user) { toastr.error('You cannot manage this organization.'); return; } @@ -49,6 +49,6 @@ angular }; $scope.isOrgOwner = function (org) { - return org && org.type === 0; + return org && org.type === constants.orgUserType.owner; }; }); diff --git a/src/app/organization/organizationVaultAddLoginController.js b/src/app/organization/organizationVaultAddLoginController.js index 86519b7eab..de4828e123 100644 --- a/src/app/organization/organizationVaultAddLoginController.js +++ b/src/app/organization/organizationVaultAddLoginController.js @@ -1,5 +1,5 @@ angular - .module('bit.vault') + .module('bit.organization') .controller('organizationVaultAddLoginController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService, passwordService, $analytics, authService, orgId, $uibModal) { diff --git a/src/app/organization/organizationVaultAttachmentsController.js b/src/app/organization/organizationVaultAttachmentsController.js new file mode 100644 index 0000000000..33ff74739e --- /dev/null +++ b/src/app/organization/organizationVaultAttachmentsController.js @@ -0,0 +1,127 @@ +angular + .module('bit.organization') + + .controller('organizationVaultAttachmentsController', function ($scope, apiService, $uibModalInstance, cryptoService, + cipherService, loginId, $analytics, validationService, toastr) { + $analytics.eventTrack('organizationVaultAttachmentsController', { category: 'Modal' }); + $scope.login = {}; + $scope.loading = true; + var closing = false; + + apiService.logins.getAdmin({ id: loginId }, function (login) { + $scope.login = cipherService.decryptLogin(login); + $scope.loading = false; + }, function () { + $scope.loading = false; + }); + + $scope.save = function (form) { + var files = document.getElementById('file').files; + if (!files || !files.length) { + validationService.addError(form, 'file', 'Select a file.', true); + 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) { + form.$loading = true; + $scope.$apply(); + + 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); + }; + } + + $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) { + attachment.loading = false; + $scope.$apply(); + + // 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); + } + + attachment.loading = false; + $scope.$apply(); + }); + }; + req.send(null); + }; + + $scope.remove = function (attachment) { + if (!confirm('Are you sure you want to delete this attachment (' + attachment.fileName + ')?')) { + return; + } + + attachment.loading = true; + apiService.ciphers.delAttachment({ id: loginId, attachmentId: attachment.id }).$promise.then(function () { + attachment.loading = false; + $analytics.eventTrack('Deleted Organization Attachment'); + var index = $scope.login.attachments.indexOf(attachment); + if (index > -1) { + $scope.login.attachments.splice(index, 1); + } + }, function () { + toastr.error('Cannot delete attachment.'); + attachment.loading = false; + }); + }; + + $scope.close = function () { + $uibModalInstance.dismiss('cancel'); + }; + + $scope.$on('modal.closing', function (e, reason, closed) { + if (closing) { + return; + } + + e.preventDefault(); + closing = true; + $uibModalInstance.close(!!$scope.login.attachments && $scope.login.attachments.length > 0); + }); + }); diff --git a/src/app/organization/organizationVaultController.js b/src/app/organization/organizationVaultController.js index f2d233e579..cf2d71f696 100644 --- a/src/app/organization/organizationVaultController.js +++ b/src/app/organization/organizationVaultController.js @@ -2,7 +2,7 @@ .module('bit.organization') .controller('organizationVaultController', function ($scope, apiService, cipherService, $analytics, $q, $state, - $localStorage, $uibModal, $filter) { + $localStorage, $uibModal, $filter, authService) { $scope.logins = []; $scope.collections = []; $scope.loading = true; @@ -139,6 +139,37 @@ }); }; + $scope.attachments = function (login) { + authService.getUserProfile().then(function (profile) { + return !!profile.organizations[login.organizationId].maxStorageGb; + }).then(function (useStorage) { + if (!useStorage) { + $uibModal.open({ + animation: true, + templateUrl: 'app/views/paidOrgRequired.html', + controller: 'paidOrgRequiredController', + resolve: { + orgId: function () { return login.organizationId; } + } + }); + return; + } + + var attachmentModel = $uibModal.open({ + animation: true, + templateUrl: 'app/vault/views/vaultAttachments.html', + controller: 'organizationVaultAttachmentsController', + resolve: { + loginId: function () { return login.id; } + } + }); + + attachmentModel.result.then(function (hasAttachments) { + login.hasAttachments = hasAttachments; + }); + }); + }; + $scope.removeLogin = function (login, collection) { if (!confirm('Are you sure you want to remove this login (' + login.name + ') from the ' + 'collection (' + collection.name + ') ?')) { diff --git a/src/app/organization/organizationVaultEditLoginController.js b/src/app/organization/organizationVaultEditLoginController.js index 064fd83be1..efdd017bcf 100644 --- a/src/app/organization/organizationVaultEditLoginController.js +++ b/src/app/organization/organizationVaultEditLoginController.js @@ -1,5 +1,5 @@ angular - .module('bit.vault') + .module('bit.organization') .controller('organizationVaultEditLoginController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService, passwordService, loginId, $analytics, orgId, $uibModal) { diff --git a/src/app/organization/views/organizationVault.html b/src/app/organization/views/organizationVault.html index e8125352a4..cadda3e682 100644 --- a/src/app/organization/views/organizationVault.html +++ b/src/app/organization/views/organizationVault.html @@ -46,6 +46,11 @@ Edit +
  • + + Attachments + +
  • Collections diff --git a/src/app/vault/vaultAttachmentsController.js b/src/app/vault/vaultAttachmentsController.js index 5ac53ba77a..4b9446cae5 100644 --- a/src/app/vault/vaultAttachmentsController.js +++ b/src/app/vault/vaultAttachmentsController.js @@ -5,7 +5,7 @@ loginId, $analytics, validationService, toastr) { $analytics.eventTrack('vaultAttachmentsController', { category: 'Modal' }); $scope.login = {}; - $scope.readOnly = false; + $scope.readOnly = true; $scope.loading = true; var closing = false; diff --git a/src/app/vault/vaultController.js b/src/app/vault/vaultController.js index dad36be735..f291cf9b92 100644 --- a/src/app/vault/vaultController.js +++ b/src/app/vault/vaultController.js @@ -201,9 +201,24 @@ $scope.attachments = function (login) { authService.getUserProfile().then(function (profile) { - return profile.premium; - }).then(function (isPremium) { - if (!isPremium) { + return { + isPremium: profile.premium, + orgUseStorage: login.organizationId && !!profile.organizations[login.organizationId].maxStorageGb + }; + }).then(function (perms) { + if (login.organizationId && !perms.orgUseStorage) { + $uibModal.open({ + animation: true, + templateUrl: 'app/views/paidOrgRequired.html', + controller: 'paidOrgRequiredController', + resolve: { + orgId: function () { return login.organizationId; } + } + }); + return; + } + + if (!login.organizationId && !perms.isPremium) { $uibModal.open({ animation: true, templateUrl: 'app/views/premiumRequired.html', @@ -212,7 +227,7 @@ return; } - if (!cryptoService.getEncKey()) { + if (!login.organizationId && !cryptoService.getEncKey()) { toastr.error('You cannot use this feature until you update your encryption key.', 'Feature Unavailable'); return; } diff --git a/src/app/vault/views/vault.html b/src/app/vault/views/vault.html index b11042a237..98b31ffd47 100644 --- a/src/app/vault/views/vault.html +++ b/src/app/vault/views/vault.html @@ -86,7 +86,7 @@ Edit
  • -
  • +
  • Attachments @@ -196,7 +196,7 @@ Edit
  • -
  • +
  • Attachments diff --git a/src/app/vault/views/vaultAttachments.html b/src/app/vault/views/vaultAttachments.html index 7a2a638dbe..b603eef314 100644 --- a/src/app/vault/views/vaultAttachments.html +++ b/src/app/vault/views/vaultAttachments.html @@ -45,22 +45,24 @@ -
    -

    Add New Attachment

    -
    -

    Errors have occurred

    -
      -
    • {{e}}
    • -
    -
    -
    - - -

    Maximum size per file is 100 MB.

    +
    +
    +

    Add New Attachment

    +
    +

    Errors have occurred

    +
      +
    • {{e}}
    • +
    +
    +
    + + +

    Maximum size per file is 100 MB.

    +