diff --git a/dist/.publish b/dist/.publish index 466ddf04c4..7bdd5f9735 160000 --- a/dist/.publish +++ b/dist/.publish @@ -1 +1 @@ -Subproject commit 466ddf04c4878942e2901a7330d6de3b2971c204 +Subproject commit 7bdd5f9735d6f0f39f147286fb03eb33877aa4e1 diff --git a/src/app/config.js b/src/app/config.js index fc8b4b55f6..1dca068f86 100644 --- a/src/app/config.js +++ b/src/app/config.js @@ -224,6 +224,12 @@ angular controller: 'organizationGroupsController', data: { pageTitle: 'Organization Groups' } }) + .state('backend.org.events', { + url: '/organization/:orgId/events', + templateUrl: 'app/organization/views/organizationEvents.html', + controller: 'organizationEventsController', + data: { pageTitle: 'Organization Events' } + }) // Frontend .state('frontend', { diff --git a/src/app/constants.js b/src/app/constants.js index 6b97e0f8bc..2e86011aac 100644 --- a/src/app/constants.js +++ b/src/app/constants.js @@ -39,6 +39,39 @@ angular.module('bit') hidden: 1, boolean: 2 }, + eventType: { + User_LoggedIn: 1000, + User_ChangedPassword: 1001, + User_Enabled2fa: 1002, + User_Disabled2fa: 1003, + User_Recovered2fa: 1004, + User_FailedLogIn: 1005, + User_FailedLogIn2fa: 1006, + + Cipher_Created: 1100, + Cipher_Updated: 1101, + Cipher_Deleted: 1102, + Cipher_AttachmentCreated: 1103, + Cipher_AttachmentDeleted: 1104, + Cipher_Shared: 1105, + Cipher_UpdatedCollections: 1106, + + Collection_Created: 1300, + Collection_Updated: 1301, + Collection_Deleted: 1302, + + Group_Created: 1400, + Group_Updated: 1401, + Group_Deleted: 1402, + + OrganizationUser_Invited: 1500, + OrganizationUser_Confirmed: 1501, + OrganizationUser_Updated: 1502, + OrganizationUser_Removed: 1503, + OrganizationUser_UpdatedGroups: 1504, + + Organization_Updated: 1600 + }, twoFactorProviderInfo: [ { type: 0, diff --git a/src/app/organization/organizationEventsController.js b/src/app/organization/organizationEventsController.js new file mode 100644 index 0000000000..f569ce57d8 --- /dev/null +++ b/src/app/organization/organizationEventsController.js @@ -0,0 +1,148 @@ +angular + .module('bit.organization') + + .controller('organizationEventsController', function ($scope, $state, apiService, $uibModal, $filter, + toastr, $analytics, constants) { + $scope.events = []; + $scope.orgUsers = []; + $scope.loading = true; + $scope.$on('$viewContentLoaded', function () { + load(); + }); + + var i = 0, + orgUsersUserIdDict = {}, + orgUsersIdDict = {}; + + function load() { + apiService.organizationUsers.list({ orgId: $state.params.orgId }).$promise.then(function (list) { + var users = []; + for (i = 0; i < list.Data.length; i++) { + var user = { + id: list.Data[i].Id, + userId: list.Data[i].UserId, + name: list.Data[i].Name, + email: list.Data[i].Email + }; + + users.push(user); + + var displayName = user.name || user.email; + orgUsersUserIdDict[user.userId] = displayName; + orgUsersIdDict[user.id] = displayName; + } + + $scope.orgUsers = users; + return apiService.events.listOrganization({ orgId: $state.params.orgId }).$promise; + }).then(function (list) { + var events = []; + for (i = 0; i < list.Data.length; i++) { + var userId = list.Data[i].ActingUserId || list.Data[i].UserId; + events.push({ + message: eventMessage(list.Data[i]), + appIcon: 'fa-globe', + appName: 'Web', + userId: userId, + userName: userId ? (orgUsersUserIdDict[userId] || '-') : '-', + date: list.Data[i].Date + }); + } + $scope.events = events; + $scope.loading = false; + }); + } + + function eventMessage(ev) { + var msg = ''; + switch (ev.Type) { + // User + case constants.eventType.User_LoggedIn: + msg = 'Logged in.'; + break; + case constants.eventType.User_ChangedPassword: + msg = 'Changed account password.'; + break; + case constants.eventType.User_Enabled2fa: + msg = 'Enabled two-step login.'; + break; + case constants.eventType.User_Disabled2fa: + msg = 'Disabled two-step login.'; + break; + case constants.eventType.User_Recovered2fa: + msg = 'Recovered account from two-step login.'; + break; + case constants.eventType.User_FailedLogIn: + msg = 'Login attempt failed with incorrect password.'; + break; + case constants.eventType.User_FailedLogIn2fa: + msg = 'Login attempt failed with incorrect two-step login.'; + break; + // Cipher + case constants.eventType.Cipher_Created: + msg = 'Created item ' + ev.CipherId + '.'; + break; + case constants.eventType.Cipher_Updated: + msg = 'Edited item ' + ev.CipherId + '.'; + break; + case constants.eventType.Cipher_Deleted: + msg = 'Deleted item ' + ev.CipherId + '.'; + break; + case constants.eventType.Cipher_AttachmentCreated: + msg = 'Created attachment for item ' + ev.CipherId + '.'; + break; + case constants.eventType.Cipher_AttachmentDeleted: + msg = 'Deleted attachment for item ' + ev.CipherId + '.'; + break; + case constants.eventType.Cipher_Shared: + msg = 'Shared item ' + ev.CipherId + '.'; + break; + case constants.eventType.Cipher_UpdatedCollections: + msg = 'Update collections for item ' + ev.CipherId + '.'; + break; + // Collection + case constants.eventType.Collection_Created: + msg = 'Created collection ' + ev.CollectionId + '.'; + break; + case constants.eventType.Collection_Updated: + msg = 'Edited collection ' + ev.CollectionId + '.'; + break; + case constants.eventType.Collection_Deleted: + msg = 'Deleted collection ' + ev.CollectionId + '.'; + break; + // Group + case constants.eventType.Group_Created: + msg = 'Created group ' + ev.GroupId + '.'; + break; + case constants.eventType.Group_Updated: + msg = 'Edited group ' + ev.GroupId + '.'; + break; + case constants.eventType.Group_Deleted: + msg = 'Deleted group ' + ev.GroupId + '.'; + break; + // Org user + case constants.eventType.OrganizationUser_Invited: + msg = 'Invited user ' + ev.OrganizationUserId + '.'; + break; + case constants.eventType.OrganizationUser_Confirmed: + msg = 'Confirmed user ' + ev.OrganizationUserId + '.'; + break; + case constants.eventType.OrganizationUser_Updated: + msg = 'Edited user ' + ev.OrganizationUserId + '.'; + break; + case constants.eventType.OrganizationUser_Removed: + msg = 'Removed user ' + ev.OrganizationUserId + '.'; + break; + case constants.eventType.OrganizationUser_UpdatedGroups: + msg = 'Edited groups for user ' + ev.OrganizationUserId + '.'; + break; + // Org + case constants.eventType.Organization_Updated: + msg = 'Edited organization settings.'; + break; + default: + break; + } + + return msg === '' ? null : msg; + } + }); diff --git a/src/app/organization/views/organizationEvents.html b/src/app/organization/views/organizationEvents.html new file mode 100644 index 0000000000..028ee0e3ab --- /dev/null +++ b/src/app/organization/views/organizationEvents.html @@ -0,0 +1,52 @@ +
+

+ Events + audit your organization +

+
+
+
+
+   + +
+
+
+ Loading... +
+
+

There are no events yet for your organization.

+
+
+ + + + + + + + + + + + + + + + + +
TimestampUserAppEvent
+ {{event.date | date:'medium'}} + + {{event.userName}} + + + + {{event.message}} +
+
+
+
+
diff --git a/src/app/services/apiService.js b/src/app/services/apiService.js index 86944cd406..00b6d7eb9d 100644 --- a/src/app/services/apiService.js +++ b/src/app/services/apiService.js @@ -179,6 +179,11 @@ getPublicKey: { url: _apiUri + '/users/:id/public-key', method: 'GET', params: { id: '@id' } } }); + _service.events = $resource(_apiUri + '/events', {}, { + list: { method: 'GET', params: {} }, + listOrganization: { url: _apiUri + '/organizations/:orgId/events', method: 'GET', params: { id: '@orgId' } } + }); + _service.identity = $resource(_identityUri + '/connect', {}, { token: { url: _identityUri + '/connect/token', diff --git a/src/app/views/organizationLayout.html b/src/app/views/organizationLayout.html index da8e00d13a..9ddbda9289 100644 --- a/src/app/views/organizationLayout.html +++ b/src/app/views/organizationLayout.html @@ -95,6 +95,11 @@ +
  • + + Events + +
  • Billing & Licensing diff --git a/src/index.html b/src/index.html index 13ccc0f451..0975583daf 100644 --- a/src/index.html +++ b/src/index.html @@ -257,6 +257,7 @@ +