From a9e85f87650a749c6dabbac09ab261759046a54a Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Sat, 4 Mar 2017 20:41:45 -0500 Subject: [PATCH] org user invites and confirmation --- src/app/global/mainController.js | 18 +++++++ .../organizationPeopleController.js | 48 ++++++++++++++++++- .../organizationPeopleInviteController.js | 14 ++++++ .../views/organizationPeople.html | 46 ++++++++++++++++-- .../views/organizationPeopleInvite.html | 29 +++++++++++ .../views/organizationSubvaults.html | 2 +- src/app/services/apiService.js | 13 +++++ src/app/services/cryptoService.js | 5 ++ src/index.html | 1 + 9 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 src/app/organization/organizationPeopleInviteController.js create mode 100644 src/app/organization/views/organizationPeopleInvite.html diff --git a/src/app/global/mainController.js b/src/app/global/mainController.js index 6f5bdb8f9d..3c41090291 100644 --- a/src/app/global/mainController.js +++ b/src/app/global/mainController.js @@ -22,6 +22,24 @@ angular $(document).off('click', '.sidebar li a'); } + + $('.table-responsive').on('shown.bs.dropdown', function (e) { + var t = $(this), + m = $(e.target).find('.dropdown-menu'), + tb = t.offset().top + t.height(), + mb = m.offset().top + m.outerHeight(true), + d = 20; // Space for shadow + scrollbar. + if (t[0].scrollWidth > t.innerWidth()) { + if (mb + d > tb) { + t.css('padding-bottom', ((mb + d) - tb)); + } + } + else { + t.css('overflow', 'visible'); + } + }).on('hidden.bs.dropdown', function () { + $(this).css({ 'padding-bottom': '', 'overflow': '' }); + }); }); $scope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) { diff --git a/src/app/organization/organizationPeopleController.js b/src/app/organization/organizationPeopleController.js index 528277b723..fd51d9af9b 100644 --- a/src/app/organization/organizationPeopleController.js +++ b/src/app/organization/organizationPeopleController.js @@ -1,6 +1,52 @@ angular .module('bit.organization') - .controller('organizationPeopleController', function ($scope) { + .controller('organizationPeopleController', function ($scope, $state, $uibModal, cryptoService, apiService, toastr) { + $scope.users = []; + loadList(); + $scope.confirm = function (user) { + apiService.users.getPublicKey({ id: user.userId }, function (userKey) { + var key = cryptoService.rsaEncrypt('org key', userKey.PublicKey); + apiService.organizationUsers.confirm({ orgId: $state.params.orgId, id: user.id }, { key: key }, function () { + user.status = 2; + toastr.success(user.email + ' has been confirmed.', 'User Confirmed'); + }, function () { + toastr.error('Unable to confirm user.', 'Error'); + }); + }, function () { + toastr.error('Unable to confirm user.', 'Error'); + }); + }; + + $scope.invite = function () { + var modal = $uibModal.open({ + animation: true, + templateUrl: 'app/organization/views/organizationPeopleInvite.html', + controller: 'organizationPeopleInviteController' + }); + + modal.result.then(function () { + loadList(); + }); + }; + + function loadList() { + apiService.organizationUsers.list({ orgId: $state.params.orgId }, function (list) { + var users = []; + + for (var i = 0; i < list.Data.length; i++) { + users.push({ + id: list.Data[i].Id, + userId: list.Data[i].UserId, + name: list.Data[i].Name, + email: list.Data[i].Email, + status: list.Data[i].Status, + type: list.Data[i].Type + }); + } + + $scope.users = users; + }); + } }); diff --git a/src/app/organization/organizationPeopleInviteController.js b/src/app/organization/organizationPeopleInviteController.js new file mode 100644 index 0000000000..231cfdb586 --- /dev/null +++ b/src/app/organization/organizationPeopleInviteController.js @@ -0,0 +1,14 @@ +angular + .module('bit.organization') + + .controller('organizationPeopleInviteController', function ($scope, $state, $uibModalInstance, apiService) { + $scope.submit = function (model) { + apiService.organizationUsers.invite({ orgId: $state.params.orgId }, { email: model.email }, function () { + $uibModalInstance.close(); + }); + }; + + $scope.close = function () { + $uibModalInstance.dismiss('cancel'); + }; + }); diff --git a/src/app/organization/views/organizationPeople.html b/src/app/organization/views/organizationPeople.html index 1607e68ae9..3344e46b3c 100644 --- a/src/app/organization/views/organizationPeople.html +++ b/src/app/organization/views/organizationPeople.html @@ -7,15 +7,53 @@
-

Users

+

Organization Users

-
-
- Some data +
+
+ Loading... +
+
+ + + + + + + + + + +
+
+ + +
+
+ User Image + + {{user.email}} +
{{user.name}}
+
+ {{user.type}} + + {{user.status}} +
+
diff --git a/src/app/organization/views/organizationPeopleInvite.html b/src/app/organization/views/organizationPeopleInvite.html new file mode 100644 index 0000000000..04ea9f0397 --- /dev/null +++ b/src/app/organization/views/organizationPeopleInvite.html @@ -0,0 +1,29 @@ + +
+ + +
+ \ No newline at end of file diff --git a/src/app/organization/views/organizationSubvaults.html b/src/app/organization/views/organizationSubvaults.html index 76f87265b1..dcb5443227 100644 --- a/src/app/organization/views/organizationSubvaults.html +++ b/src/app/organization/views/organizationSubvaults.html @@ -9,7 +9,7 @@

Subvaults

-
diff --git a/src/app/services/apiService.js b/src/app/services/apiService.js index 830a723839..7f5a8b91f1 100644 --- a/src/app/services/apiService.js +++ b/src/app/services/apiService.js @@ -38,6 +38,15 @@ del: { url: _apiUri + '/organizations/:id/delete', method: 'POST', params: { id: '@id' } } }); + _service.organizationUsers = $resource(_apiUri + '/organizations/:orgId/users/:id', {}, { + get: { method: 'GET', params: { id: '@id', orgId: '@orgId' } }, + list: { method: 'GET', params: { orgId: '@orgId' } }, + invite: { url: _apiUri + '/organizations/:orgId/users/invite', method: 'POST', params: { orgId: '@orgId' } }, + accept: { url: _apiUri + '/organizations/:orgId/users/:id/accept', method: 'POST', params: { id: '@id', orgId: '@orgId' } }, + confirm: { url: _apiUri + '/organizations/:orgId/users/:id/confirm', method: 'POST', params: { id: '@id', orgId: '@orgId' } }, + del: { url: _apiUri + '/organizations/:orgId/users/:id/delete', method: 'POST', params: { id: '@id', orgId: '@orgId' } } + }); + _service.accounts = $resource(_apiUri + '/accounts', {}, { register: { url: _apiUri + '/accounts/register', method: 'POST', params: {} }, emailToken: { url: _apiUri + '/accounts/email-token', method: 'POST', params: {} }, @@ -61,6 +70,10 @@ putDomains: { url: _apiUri + '/settings/domains', method: 'POST', params: {} }, }); + _service.users = $resource(_apiUri + '/users/:id', {}, { + getPublicKey: { url: _apiUri + '/users/:id/public-key', method: 'GET', params: { id: '@id' } } + }); + _service.identity = $resource(_apiUri + '/connect', {}, { token: { url: _apiUri + '/connect/token', diff --git a/src/app/services/cryptoService.js b/src/app/services/cryptoService.js index a0105c7e3b..e7095e368b 100644 --- a/src/app/services/cryptoService.js +++ b/src/app/services/cryptoService.js @@ -190,6 +190,11 @@ angular throw 'Public key unavailable.'; } + if (typeof publicKey === 'string') { + var publicKeyBytes = forge.util.decode64(publicKey); + publicKey = forge.pki.publicKeyFromAsn1(forge.asn1.fromDer(publicKeyBytes)); + } + var encryptedBytes = publicKey.encrypt(plainValue, 'RSA-OAEP', { md: forge.md.sha256.create() }); diff --git a/src/index.html b/src/index.html index b25ead7892..91c2c140cb 100644 --- a/src/index.html +++ b/src/index.html @@ -123,6 +123,7 @@ +