diff --git a/.github/ISSUE_TEMPLATE/thehive4_feature_request.md b/.github/ISSUE_TEMPLATE/thehive4_feature_request.md index de898ed8ef..b22caab26e 100644 --- a/.github/ISSUE_TEMPLATE/thehive4_feature_request.md +++ b/.github/ISSUE_TEMPLATE/thehive4_feature_request.md @@ -1,7 +1,7 @@ --- name: Feature Request for TheHive4 about: Create a feature request for TheHive 4. -title: "[Bug]" +title: "[Feature Request]" labels: feature request, TheHive4 assignees: '' --- diff --git a/.gitignore b/.gitignore index d78ccf369e..2c87b915d9 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ tmp .bloop/ .metals/ metals.sbt + +.jvmopts diff --git a/CHANGELOG.md b/CHANGELOG.md index 80390ce52c..eb8360bf07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Change Log +## [4.1.3](https://github.com/TheHive-Project/TheHive/milestone/72) (2021-04-12) + +**Implemented enhancements:** + +- [Improvement] Cleanup deprecated filter usage [\#1922](https://github.com/TheHive-Project/TheHive/issues/1922) +- [Improvement] Make the property "Imported" in alerts optimised for index [\#1923](https://github.com/TheHive-Project/TheHive/issues/1923) +- [Feature Request] Display case templates in alphabetic order in "New case" menu [\#1925](https://github.com/TheHive-Project/TheHive/issues/1925) +- [Enhancement] Prevent the application to start if database initialisation fails [\#1935](https://github.com/TheHive-Project/TheHive/issues/1935) +- [Enhancement] Improve performance [\#1946](https://github.com/TheHive-Project/TheHive/issues/1946) +- [Enhancement] Remove blocking queries in some UI pages [\#1948](https://github.com/TheHive-Project/TheHive/issues/1948) +- [Enhancement] Use polluingDuration config from the UI [\#1951](https://github.com/TheHive-Project/TheHive/issues/1951) +- [Enhancement] Disable confirm buttons in import dialogs [\#1953](https://github.com/TheHive-Project/TheHive/issues/1953) +- [Enhancement] Add environment file in service [\#1954](https://github.com/TheHive-Project/TheHive/issues/1954) + +**Fixed bugs:** + +- [Bug] Add "Not assigned" to Assignee field on task page for tasks without an assigned user [\#1508](https://github.com/TheHive-Project/TheHive/issues/1508) +- [Bug] (Still) slow loading of list-tags endpoint with 4.1.2 [\#1914](https://github.com/TheHive-Project/TheHive/issues/1914) +- [Bug] Aggregation on custom fields provides incorect result [\#1921](https://github.com/TheHive-Project/TheHive/issues/1921) +- [Bug] Very slow load of Case Task list in UI in 4.1.2 [\#1927](https://github.com/TheHive-Project/TheHive/issues/1927) +- [Bug] Task "Take" Button not working [\#1931](https://github.com/TheHive-Project/TheHive/issues/1931) +- [Bug] Cluster: new nodes fail to start when the oldest node has been restarted [\#1934](https://github.com/TheHive-Project/TheHive/issues/1934) +- [Bug] Index status page is very slow [\#1936](https://github.com/TheHive-Project/TheHive/issues/1936) +- [Bug] Update of color in tags [\#1950](https://github.com/TheHive-Project/TheHive/issues/1950) + ## [4.1.2](https://github.com/TheHive-Project/TheHive/milestone/71) (2021-03-29) **Implemented enhancements:** diff --git a/ScalliGraph b/ScalliGraph index 4aa2d6084e..78de3a32ac 160000 --- a/ScalliGraph +++ b/ScalliGraph @@ -1 +1 @@ -Subproject commit 4aa2d6084eaca86a8dbc7e078a53a103f0d63c8c +Subproject commit 78de3a32ac7d9d09142eff2dd5b4c0d44147067b diff --git a/build.sbt b/build.sbt index 281257f67c..cf1d470605 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ import Dependencies._ import com.typesafe.sbt.packager.Keys.bashScriptDefines import org.thp.ghcl.Milestone -val thehiveVersion = "4.1.2-1" +val thehiveVersion = "4.1.3-1" val scala212 = "2.12.13" val scala213 = "2.13.1" val supportedScalaVersions = List(scala212, scala213) diff --git a/conf/logback.xml b/conf/logback.xml index 94caf258db..d8072baa3e 100644 --- a/conf/logback.xml +++ b/conf/logback.xml @@ -34,18 +34,13 @@ - - - - - - + + + + + + + diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala index 96e8390af9..87318660a9 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala @@ -5,6 +5,7 @@ import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.controllers.FieldsParser import org.thp.scalligraph.models._ import org.thp.scalligraph.query._ +import org.thp.scalligraph.services.config.{ApplicationConfig, ConfigItem} import org.thp.scalligraph.traversal.Traversal import org.thp.scalligraph.traversal.TraversalOps._ import org.thp.scalligraph.{BadRequestError, EntityIdOrName} @@ -18,7 +19,8 @@ import javax.inject.{Inject, Singleton} import scala.reflect.runtime.{universe => ru} @Singleton -class CortexQueryExecutor @Inject() (implicit +class CortexQueryExecutor @Inject() ( + appConfig: ApplicationConfig, override val db: Database, job: PublicJob, report: PublicAnalyzerTemplate, @@ -27,6 +29,9 @@ class CortexQueryExecutor @Inject() (implicit ) extends QueryExecutor { lazy val controllers: List[PublicData] = action :: report :: job :: analyzerTemplate :: Nil + val limitedCountThresholdConfig: ConfigItem[Long, Long] = appConfig.item[Long]("query.limitedCountThreshold", "Maximum number returned by a count") + override val limitedCountThreshold: Long = limitedCountThresholdConfig.get + override lazy val publicProperties: PublicProperties = controllers.map(_.publicProperties).reduce(_ ++ _) override lazy val queries: Seq[ParamQuery[_]] = diff --git a/frontend/app/index.html b/frontend/app/index.html index 35e7900ca0..669cc171ca 100644 --- a/frontend/app/index.html +++ b/frontend/app/index.html @@ -140,6 +140,7 @@ + @@ -284,6 +285,7 @@ + diff --git a/frontend/app/scripts/app.js b/frontend/app/scripts/app.js index 0f6ebe764c..de55dce768 100644 --- a/frontend/app/scripts/app.js +++ b/frontend/app/scripts/app.js @@ -36,22 +36,22 @@ angular.module('thehive', [ 'theHiveFilters', 'theHiveDirectives', 'theHiveComponents' - ]) - .config(function($resourceProvider) { +]) + .config(function ($resourceProvider) { 'use strict'; $resourceProvider.defaults.stripTrailingSlashes = true; }) - .config(function($compileProvider) { + .config(function ($compileProvider) { 'use strict'; $compileProvider.debugInfoEnabled(false); }) - .config(function($stateProvider, $urlRouterProvider) { + .config(function ($stateProvider, $urlRouterProvider) { 'use strict'; //$urlRouterProvider.otherwise('/index'); - $urlRouterProvider.otherwise(function($injector){ - $injector.invoke(['$state', function($state) { + $urlRouterProvider.otherwise(function ($injector) { + $injector.invoke(['$state', function ($state) { $state.go('app.index'); }]); }); @@ -62,8 +62,8 @@ angular.module('thehive', [ controller: 'AuthenticationCtrl', templateUrl: 'views/login.html', resolve: { - appConfig: function(VersionSrv) { - return VersionSrv.get(); + appConfig: function (VersionSrv) { + return VersionSrv.get(); } }, params: { @@ -89,43 +89,43 @@ angular.module('thehive', [ templateUrl: 'views/app.html', controller: 'RootCtrl', resolve: { - currentUser: function($q, $state, UserSrv, AuthenticationSrv, NotificationSrv) { + currentUser: function ($q, $state, UserSrv, AuthenticationSrv, NotificationSrv) { var deferred = $q.defer(); AuthenticationSrv.current() - .then(function(userData) { - // Prepare user cache - UserSrv.loadCache(userData.organisation); - - return deferred.resolve(userData); - }) - .catch( function(err) { - NotificationSrv.error('App', err.data, err.status); - return deferred.reject(err); - //return deferred.resolve(err.status === 520 ? err.status : null); - }); + .then(function (userData) { + // Prepare user cache + UserSrv.loadCache(userData.organisation); + + return deferred.resolve(userData); + }) + .catch(function (err) { + NotificationSrv.error('App', err.data, err.status); + return deferred.reject(err); + //return deferred.resolve(err.status === 520 ? err.status : null); + }); return deferred.promise; }, - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); }, - appLayout: function($q, $rootScope, AppLayoutSrv) { + appLayout: function ($q, $rootScope, AppLayoutSrv) { AppLayoutSrv.init(); return $q.resolve(); }, - uiConfig: function($q, UiSettingsSrv) { + uiConfig: function ($q, UiSettingsSrv) { return UiSettingsSrv.all(); }, - taxonomyCache: function(TaxonomyCacheSrv) { + taxonomyCache: function (TaxonomyCacheSrv) { return TaxonomyCacheSrv.all(); } } }) .state('app.index', { url: 'index', - onEnter: function($state, AuthenticationSrv) { - $state.go(AuthenticationSrv.getHomePage(), {}, {reload: true}); + onEnter: function ($state, AuthenticationSrv) { + $state.go(AuthenticationSrv.getHomePage(), {}, { reload: true }); } }) .state('app.main', { @@ -156,13 +156,13 @@ angular.module('thehive', [ controller: 'SearchCtrl', title: 'Search', resolve: { - metadata: function($q, DashboardSrv, NotificationSrv) { + metadata: function ($q, DashboardSrv, NotificationSrv) { var defer = $q.defer(); DashboardSrv.getMetadata() - .then(function(response) { + .then(function (response) { defer.resolve(response); - }, function(err) { + }, function (err) { NotificationSrv.error('DashboardViewCtrl', err.data, err.status); defer.reject(err); }); @@ -180,22 +180,22 @@ angular.module('thehive', [ controller: 'SettingsCtrl', title: 'Personal settings', resolve: { - currentUser: function($q, $state, $timeout, AuthenticationSrv, NotificationSrv) { + currentUser: function ($q, $state, $timeout, AuthenticationSrv, NotificationSrv) { var deferred = $q.defer(); AuthenticationSrv.current() - .then(function(userData) { - return deferred.resolve(userData); - }) - .catch( function(err) { - NotificationSrv.error('SettingsCtrl', err.data, err.status); + .then(function (userData) { + return deferred.resolve(userData); + }) + .catch(function (err) { + NotificationSrv.error('SettingsCtrl', err.data, err.status); - return deferred.reject(err); - }); + return deferred.reject(err); + }); return deferred.promise; }, - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); } } @@ -212,7 +212,7 @@ angular.module('thehive', [ controllerAs: '$vm', title: 'Platform administration', resolve: { - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); } }, @@ -227,7 +227,7 @@ angular.module('thehive', [ controllerAs: '$vm', title: 'Profiles administration', resolve: { - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); } }, @@ -242,7 +242,7 @@ angular.module('thehive', [ controllerAs: '$vm', title: 'Taxonomies administration', resolve: { - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); } }, @@ -257,7 +257,7 @@ angular.module('thehive', [ controllerAs: '$vm', title: 'ATT&CK patterns administration', resolve: { - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); } } @@ -272,7 +272,7 @@ angular.module('thehive', [ controllerAs: '$vm', title: 'Organisations administration', resolve: { - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); } }, @@ -286,16 +286,16 @@ angular.module('thehive', [ controller: 'OrgDetailsCtrl', controllerAs: '$vm', resolve: { - organisation: function($stateParams, OrganisationSrv) { + organisation: function ($stateParams, OrganisationSrv) { return OrganisationSrv.get($stateParams.organisation); }, - fields: function(CustomFieldsSrv){ + fields: function (CustomFieldsSrv) { return CustomFieldsSrv.all(); }, - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); }, - uiConfig: function($q, UiSettingsSrv) { + uiConfig: function ($q, UiSettingsSrv) { return UiSettingsSrv.all(true); } }, @@ -310,13 +310,13 @@ angular.module('thehive', [ controllerAs: 'vm', title: 'Analyzer templates administration', resolve: { - appConfig: function($q, VersionSrv) { + appConfig: function ($q, VersionSrv) { var defer = $q.defer(); VersionSrv.get() - .then(function(config) { + .then(function (config) { // Check if Cortex is enabled - if(VersionSrv.hasCortexConnector()) { + if (VersionSrv.hasCortexConnector()) { defer.resolve(config); } else { defer.reject(); @@ -347,9 +347,9 @@ angular.module('thehive', [ controllerAs: '$vm', title: 'Observable administration', resolve: { - types: function(ObservableTypeSrv, $q) { + types: function (ObservableTypeSrv, $q) { return ObservableTypeSrv.list() - .then(function(response) { + .then(function (response) { return $q.resolve(response.data); }); } @@ -365,13 +365,13 @@ angular.module('thehive', [ controller: 'CaseMainCtrl', title: 'Case', resolve: { - caze: function($q, $rootScope, $stateParams, CaseSrv, NotificationSrv) { + caze: function ($q, $stateParams, CaseSrv, NotificationSrv) { var deferred = $q.defer(); CaseSrv.getById($stateParams.caseId, true) - .then(function(data) { + .then(function (data) { deferred.resolve(data); - }).catch(function(response) { + }).catch(function (response) { deferred.reject(response); NotificationSrv.error('CaseMainCtrl', response.data, response.status); }); @@ -419,29 +419,29 @@ angular.module('thehive', [ controller: 'CaseSharingCtrl', controllerAs: '$vm', resolve: { - organisations: function(AuthenticationSrv, OrganisationSrv) { + organisations: function (AuthenticationSrv, OrganisationSrv) { return AuthenticationSrv.current() - .then(function(user) { + .then(function (user) { return OrganisationSrv.links(user.organisation); }) - .then(function(response) { - return _.map(response, function(item) { + .then(function (response) { + return _.map(response, function (item) { return _.pick(item, 'name', 'description'); }); }); }, - profiles: function(ProfileSrv) { + profiles: function (ProfileSrv) { return ProfileSrv.list() - .then(function(response) { + .then(function (response) { - return _.map(_.filter(response.data, function(item) { + return _.map(_.filter(response.data, function (item) { return !item.isAdmin; }), 'name'); }); }, - shares: function(CaseSrv, $stateParams) { + shares: function (CaseSrv, $stateParams) { return CaseSrv.getShares($stateParams.caseId) - .then(function(response) { + .then(function (response) { return response.data; }); } @@ -455,7 +455,7 @@ angular.module('thehive', [ templateUrl: 'views/partials/case/case.alerts.html', controller: 'CaseAlertsCtrl', resolve: { - alerts: function($stateParams, CaseSrv) { + alerts: function ($stateParams, CaseSrv) { return CaseSrv.alerts($stateParams.caseId); } }, @@ -468,14 +468,14 @@ angular.module('thehive', [ templateUrl: 'views/partials/case/case.tasks.item.html', controller: 'CaseTasksItemCtrl', resolve: { - task: function($q, $stateParams, CaseTaskSrv, NotificationSrv) { + task: function ($q, $stateParams, CaseTaskSrv, NotificationSrv) { var deferred = $q.defer(); CaseTaskSrv.getById($stateParams.itemId) - .then(function(data) { + .then(function (data) { deferred.resolve(data); }) - .catch(function(response) { + .catch(function (response) { deferred.reject(response); NotificationSrv.error('taskDetails', response.data, response.status); }); @@ -503,17 +503,17 @@ angular.module('thehive', [ templateUrl: 'views/partials/case/case.observables.item.html', controller: 'CaseObservablesItemCtrl', resolve: { - appConfig: function(VersionSrv) { + appConfig: function (VersionSrv) { return VersionSrv.get(); }, - artifact: function($q, $stateParams, CaseArtifactSrv, NotificationSrv) { + artifact: function ($q, $stateParams, CaseArtifactSrv, NotificationSrv) { var deferred = $q.defer(); CaseArtifactSrv.api().get({ 'artifactId': $stateParams.itemId - }).$promise.then(function(data) { + }).$promise.then(function (data) { deferred.resolve(data); - }).catch(function(response) { + }).catch(function (response) { deferred.reject(response); NotificationSrv.error('Observable Details', response.data, response.status); }); @@ -561,26 +561,26 @@ angular.module('thehive', [ controller: 'DashboardViewCtrl', controllerAs: '$vm', resolve: { - dashboard: function(NotificationSrv, DashboardSrv, $stateParams, $q) { + dashboard: function (NotificationSrv, DashboardSrv, $stateParams, $q) { var defer = $q.defer(); DashboardSrv.get($stateParams.id) - .then(function(response) { + .then(function (response) { defer.resolve(response.data); - }, function(err) { + }, function (err) { NotificationSrv.error('DashboardViewCtrl', err.data, err.status); defer.reject(err); }); return defer.promise; }, - metadata: function($q, DashboardSrv, NotificationSrv) { + metadata: function ($q, DashboardSrv, NotificationSrv) { var defer = $q.defer(); DashboardSrv.getMetadata() - .then(function(response) { + .then(function (response) { defer.resolve(response); - }, function(err) { + }, function (err) { NotificationSrv.error('DashboardViewCtrl', err.data, err.status); defer.reject(err); }); @@ -593,30 +593,30 @@ angular.module('thehive', [ } }); }) - .config(function($httpProvider) { + .config(function ($httpProvider) { 'use strict'; $httpProvider.defaults.xsrfCookieName = 'THEHIVE-XSRF-TOKEN'; $httpProvider.defaults.xsrfHeaderName = 'X-THEHIVE-XSRF-TOKEN'; - $httpProvider.interceptors.push(function($rootScope, $q) { - var isApiCall = function(url) { + $httpProvider.interceptors.push(function ($rootScope, $q) { + var isApiCall = function (url) { return url && url.startsWith('./api') && !url.startsWith('./api/stream'); }; return { - request: function(config) { + request: function (config) { if (isApiCall(config.url)) { $rootScope.async += 1; } return config; }, - response: function(response) { + response: function (response) { if (isApiCall(response.config.url)) { $rootScope.async -= 1; } return response; }, - responseError: function(rejection) { + responseError: function (rejection) { if (isApiCall(rejection.config.url)) { $rootScope.async -= 1; } @@ -625,7 +625,7 @@ angular.module('thehive', [ }; }); }) - .config(function(localStorageServiceProvider) { + .config(function (localStorageServiceProvider) { 'use strict'; localStorageServiceProvider @@ -633,7 +633,7 @@ angular.module('thehive', [ .setStorageType('localStorage') .setNotify(false, false); }) - .config(function(NotificationProvider) { + .config(function (NotificationProvider) { 'use strict'; NotificationProvider.setOptions({ @@ -647,14 +647,14 @@ angular.module('thehive', [ maxCount: 5 }); }) - .config(function($provide, markedProvider, hljsServiceProvider) { + .config(function ($provide, markedProvider, hljsServiceProvider) { 'use strict'; markedProvider.setOptions({ gfm: true, tables: true, sanitize: true, - highlight: function(code, lang) { + highlight: function (code, lang) { if (lang) { return hljs.highlight(lang, code, true).value; } else { @@ -672,55 +672,55 @@ angular.module('thehive', [ $provide.decorator('marked', [ '$delegate', function markedDecorator($delegate) { - // Credits: https://github.com/markedjs/marked/issues/655#issuecomment-383226346 - var defaults = markedProvider.defaults; + // Credits: https://github.com/markedjs/marked/issues/655#issuecomment-383226346 + var defaults = markedProvider.defaults; - var renderer = defaults.renderer; - var linkRenderer = _.wrap(renderer.link, function(originalLink, href, title, text) { - var html = originalLink.call(renderer, href, title, text); - return html.replace(/^ 0; }; - self.select = function(caze) { + self.select = function (caze) { if (caze.selected) { self.selection.push(caze); } else { - self.selection = _.reject(self.selection, function(item) { + self.selection = _.reject(self.selection, function (item) { return item._id === caze._id; }); } self.updateMenu(); }; - self.selectAll = function() { + self.selectAll = function () { var selected = self.menu.selectAll; - _.each(self.list.values, function(item) { - if(SecuritySrv.checkPermissions(['manageCase'], item.extraData.permissions)) { + _.each(self.list.values, function (item) { + if (SecuritySrv.checkPermissions(['manageCase'], item.extraData.permissions)) { item.selected = selected; } }); if (selected) { - self.selection = _.filter(self.list.values, function(item) { + self.selection = _.filter(self.list.values, function (item) { return !!item.selected; }); } else { @@ -209,9 +181,9 @@ this.search(); }; - this.filterMyCases = function() { + this.filterMyCases = function () { this.filtering.clearFilters() - .then(function() { + .then(function () { var currentUser = AuthenticationSrv.currentUser; self.filtering.addFilter({ field: 'assignee', @@ -227,9 +199,9 @@ }); }; - this.filterMyOrgCases = function() { + this.filterMyOrgCases = function () { this.filtering.clearFilters() - .then(function() { + .then(function () { var currentUser = AuthenticationSrv.currentUser; self.filtering.addFilter({ field: 'owningOrganisation', @@ -246,9 +218,9 @@ }); }; - this.filterSharedWithMyOrg = function() { + this.filterSharedWithMyOrg = function () { this.filtering.clearFilters() - .then(function() { + .then(function () { var currentUser = AuthenticationSrv.currentUser; self.filtering.addFilter({ field: 'owningOrganisation', @@ -265,9 +237,9 @@ }); }; - this.filterMyOpenCases = function() { + this.filterMyOpenCases = function () { this.filtering.clearFilters() - .then(function() { + .then(function () { var currentUser = AuthenticationSrv.currentUser; self.filtering.addFilter({ field: 'assignee', @@ -283,36 +255,36 @@ }); }; - this.filterByStatus = function(status) { + this.filterByStatus = function (status) { this.filtering.clearFilters() - .then(function() { + .then(function () { self.addFilterValue('status', status); }); }; - this.filterByResolutionStatus = function(status) { + this.filterByResolutionStatus = function (status) { this.filtering.clearFilters() - .then(function() { + .then(function () { self.filtering.addFilterValue('resolutionStatus', status); self.addFilterValue('status', 'Resolved'); }); }; - this.sortBy = function(sort) { + this.sortBy = function (sort) { this.list.sort = sort; this.list.update(); this.filtering.setSort(sort); }; - this.sortByField = function(field) { + this.sortByField = function (field) { var context = this.filtering.context; var currentSort = Array.isArray(context.sort) ? _.without(context.sort, '-flag', '+flag')[0] : context.sort; var sort = null; - if(currentSort.substr(1) !== field) { + if (currentSort.substr(1) !== field) { sort = ['-flag', '+' + field]; } else { - sort = ['-flag', (currentSort === '+' + field) ? '-'+field : '+'+field]; + sort = ['-flag', (currentSort === '+' + field) ? '-' + field : '+' + field]; } self.list.sort = sort; @@ -320,20 +292,20 @@ self.filtering.setSort(sort); }; - this.bulkFlag = function(flag) { + this.bulkFlag = function (flag) { var ids = _.pluck(self.selection, '_id'); - return CaseSrv.bulkUpdate(ids, {flag: flag}) - .then(function(/*responses*/) { + return CaseSrv.bulkUpdate(ids, { flag: flag }) + .then(function (/*responses*/) { NotificationSrv.log('Selected cases have been updated successfully', 'success'); }) - .catch(function(err) { + .catch(function (err) { NotificationSrv.error('Bulk flag cases', err.data, err.status); }); } - this.bulkEdit = function() { + this.bulkEdit = function () { var modal = $uibModal.open({ animation: 'true', templateUrl: 'views/partials/case/case.update.html', @@ -341,22 +313,22 @@ controllerAs: '$dialog', size: 'lg', resolve: { - selection: function() { + selection: function () { return self.selection; } } }); - modal.result.then(function(operations) { - $q.all(_.map(operations, function(operation) { + modal.result.then(function (operations) { + $q.all(_.map(operations, function (operation) { return CaseSrv.bulkUpdate(operation.ids, operation.patch); - })).then(function(/*responses*/) { + })).then(function (/*responses*/) { NotificationSrv.log('Selected cases have been updated successfully', 'success'); }); }); }; - this.bulkRemove = function() { + this.bulkRemove = function () { var modal = $uibModal.open({ animation: 'true', templateUrl: 'views/partials/case/case.bulk.delete.confirm.html', @@ -364,49 +336,49 @@ controllerAs: '$dialog', size: 'lg', resolve: { - selection: function() { + selection: function () { return self.selection; } } }); - modal.result.catch(function(err) { - if(err && !_.isString(err)) { + modal.result.catch(function (err) { + if (err && !_.isString(err)) { NotificationSrv.error('Case Remove', err.data, err.status); } }) } - this.bulkReopen = function() { + this.bulkReopen = function () { return ModalUtilsSrv.confirm('Reopen cases', 'Are you sure you want to reopen the selected cases?', { okText: 'Yes, proceed' - }).then(function() { + }).then(function () { var ids = _.pluck(self.selection, '_id'); - return CaseSrv.bulkUpdate(ids, {status: 'Open'}) - .then(function(/*responses*/) { + return CaseSrv.bulkUpdate(ids, { status: 'Open' }) + .then(function (/*responses*/) { NotificationSrv.log('Selected cases have been reopened successfully', 'success'); }) - .catch(function(err) { + .catch(function (err) { NotificationSrv.error('Bulk reopen cases', err.data, err.status); }); }); } - this.closeCase = function(caze) { + this.closeCase = function (caze) { var scope = $rootScope.$new(); scope.CaseResolutionStatus = CaseResolutionStatus; scope.CaseImpactStatus = CaseImpactStatus; scope.caseId = caze._id; - scope.updateField = function(data) { + scope.updateField = function (data) { return CaseSrv.update({ caseId: caze._id }, data) - .$promise - .then(function(/*response*/) { - return caze; - }); + .$promise + .then(function (/*response*/) { + return caze; + }); }; var modal = $uibModal.open({ @@ -415,63 +387,63 @@ controller: 'CaseCloseModalCtrl', size: 'lg', resolve: { - caze: function() { + caze: function () { return angular.copy(caze); } } }) - return modal.result.catch(function(err){ - if(err && !_.isString(err)) { + return modal.result.catch(function (err) { + if (err && !_.isString(err)) { NotificationSrv.error('Case bulk close', err.data, err.status); } }); } - $scope.updateField = function(data) { + $scope.updateField = function (data) { return CaseSrv.update({ caseId: caseId }, data).$promise; }; - this.bulkClose = function() { - return ModalUtilsSrv.confirm('Close cases', 'Are you sure you want to close the selected ' + self.selection.length+' case(s)?', { + this.bulkClose = function () { + return ModalUtilsSrv.confirm('Close cases', 'Are you sure you want to close the selected ' + self.selection.length + ' case(s)?', { okText: 'Yes, proceed' - }).then(function() { - return self.selection.reduce(function(initialPromise, nextCase) { + }).then(function () { + return self.selection.reduce(function (initialPromise, nextCase) { return initialPromise .then(self.closeCase(nextCase)); }, $q.resolve()); - }).catch(function(err){ - if(err && !_.isString(err)) { + }).catch(function (err) { + if (err && !_.isString(err)) { NotificationSrv.error('Case bulk close', err.data, err.status); } }); } - this.getCaseResponders = function(caze, force) { + this.getCaseResponders = function (caze, force) { if (!force && this.caseResponders !== null) { return; } self.caseResponders = null; CortexSrv.getResponders('case', caze._id) - .then(function(responders){ + .then(function (responders) { self.caseResponders = responders; return CortexSrv.promntForResponder(responders); }) - .then(function(response) { - if(response && _.isString(response)) { + .then(function (response) { + if (response && _.isString(response)) { NotificationSrv.log(response, 'warning'); } else { return CortexSrv.runResponder(response.id, response.name, 'case', _.pick(caze, '_id', 'tlp', 'pap')); } }) - .then(function(response){ + .then(function (response) { NotificationSrv.log(['Responder', response.data.responderName, 'started successfully on case', caze.title].join(' '), 'success'); }) - .catch(function(err) { - if(err && !_.isString(err)) { + .catch(function (err) { + if (err && !_.isString(err)) { NotificationSrv.error('CaseList', err.data, err.status); } }); @@ -486,28 +458,28 @@ this.typedCount = undefined; this.loading = false; - this.ok = function() { + this.ok = function () { $uibModalInstance.close(); } - this.cancel = function() { + this.cancel = function () { $uibModalInstance.dismiss(); } - this.confirm = function() { + this.confirm = function () { self.loading = true; - var promises = _.map(self.selection, function(caze) { + var promises = _.map(self.selection, function (caze) { return CaseSrv.forceRemove({ caseId: caze._id }).$promise; }); $q.all(promises) - .then(function(responses) { + .then(function (responses) { self.loading = false; NotificationSrv.log('Cases have been deleted successfully: ' + responses.length, 'success'); $uibModalInstance.close(); }) - .catch(function(errors) { + .catch(function (errors) { self.loading = false; - _.each(errors, function(err) { + _.each(errors, function (err) { NotificationSrv.error('Bulk delete cases', err.data, err.status); }); }) diff --git a/frontend/app/scripts/controllers/case/CaseMainCtrl.js b/frontend/app/scripts/controllers/case/CaseMainCtrl.js index ebd98a62c1..553f3f297f 100644 --- a/frontend/app/scripts/controllers/case/CaseMainCtrl.js +++ b/frontend/app/scripts/controllers/case/CaseMainCtrl.js @@ -1,7 +1,7 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveControllers').controller('CaseMainCtrl', - function($scope, $rootScope, $state, $stateParams, $q, $uibModal, CaseTabsSrv, CaseSrv, UserSrv, MispSrv, StreamSrv, StreamQuerySrv, StreamStatSrv, NotificationSrv, UtilsSrv, CaseResolutionStatus, CaseImpactStatus, CortexSrv, caze) { + function ($scope, $rootScope, $state, $stateParams, $q, $uibModal, CaseTabsSrv, CaseSrv, UserSrv, StreamSrv, StreamQuerySrv, NotificationSrv, UtilsSrv, CaseResolutionStatus, CaseImpactStatus, CortexSrv, caze) { $scope.CaseResolutionStatus = CaseResolutionStatus; $scope.CaseImpactStatus = CaseImpactStatus; $scope.caseResponders = null; @@ -24,32 +24,36 @@ $scope.newestLink = null; $scope.oldestLink = null; + $scope.tasksCount = null; + $scope.observablesCount = null; + $scope.alertCount = null; + $scope.caze = caze; $scope.userPermissions = (caze.extraData.permissions || []).join(','); $rootScope.title = 'Case #' + caze.number + ': ' + caze.title; $scope.canEdit = caze.extraData.permissions.indexOf('manageCase') !== -1; - $scope.initExports = function() { - $scope.existingExports = _.filter($scope.caze.extraData.alerts || [], function(item) { + $scope.initExports = function () { + $scope.existingExports = _.filter($scope.caze.extraData.alerts || [], function (item) { return item.type === 'misp'; }).length; }; $scope.initExports(); - $scope.countIoc = function(link) { - return _.filter(link.linkedWith, function(l) { + $scope.countIoc = function (link) { + return _.filter(link.linkedWith, function (l) { return l.ioc; }).length; }; CaseSrv.links({ caseId: $scope.caseId - }, function(data) { - $scope.links = _.map(data, function(item){ - item.linksCount = item.linkedWith.length || 0; + }, function (data) { + $scope.links = _.map(data, function (item) { + item.linksCount = item.linkedWith.length || 0; - return item; + return item; }); if (data.length > 0) { @@ -67,15 +71,15 @@ scope: $scope, rootId: $scope.caseId, objectType: 'case', - callback: function(updates) { + callback: function (updates) { CaseSrv.getById($stateParams.caseId, true) - .then(function(data) { + .then(function (data) { $scope.caze = data; - if(updates.length === 1 && updates[0] && updates[0].base.details.customFields){ + if (updates.length === 1 && updates[0] && updates[0].base.details.customFields) { $scope.$broadcast('case:refresh-custom-fields'); } - }).catch(function(response) { + }).catch(function (response) { NotificationSrv.error('CaseMainCtrl', response.data, response.status); }); } @@ -83,15 +87,7 @@ // Stats for case tasks counter StreamQuerySrv('v1', [ - {_name: 'getCase', idOrName: caseId}, - {_name: 'tasks'}, - {_name: 'filter', - _not: { - '_field': 'status', - '_value': 'Cancel' - } - }, - {_name: 'count'} + { _name: 'countTask', caseId: caseId }, ], { scope: $scope, rootId: caseId, @@ -101,16 +97,15 @@ name: 'task-stats-' + caseId } }, - onUpdate: function(updates) { + guard: UtilsSrv.hasAddDeleteEvents, + onUpdate: function (updates) { $scope.tasksCount = updates; } }); // Stats for case observables counter StreamQuerySrv('v1', [ - {_name: 'getCase', idOrName: caseId}, - {_name: 'observables'}, - {_name: 'count'} + { _name: 'countCaseObservable', caseId: caseId }, ], { scope: $scope, rootId: caseId, @@ -120,16 +115,15 @@ name: 'observable-stats-' + caseId } }, - onUpdate: function(updates) { - $scope.observableCount = updates; + guard: UtilsSrv.hasAddDeleteEvents, + onUpdate: function (updates) { + $scope.observablesCount = updates; } }); - // Stats for case observables counter + // Stats for case alerts counter StreamQuerySrv('v1', [ - {_name: 'getCase', idOrName: caseId}, - {_name: 'alerts'}, - {_name: 'count'} + { _name: 'countRelatedAlert', caseId: caseId }, ], { scope: $scope, rootId: caseId, @@ -139,34 +133,34 @@ name: 'alert-stats-' + caseId } }, - onUpdate: function(updates) { + onUpdate: function (updates) { $scope.alertCount = updates; } }); - $scope.$on('tasks:task-removed', function(event, task) { + $scope.$on('tasks:task-removed', function (event, task) { CaseTabsSrv.removeTab('task-' + task._id); }); - $scope.$on('observables:observable-removed', function(event, observable) { + $scope.$on('observables:observable-removed', function (event, observable) { CaseTabsSrv.removeTab('observable-' + observable._id); }); - $scope.openTab = function(tabName) { + $scope.openTab = function (tabName) { var tab = CaseTabsSrv.getTab(tabName), params = angular.extend({}, $state.params, tab.params || {}); $state.go(tab.state, params); }; - $scope.removeTab = function(tab) { + $scope.removeTab = function (tab) { var switchToDetails = CaseTabsSrv.removeTab(tab); - if(switchToDetails) { + if (switchToDetails) { $scope.openTab('details'); } }; - $scope.switchFlag = function() { + $scope.switchFlag = function () { if ($scope.caze.flag === true) { $scope.updateField('flag', false); } else { @@ -175,7 +169,7 @@ }; // update a specific case field - $scope.updateField = function(fieldName, newValue) { + $scope.updateField = function (fieldName, newValue) { var data = {}; if (angular.isString(fieldName)) { @@ -188,10 +182,10 @@ CaseSrv.update({ caseId: $scope.caseId - }, data, function(/*response*/) { + }, data, function (/*response*/) { //UtilsSrv.shallowClearAndCopy(response, $scope.caze); defer.resolve($scope.caze); - }, function(response) { + }, function (response) { NotificationSrv.error('caseDetails', response.data, response.status); defer.reject(response); }); @@ -199,33 +193,33 @@ return defer.promise; }; - $scope.isCaseClosed = function() { + $scope.isCaseClosed = function () { return $scope.caze.status === 'Resolved'; }; - $scope.isCaseTruePositive = function() { + $scope.isCaseTruePositive = function () { return $scope.caze.resolutionStatus === 'TruePositive'; }; - $scope.openCloseDialog = function() { + $scope.openCloseDialog = function () { var modalInstance = $uibModal.open({ scope: $scope, templateUrl: 'views/partials/case/case.close.html', controller: 'CaseCloseModalCtrl', size: 'lg', resolve: { - caze: function() { + caze: function () { return angular.copy($scope.caze); } } }); - modalInstance.result.then(function() { + modalInstance.result.then(function () { $state.go('app.cases'); }); }; - $scope.reopenCase = function() { + $scope.reopenCase = function () { $uibModal.open({ scope: $scope, templateUrl: 'views/partials/case/case.reopen.html', @@ -233,26 +227,26 @@ }); }; - $scope.mergeCase = function() { + $scope.mergeCase = function () { var caseModal = $uibModal.open({ templateUrl: 'views/partials/case/case.merge.html', controller: 'CaseMergeModalCtrl', controllerAs: 'dialog', size: 'lg', resolve: { - source: function() { + source: function () { return $scope.caze; }, - title: function() { + title: function () { return 'Merge Case #' + $scope.caze.number; }, - prompt: function() { + prompt: function () { return '#' + $scope.caze.number + ': ' + $scope.caze.title; } } }); - caseModal.result.then(function(selectedCase) { + caseModal.result.then(function (selectedCase) { CaseSrv.merge([$scope.caze._id, selectedCase._id]) .then(function (response) { var merged = response.data; @@ -272,15 +266,15 @@ // caseId: $scope.caze.id, // mergedCaseId: selectedCase.id // }, , ); - }).catch(function(err) { - if(err && !_.isString(err)) { + }).catch(function (err) { + if (err && !_.isString(err)) { NotificationSrv.error('Case Merge', err.data, err.status); } }); }; - $scope.exportToMisp = function() { - if($scope.appConfig.connectors.misp && $scope.appConfig.connectors.misp.servers.length === 0) { + $scope.exportToMisp = function () { + if ($scope.appConfig.connectors.misp && $scope.appConfig.connectors.misp.servers.length === 0) { NotificationSrv.log('There are no MISP servers defined', 'error'); return; } @@ -291,71 +285,71 @@ controllerAs: 'dialog', size: 'lg', resolve: { - caze: function() { + caze: function () { return $scope.caze; }, - config: function() { + config: function () { return $scope.appConfig.connectors.misp; } } }); - modalInstance.result.then(function() { + modalInstance.result.then(function () { return CaseSrv.getById($scope.caseId, true); // return CaseSrv.get({ // 'caseId': $scope.caseId, // 'nstats': true // }).$promise; - }).then(function(data) { + }).then(function (data) { $scope.caze = data; $scope.initExports(); }); }; - $scope.removeCase = function() { - var modalInstance = $uibModal.open({ - templateUrl: 'views/partials/case/case.delete.confirm.html', - controller: 'CaseDeleteModalCtrl', - resolve: { - caze: function() { - return $scope.caze; - } - } - }); - - modalInstance.result.then(function() { - $state.go('app.cases'); - }) - .catch(function(err) { - if(err && !_.isString(err)) { - NotificationSrv.error('caseDetails', err.data, err.status); - } - }); + $scope.removeCase = function () { + var modalInstance = $uibModal.open({ + templateUrl: 'views/partials/case/case.delete.confirm.html', + controller: 'CaseDeleteModalCtrl', + resolve: { + caze: function () { + return $scope.caze; + } + } + }); + + modalInstance.result.then(function () { + $state.go('app.cases'); + }) + .catch(function (err) { + if (err && !_.isString(err)) { + NotificationSrv.error('caseDetails', err.data, err.status); + } + }); }; - $scope.getCaseResponders = function(force) { - if(!force && $scope.caseResponders !== null) { - return; + $scope.getCaseResponders = function (force) { + if (!force && $scope.caseResponders !== null) { + return; } $scope.caseResponders = null; CortexSrv.getResponders('case', $scope.caseId) - .then(function(responders){ + .then(function (responders) { $scope.caseResponders = responders; return CortexSrv.promntForResponder(responders); }) - .then(function(response) { - if(response && _.isString(response)) { + .then(function (response) { + if (response && _.isString(response)) { NotificationSrv.log(response, 'warning'); } else { return CortexSrv.runResponder(response.id, response.name, 'case', _.pick($scope.caze, '_id', 'tlp', 'pap')); } }) - .then(function(response){ + .then(function (response) { NotificationSrv.log(['Responder', response.data.responderName, 'started successfully on case', $scope.caze.title].join(' '), 'success'); }) - .catch(function(err) { - if(err && !_.isString(err)) { + .catch(function (err) { + if (err && !_.isString(err)) { NotificationSrv.error('caseDetails', err.data, err.status); } }); @@ -365,21 +359,21 @@ * A workaround filter to make sure the ngRepeat doesn't order the * object keys */ - $scope.notSorted = function(obj) { + $scope.notSorted = function (obj) { if (!obj) { return []; } return Object.keys(obj); }; - $scope.keys = function(obj) { + $scope.keys = function (obj) { return _.keys(obj); }; - $scope.getTags = function(selection) { + $scope.getTags = function (selection) { var tags = []; - angular.forEach(selection, function(tag) { + angular.forEach(selection, function (tag) { tags.push(tag.text); }); diff --git a/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js b/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js index 4a59571002..95d3b631fb 100644 --- a/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js +++ b/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js @@ -17,7 +17,7 @@ selectAll: false }; - this.$onInit = function() { + this.$onInit = function () { $scope.filtering = new FilteringSrv('observable', 'observable.list', { version: 'v1', defaults: { @@ -30,7 +30,7 @@ }); $scope.filtering.initContext($scope.caseId) - .then(function() { + .then(function () { $scope.load(); $scope.initAnalyzersList(); @@ -40,19 +40,19 @@ scope: $scope, rootId: $scope.caseId, objectType: 'case_artifact_job', - callback: function(data) { + callback: function (data) { var successFound = false; var i = 0; var ln = data.length; - while(!successFound && i < ln) { - if(data[i].base.operation === 'Update' && data[i].base.details.status === 'Success') { + while (!successFound && i < ln) { + if (data[i].base.operation === 'Update' && data[i].base.details.status === 'Success') { successFound = true; } i++; } - if(successFound) { + if (successFound) { $scope.artifacts.update(); } } @@ -64,7 +64,7 @@ }); }; - $scope.load = function() { + $scope.load = function () { $scope.artifacts = new PaginatedQuerySrv({ name: 'observables', root: $scope.caseId, @@ -72,22 +72,23 @@ streamObjectType: 'case_artifact', version: 'v1', scope: $scope, + limitedCount: true, sort: $scope.filtering.context.sort, pageSize: $scope.filtering.context.pageSize, filter: $scope.filtering.buildQuery(), extraData: ['seen', 'permissions', 'shareCount'], // extraData: ['seen', 'permissions', 'isOwner', 'shareCount'], operations: [ - {'_name': 'getCase', 'idOrName': $scope.caseId}, - {'_name': 'observables'} + { '_name': 'getCase', 'idOrName': $scope.caseId }, + { '_name': 'observables' } ], - onUpdate: function() { + onUpdate: function () { $scope.resetSelection(); } }); }; - $scope.resetSelection = function() { + $scope.resetSelection = function () { if ($scope.menu.selectAll) { $scope.selectAll(); } else { @@ -97,15 +98,15 @@ } }; - $scope.sortByField = function(field) { + $scope.sortByField = function (field) { var context = this.filtering.context; var currentSort = Array.isArray(context.sort) ? context.sort[0] : context.sort; var sort = null; - if(currentSort.substr(1) !== field) { + if (currentSort.substr(1) !== field) { sort = ['+' + field]; } else { - sort = [(currentSort === '+' + field) ? '-'+field : '+'+field]; + sort = [(currentSort === '+' + field) ? '-' + field : '+' + field]; } $scope.artifacts.sort = sort; @@ -113,7 +114,7 @@ $scope.filtering.setSort(sort); }; - $scope.keys = function(obj) { + $scope.keys = function (obj) { return _.keys(obj || {}); }; @@ -149,13 +150,13 @@ $scope.search(); }; - $scope.filterByTlp = function(value) { + $scope.filterByTlp = function (value) { $scope.addFilterValue('tlp', value); }; // *************************************************** - $scope.countReports = function(observable) { + $scope.countReports = function (observable) { return _.keys(observable.reports).length; }; @@ -174,7 +175,7 @@ // init lists // $scope.initAnalyzersList = function () { - if($scope.analysisEnabled) { + if ($scope.analysisEnabled) { AnalyzerSrv.query() .then(function (analyzers) { $scope.analyzersList.analyzers = analyzers; @@ -186,14 +187,14 @@ $scope.selectAll = function () { var selected = $scope.menu.selectAll; - _.each($scope.artifacts.values, function(item) { - if(SecuritySrv.checkPermissions(['manageObservable'], item.extraData.permissions)) { + _.each($scope.artifacts.values, function (item) { + if (SecuritySrv.checkPermissions(['manageObservable'], item.extraData.permissions)) { item.selected = selected; } }); if (selected) { - $scope.selection.artifacts = _.filter($scope.artifacts.values, function(item) { + $scope.selection.artifacts = _.filter($scope.artifacts.values, function (item) { return !!item.selected; }); } else { @@ -206,7 +207,7 @@ if (artifact.selected) { $scope.selection.artifacts.push(artifact); } else { - $scope.selection.artifacts = _.reject($scope.selection.artifacts, function(item) { + $scope.selection.artifacts = _.reject($scope.selection.artifacts, function (item) { return item._id === artifact._id; }); } @@ -220,17 +221,17 @@ controller: 'ObservableCreationCtrl', size: 'lg', resolve: { - params: function() { + params: function () { return null; }, - tags: function() { + tags: function () { return []; } } }); }; - $scope.bulkEdit = function() { + $scope.bulkEdit = function () { var modal = $uibModal.open({ animation: 'true', templateUrl: 'views/partials/observables/observable.update.html', @@ -238,22 +239,22 @@ controllerAs: '$dialog', size: 'lg', resolve: { - selection: function() { + selection: function () { return $scope.selection.artifacts; } } }); - modal.result.then(function(operations) { - $q.all(_.map(operations, function(operation) { + modal.result.then(function (operations) { + $q.all(_.map(operations, function (operation) { return CaseArtifactSrv.bulkUpdate(operation.ids, operation.patch); - })).then(function(/*responses*/) { + })).then(function (/*responses*/) { NotificationSrv.log('Selected observables have been updated successfully', 'success'); }); }); }; - $scope.bulkAnalyze = function() { + $scope.bulkAnalyze = function () { var modal = $uibModal.open({ animation: 'true', templateUrl: 'views/partials/observables/observable.analyze.html', @@ -261,22 +262,22 @@ controllerAs: '$dialog', size: 'lg', resolve: { - selection: function() { + selection: function () { return $scope.selection.artifacts; }, - analyzers: function() { + analyzers: function () { return $scope.analyzersList.analyzers; } } }); - modal.result.then(function(operations) { + modal.result.then(function (operations) { var analyzerNames = _.uniq(_.pluck(operations, 'analyzerName')); CortexSrv.getServers(analyzerNames) - .then(function(serverId) { + .then(function (serverId) { return $q.all( - _.map(operations, function(item) { + _.map(operations, function (item) { return CortexSrv.createJob({ cortexId: serverId, artifactId: item.observableId, @@ -285,19 +286,19 @@ }) ); }) - .then(function() { + .then(function () { NotificationSrv.log('Analyzers have been successfully started for the selected observables', 'success'); - }, function() { + }, function () { }); }); }; - $scope.showExport = function() { + $scope.showExport = function () { $scope.showExportPanel = true; }; - $scope.hideExport = function() { + $scope.hideExport = function () { $scope.showExportPanel = false; }; @@ -306,18 +307,18 @@ ModalUtilsSrv.confirm('Remove Observables', 'Are you sure you want to delete the selected Observables?', { okText: 'Yes, remove them', flavor: 'danger' - }).then(function() { + }).then(function () { - $q.all(_.map($scope.selection.artifacts, function(observable) { + $q.all(_.map($scope.selection.artifacts, function (observable) { return CaseArtifactSrv.api().delete({ artifactId: observable._id }, function () { $scope.$emit('observables:observable-removed', observable); }).$promise; })); - }).then(function(/*responses*/) { + }).then(function (/*responses*/) { NotificationSrv.log('The selected observables have been deleted', 'success'); - }).catch(function(/*err*/) { + }).catch(function (/*err*/) { //NotificationSrv.error('Observable deletion', response.data, response.status); }); }; @@ -341,9 +342,9 @@ var analyzerIds = _.uniq(_.pluck(toRun, 'analyzerId')); CortexSrv.getServers(analyzerIds) - .then(function(serverId) { + .then(function (serverId) { return $q.all( - _.map(toRun, function(item) { + _.map(toRun, function (item) { return CortexSrv.createJob({ cortexId: serverId, artifactId: item.artifact._id, @@ -352,9 +353,9 @@ }) ); }) - .then(function() { + .then(function () { NotificationSrv.log('Analyzers have been successfully started for ' + nbArtifacts + ' observables', 'success'); - }, function() { + }, function () { }); }; @@ -365,10 +366,10 @@ }); }; - $scope.showReport = function(observable, analyzerId) { + $scope.showReport = function (observable, analyzerId) { CortexSrv.getJobs($scope.caseId, observable._id, analyzerId, 1) - .then(function(response){ - if(!response.data || response.data.length !== 1) { + .then(function (response) { + if (!response.data || response.data.length !== 1) { return; } @@ -388,56 +389,56 @@ controllerAs: '$vm', size: 'max', resolve: { - report: function() { + report: function () { return report; }, - observable: function() { + observable: function () { return observable; } } }); }) - .catch(function(/*err*/) { + .catch(function (/*err*/) { NotificationSrv.error('Unable to fetch the analysis report'); }); }; - $scope.getObsResponders = function(observable, force) { - if(!force && $scope.obsResponders !== null) { - return; + $scope.getObsResponders = function (observable, force) { + if (!force && $scope.obsResponders !== null) { + return; } $scope.obsResponders = null; CortexSrv.getResponders('case_artifact', observable._id) - .then(function(responders) { - $scope.obsResponders = responders; - return CortexSrv.promntForResponder(responders); - }) - .then(function(response) { - if(response && _.isString(response)) { - NotificationSrv.log(response, 'warning'); - } else { - return CortexSrv.runResponder(response.id, response.name, 'case_artifact', _.pick(observable, '_id')); - } - }) - .then(function(response){ - var data = '['+$filter('fang')(observable.data || observable.attachment.name)+']'; - NotificationSrv.log(['Responder', response.data.responderName, 'started successfully on observable', data].join(' '), 'success'); - }) - .catch(function(err) { - if(err && !_.isString(err)) { - NotificationSrv.error('observablesList', err.data, err.status); - } - }); + .then(function (responders) { + $scope.obsResponders = responders; + return CortexSrv.promntForResponder(responders); + }) + .then(function (response) { + if (response && _.isString(response)) { + NotificationSrv.log(response, 'warning'); + } else { + return CortexSrv.runResponder(response.id, response.name, 'case_artifact', _.pick(observable, '_id')); + } + }) + .then(function (response) { + var data = '[' + $filter('fang')(observable.data || observable.attachment.name) + ']'; + NotificationSrv.log(['Responder', response.data.responderName, 'started successfully on observable', data].join(' '), 'success'); + }) + .catch(function (err) { + if (err && !_.isString(err)) { + NotificationSrv.error('observablesList', err.data, err.status); + } + }); }; } ) - .controller('JobReportModalCtrl', function($uibModalInstance, report, observable) { - this.report = report; - this.observable = observable; - this.close = function() { - $uibModalInstance.dismiss(); - }; - }); + .controller('JobReportModalCtrl', function ($uibModalInstance, report, observable) { + this.report = report; + this.observable = observable; + this.close = function () { + $uibModalInstance.dismiss(); + }; + }); })(); diff --git a/frontend/app/scripts/controllers/case/CaseTasksCtrl.js b/frontend/app/scripts/controllers/case/CaseTasksCtrl.js index 039b1aafaa..14a370d284 100755 --- a/frontend/app/scripts/controllers/case/CaseTasksCtrl.js +++ b/frontend/app/scripts/controllers/case/CaseTasksCtrl.js @@ -1,9 +1,9 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveControllers') .controller('CaseTasksCtrl', CaseTasksCtrl); - function CaseTasksCtrl($scope, $state, $stateParams, $q, $uibModal, AuthenticationSrv, ModalUtilsSrv, FilteringSrv, CaseTabsSrv, PaginatedQuerySrv, CaseTaskSrv, UserSrv, NotificationSrv, CortexSrv, AppLayoutSrv) { + function CaseTasksCtrl($scope, $state, $stateParams, $q, AuthenticationSrv, ModalUtilsSrv, FilteringSrv, CaseTabsSrv, PaginatedQuerySrv, CaseTaskSrv, NotificationSrv, CortexSrv, AppLayoutSrv) { CaseTabsSrv.activateTab($state.current.data.tab); @@ -23,7 +23,7 @@ selectAll: false }; - this.$onInit = function() { + this.$onInit = function () { $scope.filtering = new FilteringSrv('task', 'task.list', { version: 'v1', defaults: { @@ -36,53 +36,55 @@ }); $scope.filtering.initContext($scope.caseId) - .then(function() { + .then(function () { $scope.load(); - $scope.$watchCollection('tasks.pageSize', function (newValue) { + $scope.$watch('list.pageSize', function (newValue) { $scope.filtering.setPageSize(newValue); }); }); }; - $scope.getAssignableUsers = function(taskId) { + $scope.getAssignableUsers = function (taskId) { return [ - {_name: 'getTask', idOrName: taskId}, - {_name: 'assignableUsers'} + { _name: 'getTask', idOrName: taskId }, + { _name: 'assignableUsers' } ]; }; - $scope.load = function() { + $scope.load = function () { $scope.list = new PaginatedQuerySrv({ name: 'case-tasks', root: $scope.caseId, - objectType: 'case_task', + objectType: 'task', + streamObjectType: 'case_task', version: 'v1', scope: $scope, sort: $scope.filtering.context.sort, loadAll: false, + limitedCount: true, pageSize: $scope.filtering.context.pageSize, filter: $scope.filtering.buildQuery(), baseFilter: { - _not: { + _ne: { _field: 'status', _value: 'Cancel' } }, operations: [ - {'_name': 'getCase', "idOrName": $scope.caseId}, - {'_name': 'tasks'} + { '_name': 'getCase', "idOrName": $scope.caseId }, + { '_name': 'tasks' } ], extraData: ['shareCount', 'actionRequired'], //extraData: ['isOwner', 'shareCount'], - onUpdate: function() { + onUpdate: function () { $scope.buildTaskGroups($scope.list.values); $scope.resetSelection(); } }); }; - $scope.resetSelection = function() { + $scope.resetSelection = function () { if ($scope.menu.selectAll) { $scope.selectAll(); } else { @@ -92,7 +94,7 @@ } }; - $scope.updateMenu = function() { + $scope.updateMenu = function () { // Handle flag/unflag menu items var temp = _.uniq(_.pluck($scope.selection, 'flag')); $scope.menu.unflag = temp.length === 1 && temp[0] === true; @@ -113,28 +115,28 @@ $scope.menu.delete = $scope.selection.length > 0; }; - $scope.select = function(task) { + $scope.select = function (task) { if (task.selected) { $scope.selection.push(task); } else { - $scope.selection = _.reject($scope.selection, function(item) { + $scope.selection = _.reject($scope.selection, function (item) { return item._id === task._id; }); } $scope.updateMenu(); }; - $scope.selectAll = function() { + $scope.selectAll = function () { var selected = $scope.menu.selectAll; - _.each($scope.list.values, function(item) { + _.each($scope.list.values, function (item) { // if(SecuritySrv.checkPermissions(['manageCase'], item.extraData.permissions)) { item.selected = selected; // } }); if (selected) { - $scope.selection = _.filter($scope.list.values, function(item) { + $scope.selection = _.filter($scope.list.values, function (item) { return !!item.selected; }); } else { @@ -144,8 +146,6 @@ $scope.updateMenu(); }; - // ######################@@@ - $scope.toggleStats = function () { $scope.filtering.toggleStats(); }; @@ -177,16 +177,16 @@ $scope.search(); }; - $scope.filterBy = function(field, value) { + $scope.filterBy = function (field, value) { $scope.filtering.clearFilters() - .then(function() { + .then(function () { $scope.addFilterValue(field, value); }); }; - $scope.filterMyTasks = function() { + $scope.filterMyTasks = function () { $scope.filtering.clearFilters() - .then(function() { + .then(function () { var currentUser = AuthenticationSrv.currentUser; $scope.filtering.addFilter({ field: 'assignee', @@ -202,31 +202,31 @@ }); }; - $scope.toggleGroupedView = function() { + $scope.toggleGroupedView = function () { $scope.state.showGrouped = !$scope.state.showGrouped; AppLayoutSrv.groupTasks($scope.state.showGrouped); }; - $scope.buildTaskGroups = function(tasks) { + $scope.buildTaskGroups = function (tasks) { // Sort tasks by order - var orderedTasks = _.sortBy(_.map(tasks, function(t) { + var orderedTasks = _.sortBy(_.map(tasks, function (t) { return _.pick(t, 'group', 'order'); }), 'order'); var groups = []; // Get group names by keeping the group orders - _.each(orderedTasks, function(task) { - if(groups.indexOf(task.group) === -1) { + _.each(orderedTasks, function (task) { + if (groups.indexOf(task.group) === -1) { groups.push(task.group); } }); var groupedTasks = []; - _.each(groups, function(group) { + _.each(groups, function (group) { groupedTasks.push({ group: group, - tasks: _.filter(tasks, function(t) { + tasks: _.filter(tasks, function (t) { return t.group === group; }) }); @@ -236,7 +236,7 @@ $scope.groupedTasks = groupedTasks; }; - $scope.showTask = function(taskId) { + $scope.showTask = function (taskId) { $state.go('app.case.tasks-item', { itemId: taskId }); @@ -247,98 +247,98 @@ field[fieldName] = newValue; return CaseTaskSrv.update({ taskId: task._id - }, field, function () {}, function (response) { + }, field, function () { }, function (response) { NotificationSrv.error('taskList', response.data, response.status); }); }; - $scope.addTask = function() { + $scope.addTask = function () { CaseTaskSrv.save({ 'caseId': $scope.caseId, 'flag': false - }, $scope.newTask, function() { + }, $scope.newTask, function () { $scope.isNewTask = false; $scope.newTask.title = ''; $scope.newTask.group = ''; NotificationSrv.success('Task has been successfully added'); - }, function(response) { + }, function (response) { NotificationSrv.error('taskList', response.data, response.status); }); }; - $scope.removeTask = function(task) { + $scope.removeTask = function (task) { ModalUtilsSrv.confirm('Delete task', 'Are you sure you want to delete the selected task?', { okText: 'Yes, remove it', flavor: 'danger' - }).then(function() { + }).then(function () { CaseTaskSrv.update({ 'taskId': task._id }, { status: 'Cancel' - }, function() { + }, function () { $scope.$emit('tasks:task-removed', task); NotificationSrv.success('Task has been successfully removed'); - }, function(response) { + }, function (response) { NotificationSrv.error('taskList', response.data, response.status); }); }); }; - $scope.bulkUpdate = function(ids, patch) { + $scope.bulkUpdate = function (ids, patch) { return CaseTaskSrv.bulkUpdate(ids, patch) - .then(function(/*responses*/) { + .then(function (/*responses*/) { NotificationSrv.log('Selected tasks have been updated successfully', 'success'); }) - .catch(function(err) { + .catch(function (err) { NotificationSrv.error('Bulk update tasks', err.data, err.status); }); } - $scope.bulkFlag = function(flag) { + $scope.bulkFlag = function (flag) { var ids = _.pluck($scope.selection, '_id'); - return $scope.bulkUpdate(ids, {flag: flag}); + return $scope.bulkUpdate(ids, { flag: flag }); } - $scope.bulkStatus = function(status) { + $scope.bulkStatus = function (status) { var ids = _.pluck($scope.selection, '_id'); - return $scope.bulkUpdate(ids, {status: status}); + return $scope.bulkUpdate(ids, { status: status }); } - $scope.bulkRemove = function() { + $scope.bulkRemove = function () { var ids = _.pluck($scope.selection, '_id'); ModalUtilsSrv.confirm('Delete selected tasks', 'Are you sure you want to delete the selected tasks?', { okText: 'Yes, proceed', flavor: 'danger' - }).then(function() { - return CaseTaskSrv.bulkUpdate(ids, {status: 'Cancel'}) - .then(function(/*responses*/) { + }).then(function () { + return CaseTaskSrv.bulkUpdate(ids, { status: 'Cancel' }) + .then(function (/*responses*/) { NotificationSrv.log('Selected tasks have been successfully removed', 'success'); - _.each($scope.selection, function(task) { + _.each($scope.selection, function (task) { $scope.$emit('tasks:task-removed', task); }); }) - .catch(function(err) { + .catch(function (err) { NotificationSrv.error('Bulk remove tasks', err.data, err.status); }); - }).catch(function(err) { - if(err && !_.isString(err)) { + }).catch(function (err) { + if (err && !_.isString(err)) { NotificationSrv.error('Bulk remove tasks', err.data, err.status); } }) } // open task tab with its details - $scope.startTask = function(task) { + $scope.startTask = function (task) { var taskId = task._id; if (task.status === 'Waiting') { $scope.updateTaskStatus(taskId, 'InProgress') - .then(function(/*response*/) { + .then(function (/*response*/) { $scope.showTask(taskId); }); } else { @@ -346,34 +346,34 @@ } }; - $scope.openTask = function(task) { + $scope.openTask = function (task) { if (task.status === 'Completed') { $scope.updateTaskStatus(task._id, 'InProgress') - .then(function(/*response*/) { + .then(function (/*response*/) { $scope.showTask(task._id); }); } }; - $scope.closeTask = function(task) { + $scope.closeTask = function (task) { if (task.status === 'InProgress') { $scope.updateTaskStatus(task._id, 'Completed') - .then(function() { + .then(function () { NotificationSrv.success('Task has been successfully closed'); }); } }; - $scope.updateTaskStatus = function(taskId, status) { + $scope.updateTaskStatus = function (taskId, status) { var defer = $q.defer(); CaseTaskSrv.update({ 'taskId': taskId }, { 'status': status - }, function(data) { + }, function (data) { defer.resolve(data); - }, function(response) { + }, function (response) { NotificationSrv.error('taskList', response.data, response.status); defer.reject(response); }); @@ -381,32 +381,32 @@ return defer.promise; }; - $scope.getTaskResponders = function(task, force) { - if(!force && $scope.taskResponders !== null) { - return; + $scope.getTaskResponders = function (task, force) { + if (!force && $scope.taskResponders !== null) { + return; } $scope.taskResponders = null; CortexSrv.getResponders('case_task', task._id) - .then(function(responders) { - $scope.taskResponders = responders; - return CortexSrv.promntForResponder(responders); - }) - .then(function(response) { - if(response && _.isString(response)) { - NotificationSrv.log(response, 'warning'); - } else { - return CortexSrv.runResponder(response.id, response.name, 'case_task', _.pick(task, '_id')); - } - }) - .then(function(response){ - NotificationSrv.success(['Responder', response.data.responderName, 'started successfully on task', task.title].join(' ')); - }) - .catch(function(err) { - if(err && !_.isString(err)) { - NotificationSrv.error('taskList', err.data, err.status); - } - }); + .then(function (responders) { + $scope.taskResponders = responders; + return CortexSrv.promntForResponder(responders); + }) + .then(function (response) { + if (response && _.isString(response)) { + NotificationSrv.log(response, 'warning'); + } else { + return CortexSrv.runResponder(response.id, response.name, 'case_task', _.pick(task, '_id')); + } + }) + .then(function (response) { + NotificationSrv.success(['Responder', response.data.responderName, 'started successfully on task', task.title].join(' ')); + }) + .catch(function (err) { + if (err && !_.isString(err)) { + NotificationSrv.error('taskList', err.data, err.status); + } + }); }; // $scope.runResponder = function(responderId, responderName, task) { diff --git a/frontend/app/scripts/directives/tag-colour.js b/frontend/app/scripts/directives/tag-colour.js index c6fb358884..e17d9051e8 100644 --- a/frontend/app/scripts/directives/tag-colour.js +++ b/frontend/app/scripts/directives/tag-colour.js @@ -1,19 +1,22 @@ -(function() { +(function () { 'use strict'; - angular.module('theHiveDirectives').directive('tagColour', function($timeout, TaxonomyCacheSrv) { + angular.module('theHiveDirectives').directive('tagColour', function ($timeout, TaxonomyCacheSrv, TagSrv) { return { restrict: 'A', scope: { tag: '=' }, - link: function(scope, element/*, attrs*/) { - if(!scope.tag) { + link: function (scope, element/*, attrs*/) { + if (!scope.tag) { return; } - scope.bgColour = TaxonomyCacheSrv.getColour(scope.tag) || TaxonomyCacheSrv.getColour('_freetags_:' + scope.tag) || '#3c8dbc'; + scope.bgColour = TaxonomyCacheSrv.getColour(scope.tag) || + TaxonomyCacheSrv.getColour('_freetags_:' + scope.tag) || + TagSrv.tagsDefaultColour || + '#000000'; - $timeout(function() { + $timeout(function () { angular.element(element[0]).attr('style', 'background-color:' + scope.bgColour); }); } diff --git a/frontend/app/scripts/directives/updatableUser.js b/frontend/app/scripts/directives/updatableUser.js index 1722831dd6..40a5e4c258 100644 --- a/frontend/app/scripts/directives/updatableUser.js +++ b/frontend/app/scripts/directives/updatableUser.js @@ -1,25 +1,25 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveDirectives') - .directive('updatableUser', function(UserSrv, QuerySrv, UtilsSrv, AuthenticationSrv, NotificationSrv) { + .directive('updatableUser', function (UserSrv, QuerySrv, UtilsSrv, AuthenticationSrv, NotificationSrv) { return { restrict: 'E', - link: function(scope, element, attrs, ctrl, transclude) { + link: function (scope, element, attrs, ctrl, transclude) { var cached = false; UtilsSrv.updatableLink(scope, element, attrs, ctrl, transclude); - scope.setValue = function(value) { + scope.setValue = function (value) { scope.value = value; }; scope.getUserInfo = UserSrv.getCache; - scope.$watch('updatable.updating', function(value) { + scope.$watch('updatable.updating', function (value) { - if(value === true && !cached) { + if (value === true && !cached) { var assignableUsers = []; - if(_.isFunction(scope.query)) { + if (_.isFunction(scope.query)) { assignableUsers = scope.query.apply(this, scope.queryParams); } else { assignableUsers = scope.query; @@ -32,12 +32,12 @@ }, sort: ['+name'] }) - .then(function(users) { - scope.userList = users; - }) - .catch(function(err) { - NotificationSrv.error('Fetching users', err.data, err.status); - }); + .then(function (users) { + scope.userList = users; + }) + .catch(function (err) { + NotificationSrv.error('Fetching users', err.data, err.status); + }); cached = true; } @@ -47,6 +47,7 @@ scope: { value: '=?', query: '=', + blankText: '@', queryParams: '=', onUpdate: '&', active: '=?', diff --git a/frontend/app/scripts/filters/limited-count.js b/frontend/app/scripts/filters/limited-count.js new file mode 100644 index 0000000000..1959e9f48d --- /dev/null +++ b/frontend/app/scripts/filters/limited-count.js @@ -0,0 +1,16 @@ +(function () { + 'use strict'; + + angular.module('theHiveFilters') + .filter('limitedCount', function () { + return function (count) { + if (isNaN(count)) + return 0 + + if (count < 0) + return (-1 * count) + '+'; + + return count; + }; + }); +})(); diff --git a/frontend/app/scripts/services/api/AlertingSrv.js b/frontend/app/scripts/services/api/AlertingSrv.js index 7bea51dfe3..ed28e2662f 100644 --- a/frontend/app/scripts/services/api/AlertingSrv.js +++ b/frontend/app/scripts/services/api/AlertingSrv.js @@ -1,7 +1,7 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveServices') - .factory('AlertingSrv', function($q, $http, $rootScope, StatSrv, StreamSrv, PSearchSrv, PaginatedQuerySrv) { + .factory('AlertingSrv', function ($q, $http, $rootScope, StatSrv, StreamSrv, PSearchSrv, PaginatedQuerySrv) { var baseUrl = './api/alert'; @@ -123,13 +123,13 @@ }; var factory = { - getSimilarityFilters: function() { + getSimilarityFilters: function () { return similarityFilters; }, - getSimilarityFilter: function(name) { + getSimilarityFilter: function (name) { return (similarityFilters[name] || {}).filters; }, - list: function(config, callback) { + list: function (config, callback) { return new PaginatedQuerySrv({ name: 'alerts', root: undefined, @@ -138,67 +138,68 @@ scope: config.scope, sort: config.sort || ['-date'], loadAll: config.loadAll || false, - pageSize: config.pageSize || 10, - filter: config.filter || undefined, + pageSize: config.pageSize || 10, + filter: config.filter || undefined, onUpdate: callback || undefined, + limitedCount: config.limitedCount || false, operations: [ - {'_name': 'listAlert'} + { '_name': 'listAlert' } ], extraData: ['importDate'] }); }, - get: function(alertId) { + get: function (alertId) { return $http.get('./api/v1/alert/' + alertId) - .then(function(response) { + .then(function (response) { return response.data; }); }, - create: function(alertId, data) { + create: function (alertId, data) { return $http.post(baseUrl + '/' + alertId + '/createCase', data || {}); }, - update: function(alertId, updates) { + update: function (alertId, updates) { return $http.patch(baseUrl + '/' + alertId, updates); }, - mergeInto: function(alertId, caseId) { + mergeInto: function (alertId, caseId) { return $http.post(baseUrl + '/' + alertId + '/merge/' + caseId); }, - bulkMergeInto: function(alertIds, caseId) { + bulkMergeInto: function (alertIds, caseId) { return $http.post(baseUrl + '/merge/_bulk', { caseId: caseId, alertIds: alertIds }); }, - canMarkAsRead: function(event) { + canMarkAsRead: function (event) { return !!!event.read; }, - canMarkAsUnread: function(event) { + canMarkAsUnread: function (event) { return !!event.read; }, - markAsRead: function(alertId) { + markAsRead: function (alertId) { return $http.post(baseUrl + '/' + alertId + '/markAsRead'); }, - markAsUnread: function(alertId) { + markAsUnread: function (alertId) { return $http.post(baseUrl + '/' + alertId + '/markAsUnread'); }, - follow: function(alertId) { + follow: function (alertId) { return $http.post(baseUrl + '/' + alertId + '/follow'); }, - unfollow: function(alertId) { + unfollow: function (alertId) { return $http.post(baseUrl + '/' + alertId + '/unfollow'); }, - forceRemove: function(alertId) { + forceRemove: function (alertId) { return $http.delete(baseUrl + '/' + alertId, { params: { force: 1 @@ -206,7 +207,7 @@ }); }, - bulkRemove: function(alertIds) { + bulkRemove: function (alertIds) { return $http.post(baseUrl + '/delete/_bulk', { ids: alertIds }, { @@ -216,7 +217,7 @@ }); }, - stats: function(scope) { + stats: function (scope) { var field = 'status', result = {}, statConfig = { @@ -230,7 +231,7 @@ rootId: 'any', objectType: 'alert', scope: scope, - callback: function() { + callback: function () { StatSrv.get(statConfig); } }); diff --git a/frontend/app/scripts/services/api/TagSrv.js b/frontend/app/scripts/services/api/TagSrv.js index ff9914a730..abf893ae77 100644 --- a/frontend/app/scripts/services/api/TagSrv.js +++ b/frontend/app/scripts/services/api/TagSrv.js @@ -17,12 +17,11 @@ self.tagsDefaultColour = defaultColour; return QuerySrv.query('v1', [ - { _name: 'listTag' }, { _name: 'freetags' }, - { _name: 'filter', _not: { colour: defaultColour } } + { _name: 'filter', _ne: { '_field': 'colour', '_value': defaultColour } } ], { params: { - name: 'list-tags' + name: 'freetags-cache' } }) }) diff --git a/frontend/app/scripts/services/api/TaxonomyCacheSrv.js b/frontend/app/scripts/services/api/TaxonomyCacheSrv.js index cfae25df92..b7d861be36 100644 --- a/frontend/app/scripts/services/api/TaxonomyCacheSrv.js +++ b/frontend/app/scripts/services/api/TaxonomyCacheSrv.js @@ -44,6 +44,13 @@ }); }; + this.refreshFreeTags = function () { + return TagSrv.getFreeTags() + .then(function (freeTags) { + self.cacheTagColors(freeTags); + }); + } + this.all = function (reload) { var deferred = $q.defer(); diff --git a/frontend/app/scripts/services/common/QuerySrv.js b/frontend/app/scripts/services/common/QuerySrv.js index fbd83b143f..ddbc36600b 100644 --- a/frontend/app/scripts/services/common/QuerySrv.js +++ b/frontend/app/scripts/services/common/QuerySrv.js @@ -1,17 +1,17 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveServices') - .service('QuerySrv', function($http, $q) { + .service('QuerySrv', function ($http, $q) { var self = this; - this._buildSort = function(sort) { + this._buildSort = function (sort) { var input = _.isArray(sort) ? sort : [sort]; - return _.map(input, function(item) { + return _.map(input, function (item) { var ret = {}; - if(item.startsWith('-')) { + if (item.startsWith('-')) { ret[item.slice(1)] = 'desc'; } else if (item.startsWith('+')) { ret[item.slice(1)] = 'asc'; @@ -21,7 +21,7 @@ }); }; - this.query = function(version, operations, config) { + this.query = function (version, operations, config) { return $http.post('./api/' + version + '/query', { query: operations }, config || {}); @@ -36,43 +36,43 @@ * @param {type} options Options object defining filters, sort, pagination and addition $http options * @return {type} Promise resolving to the list of the searched objects */ - this.call = function(version, selectorOperation, options) { + this.call = function (version, selectorOperation, options) { var operations = [].concat(selectorOperation); var config = {}; // Apply filter is defined if (options && options.filter && !_.isEmpty(options.filter)) { operations.push( - _.extend({'_name': 'filter'}, options.filter) + _.extend({ '_name': 'filter' }, options.filter) ); } // Apply sort is defined if (options && options.sort) { operations.push( - _.extend({'_name': 'sort'}, {'_fields': this._buildSort(options.sort)}) + _.extend({ '_name': 'sort' }, { '_fields': this._buildSort(options.sort) }) ); } // Apply pagination if isDefined - if(options && options.page) { + if (options && options.page) { operations.push( - _.extend({'_name': 'page'}, options.page) + _.extend({ '_name': 'page' }, options.page) ); } - if(options && options.name) { + if (options && options.name) { config.params = { name: options.name }; } - if(options && options.config) { + if (options && options.config) { config = _.extend({}, config, options.config); } return self.query(version, operations, config) - .then(function(response) { + .then(function (response) { return $q.resolve(response.data); }); }; @@ -86,32 +86,32 @@ * @param {type} options Options object defining filters and addition $http options * @return {type} Promise resolving to the total of the searched objects */ - this.count = function(version, selectorOperation, options) { + this.count = function (version, selectorOperation, options) { var operations = [].concat(selectorOperation); var config = {}; // Apply filter is defined if (options && options.filter && !_.isEmpty(options.filter)) { operations.push( - _.extend({'_name': 'filter'}, options.filter) + _.extend({ '_name': 'filter' }, options.filter) ); } - if(options && options.name) { + if (options && options.name) { config.params = { name: options.name + '.count' }; } - if(options && options.config) { + if (options && options.config) { config = _.extend({}, config, options.config); } - // Add filters - operations.push({'_name': 'count'}); + // Add count + operations.push({ '_name': options.limitedCount ? 'limitedCount' : 'count' }); return self.query(version, operations, config) - .then(function(response) { + .then(function (response) { return $q.resolve(response.data); }); }; diff --git a/frontend/app/scripts/services/common/UtilsSrv.js b/frontend/app/scripts/services/common/UtilsSrv.js index e63a89ee41..7365af6718 100644 --- a/frontend/app/scripts/services/common/UtilsSrv.js +++ b/frontend/app/scripts/services/common/UtilsSrv.js @@ -1,11 +1,11 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveServices') - .factory('UtilsSrv', function($location) { + .factory('UtilsSrv', function ($location) { var sensitiveTypes = ['url', 'ip', 'mail', 'domain', 'filename']; var service = { - guid: function() { + guid: function () { function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) @@ -14,15 +14,15 @@ return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); }, - objectify: function(arr, property) { - return _.map(arr, function(str) { + objectify: function (arr, property) { + return _.map(arr, function (str) { var obj = {}; obj[property] = str; return obj; }); }, - fangValue: function(value) { + fangValue: function (value) { return value .replace(/\[\.\]/g, ".") .replace(/hxxp/gi, "http") @@ -30,7 +30,7 @@ .replace(/http/gi, "hxxp"); }, - fang: function(observable) { + fang: function (observable) { if (sensitiveTypes.indexOf(observable.dataType) === -1) { return observable.data; } @@ -38,16 +38,16 @@ return service.fangValue(observable.data); }, - unfang: function(observable) { + unfang: function (observable) { return observable.data .replace(/\[\.\]/g, ".") .replace(/hxxp/gi, "http"); }, - shallowClearAndCopy: function(src, dst) { + shallowClearAndCopy: function (src, dst) { dst = dst || {}; - angular.forEach(dst, function(value, key) { + angular.forEach(dst, function (value, key) { delete dst[key]; }); @@ -59,7 +59,7 @@ return dst; }, - updatableLink: function(scope, element, attrs) { + updatableLink: function (scope, element, attrs) { scope.updatable = { 'updating': false }; @@ -67,10 +67,10 @@ if (!angular.isDefined(scope.active)) { scope.active = false; } - scope.edit = function() { + scope.edit = function () { scope.updatable.updating = true; }; - scope.update = function(newValue) { + scope.update = function (newValue) { if (angular.isDefined(newValue)) { scope.value = newValue; } @@ -82,11 +82,11 @@ updateResult = updateResult.$promise; } if (angular.isObject(updateResult) && angular.isFunction(updateResult.then)) { - updateResult.then(function() { + updateResult.then(function () { scope.oldValue = scope.value; scope.active = false; scope.format = 'static'; - }, function() { + }, function () { scope.value = scope.oldValue; }); } else { @@ -97,16 +97,16 @@ } scope.updatable.updating = false; }; - scope.cancel = function() { + scope.cancel = function () { scope.value = scope.oldValue; scope.updatable.updating = false; }; - scope.clear = function() { + scope.clear = function () { scope.value = null; }; }, - extractQueryParam: function(paramName, queryString) { + extractQueryParam: function (paramName, queryString) { if (!queryString || !paramName) { return; } @@ -116,17 +116,17 @@ if (param) { return param; } else { - var parsedQuery = _.find(queryString.split('&'), function(str) { + var parsedQuery = _.find(queryString.split('&'), function (str) { return str.startsWith(paramName + '='); }); return parsedQuery ? parsedQuery.substr(paramName.length + 1) : undefined; } }, - getDateRange: function(operator) { + getDateRange: function (operator) { var from; - switch(operator) { + switch (operator) { case 'last7days': from = moment().subtract(7, 'days'); break; @@ -154,6 +154,15 @@ from: from.hour(0).minutes(0).seconds(0).toDate(), to: to.hour(23).minutes(59).seconds(59).toDate() }; + }, + + hasAddDeleteEvents: function (updates) { + if (!updates || updates.length === 0) + return; + + return _.without(_.map(updates, function (u) { + return u.base.operation; + }), 'Update').length > 0; } }; diff --git a/frontend/app/scripts/services/common/data/PaginatedQuerySrv.js b/frontend/app/scripts/services/common/data/PaginatedQuerySrv.js index 25cce42f48..0a863d8fe5 100644 --- a/frontend/app/scripts/services/common/data/PaginatedQuerySrv.js +++ b/frontend/app/scripts/services/common/data/PaginatedQuerySrv.js @@ -1,9 +1,9 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveServices') - .service('PaginatedQuerySrv', function($filter, StreamSrv, QuerySrv) { + .service('PaginatedQuerySrv', function ($filter, StreamSrv, QuerySrv) { - return function(options) { + return function (options) { var self = this; // Internal fields @@ -22,6 +22,7 @@ this.loadAll = !!options.loadAll; this.pageSize = options.pageSize || 10; this.pageOptions = options.pageOptions || {}; + this.limitedCount = options.limitedCount || false; this.baseFilter = options.baseFilter; this.filter = options.filter; this.sort = options.sort; @@ -34,6 +35,8 @@ this.extraData = options.extraData || undefined; this.name = options.name || undefined; this.config = options.config || {}; + this.loading = false; + this.loadingCount = false; this.operations = options.operations; @@ -43,8 +46,8 @@ scope: self.scope, rootId: self.root, objectType: self.streamObjectType, - callback: function(updates) { - if(!self.guard || self.guard(updates)) { + callback: function (updates) { + if (!self.guard || self.guard(updates)) { self.update(updates, true); } } @@ -56,12 +59,12 @@ /* Function to change the page */ - this.changePage = function() { + this.changePage = function () { if (this.loadAll) { this.values.length = 0; var end = this.currentPage * this.pageSize; var start = end - this.pageSize; - angular.forEach(this.allValues.slice(start, end), function(d) { + angular.forEach(this.allValues.slice(start, end), function (d) { self.values.push(d); }); @@ -76,7 +79,7 @@ /* Function to compute the range of the page */ - this.getPage = function() { + this.getPage = function () { if (this.loadAll) { return; } @@ -90,7 +93,7 @@ from: from, to: to }, - self.extraData ? {extraData: self.extraData} : {}, + self.extraData ? { extraData: self.extraData } : {}, self.pageOptions ? self.pageOptions : {} ); }; @@ -101,20 +104,20 @@ * * @return {type} filters definition */ - this.getFilter = function() { - if(!this.baseFilter && !this.filter) { + this.getFilter = function () { + if (!this.baseFilter && !this.filter) { return; } - var predicates = _.filter([this.baseFilter, this.filter], function(item) { + var predicates = _.filter([this.baseFilter, this.filter], function (item) { return !_.isEmpty(item); }); - if(predicates.length === 0) { + if (predicates.length === 0) { return []; } - return predicates.length === 1 ? predicates[0] : {'_and': predicates}; + return predicates.length === 1 ? predicates[0] : { '_and': predicates }; }; /** @@ -122,17 +125,18 @@ * * @return {type} sort definition */ - this.getSort = function() { + this.getSort = function () { return self.sort; }; /* Function to change the page */ - this.update = function(updates, forceCount) { + this.update = function (updates, forceCount) { var filters = self.getFilter(); // Get the list + this.loading = true; QuerySrv.call(this.version, this.operations, { filter: filters, sort: self.getSort(), @@ -140,37 +144,46 @@ config: self.config, withParent: false, name: self.name - }).then(function(data) { - if (self.loadAll) { - self.allValues = data; - - self.total = data.length; - - self.changePage(); - } else { - self.values = data; - if (angular.isFunction(self.onUpdate)) { - self.onUpdate(updates); + }) + .then(function (data) { + if (self.loadAll) { + self.allValues = data; + + self.total = data.length; + + self.changePage(); + } else { + self.values = data; + if (angular.isFunction(self.onUpdate)) { + self.onUpdate(updates); + } } - } - }).catch(function(err) { - if(self.onFailure) { - self.onFailure(err); - } - }); + }) + .catch(function (err) { + if (self.onFailure) { + self.onFailure(err); + } + }) + .finally(function () { + self.loading = false; + }); // get the total if not cached var hash = $filter('md5')(JSON.stringify(this.filter)); - if(forceCount || (!!!this.loadAll && this.filterHash !== hash)) { + if (forceCount || (!!!this.loadAll && this.filterHash !== hash)) { this.filterHash = hash; // Compute the total again + self.loadingCount = true; QuerySrv.count('v1', this.operations, { filter: filters, name: self.name, config: self.config, - }).then(function(total) { + limitedCount: self.limitedCount + }).then(function (total) { self.total = total; + }).finally(function () { + self.loadingCount = false; }); } diff --git a/frontend/app/scripts/services/common/data/StreamQuerySrv.js b/frontend/app/scripts/services/common/data/StreamQuerySrv.js index 7f08c58efd..174015bc95 100644 --- a/frontend/app/scripts/services/common/data/StreamQuerySrv.js +++ b/frontend/app/scripts/services/common/data/StreamQuerySrv.js @@ -1,22 +1,24 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveServices') - .factory('StreamQuerySrv', function($http, StreamSrv, QuerySrv) { - return function(version, operations, config) { + .factory('StreamQuerySrv', function ($http, StreamSrv, QuerySrv) { + return function (version, operations, config) { StreamSrv.addListener({ + scope: config.scope, rootId: config.rootId, objectType: config.objectType, - scope: config.scope, - callback:function() { - QuerySrv.query(version, operations, config.query) - .then(function(response) { - config.onUpdate(response.data); - }); + callback: function (updates) { + if (!config.guard || config.guard(updates)) { + QuerySrv.query(version, operations, config.query) + .then(function (response) { + config.onUpdate(response.data); + }); + } } }); QuerySrv.query(version, operations, config.query) - .then(function(response) { + .then(function (response) { config.onUpdate(response.data); }); }; diff --git a/frontend/app/scripts/services/common/data/StreamSrv.js b/frontend/app/scripts/services/common/data/StreamSrv.js index 3382b37844..c1f8d9e2eb 100644 --- a/frontend/app/scripts/services/common/data/StreamSrv.js +++ b/frontend/app/scripts/services/common/data/StreamSrv.js @@ -1,6 +1,6 @@ -(function() { +(function () { 'use strict'; - angular.module('theHiveServices').factory('StreamSrv', function($q, $rootScope, $http, $timeout, UserSrv, AuthenticationSrv, AfkSrv, NotificationSrv) { + angular.module('theHiveServices').factory('StreamSrv', function ($q, $rootScope, $http, $timeout, UserSrv, AuthenticationSrv, AfkSrv, NotificationSrv, VersionSrv) { var self = { isPolling: false, @@ -8,18 +8,18 @@ httpRequestCanceller: $q.defer(), disabled: true, - init: function() { + init: function () { self.streamId = null; self.disabled = false; self.requestStream(); }, - runCallbacks: function(id, objectType, message) { + runCallbacks: function (id, objectType, message) { $rootScope.$broadcast('stream:' + id + '-' + objectType, message); }, - handleStreamResponse: function(data) { - if(!data || data.length === 0) { + handleStreamResponse: function (data) { + if (!data || data.length === 0) { return; } @@ -28,7 +28,7 @@ var byRootIdsWithObjectTypes = {}; var bySecondaryObjectTypes = {}; - angular.forEach(data, function(message) { + angular.forEach(data, function (message) { var rootId = message.base.rootId; var objectType = message.base.objectType; var rootIdWithObjectType = rootId + '|' + objectType; @@ -52,7 +52,7 @@ byRootIdsWithObjectTypes[rootIdWithObjectType] = [message]; } - _.each(secondaryObjectTypes, function(type) { + _.each(secondaryObjectTypes, function (type) { if (type in bySecondaryObjectTypes) { bySecondaryObjectTypes[type].push(message); } else { @@ -62,19 +62,19 @@ }); - angular.forEach(byRootIds, function(messages, rootId) { + angular.forEach(byRootIds, function (messages, rootId) { self.runCallbacks(rootId, 'any', messages); }); - angular.forEach(byObjectTypes, function(messages, objectType) { + angular.forEach(byObjectTypes, function (messages, objectType) { self.runCallbacks('any', objectType, messages); }); // Trigger strem event for sub object types - angular.forEach(bySecondaryObjectTypes, function(messages, objectType) { + angular.forEach(bySecondaryObjectTypes, function (messages, objectType) { self.runCallbacks('any', objectType, messages); }); - angular.forEach(byRootIdsWithObjectTypes, function(messages, rootIdWithObjectType) { + angular.forEach(byRootIdsWithObjectTypes, function (messages, rootIdWithObjectType) { var temp = rootIdWithObjectType.split('|', 2), rootId = temp[0], objectType = temp[1]; @@ -85,15 +85,15 @@ self.runCallbacks('any', 'any', data); }, - cancelPoll: function() { - if(self.httpRequestCanceller) { + cancelPoll: function () { + if (self.httpRequestCanceller) { self.httpRequestCanceller.resolve('cancel'); } self.disabled = true; }, - poll: function() { + poll: function () { // Skip polling is a poll is already running if (self.streamId === null || self.isPolling === true) { return; @@ -108,7 +108,7 @@ // Poll stream changes self.pollPromise = $http.get('./api/stream/' + self.streamId, { timeout: self.httpRequestCanceller.promise - }).then(function(res) { + }).then(function (res) { // Flag polling end self.isPolling = false; @@ -117,25 +117,35 @@ // Check if the session will expire soon if (res.status === 220) { - AfkSrv.prompt().then(function() { + AfkSrv.prompt().then(function () { UserSrv.getUserInfo(AuthenticationSrv.currentUser.login) - .then(function() { + .then(function () { - }, function(response) { + }, function (response) { NotificationSrv.error('StreamSrv', response.data, response.status); }); }); } - $timeout(function() { - self.poll(); - }, 0); + VersionSrv.get().then(function (appConfig) { + var pollingDuration; + try { + pollingDuration = appConfig.config.pollingDuration + } catch (error) { + pollingDuration = 0 + } + + $timeout(function () { + self.poll(); + }, pollingDuration); + }) + - }).catch(function(err) { + }).catch(function (err) { // Initialize the stream; self.isPolling = false; - if(err && err.xhrStatus === 'abort') { + if (err && err.xhrStatus === 'abort') { return; } @@ -152,17 +162,17 @@ }, - requestStream: function() { + requestStream: function () { if (self.streamId !== null) { return; } - $http.post('./api/stream').then(function(response) { + $http.post('./api/stream').then(function (response) { var streamId = response.data; self.streamId = streamId; self.poll(self.streamId); - }).catch(function(err) { + }).catch(function (err) { NotificationSrv.error('StreamSrv', err.data, err.status); }); }, @@ -174,16 +184,16 @@ *
  • scope {Object}
  • *
  • callback {Function}
  • */ - addListener: function(config) { - if(!config.scope) { + addListener: function (config) { + if (!config.scope) { console.error('No scope provided, use the old listen method', config); self.listen(config.rootId, config.objectType, config.callback); return; } var eventName = 'stream:' + config.rootId + '-' + config.objectType; - config.scope.$on(eventName, function(event, data) { - if(!self.disabled) { + config.scope.$on(eventName, function (event, data) { + if (!self.disabled) { config.callback(data); } }); diff --git a/frontend/app/views/app.case.html b/frontend/app/views/app.case.html index e548b9ce1f..cd6114141b 100644 --- a/frontend/app/views/app.case.html +++ b/frontend/app/views/app.case.html @@ -20,21 +20,22 @@    Observables   - {{observableCount}} + {{observablesCount}}    TTPs   - -   {{tab.label | limitTo:25}}   +   {{tab.label | + limitTo:25}}   -   {{tab.label | fang | limitTo:25}}   + +   {{tab.label | fang | limitTo:25}}     {{tab.label | limitTo:25}}   @@ -42,7 +43,8 @@
    - + diff --git a/frontend/app/views/components/alert/observable-list.component.html b/frontend/app/views/components/alert/observable-list.component.html index 131bca24ed..e6bea41a0e 100644 --- a/frontend/app/views/components/alert/observable-list.component.html +++ b/frontend/app/views/components/alert/observable-list.component.html @@ -1,25 +1,25 @@ -
    +
    -
    +
    -
    -
    +
    No records
    -
    +
    @@ -34,28 +34,36 @@ diff --git a/frontend/app/views/components/alert/observables/toolbar.html b/frontend/app/views/components/alert/observables/toolbar.html index e83ac7f7a4..6b317e046a 100644 --- a/frontend/app/views/components/alert/observables/toolbar.html +++ b/frontend/app/views/components/alert/observables/toolbar.html @@ -1,13 +1,22 @@
    - + - {{observable.dataType}} + {{observable.dataType}} -
    - {{observable.data | fang | ellipsis:250}} -
    -
    - {{observable.attachment.name}} ({{observable.attachment.size}} bytes) -
    -
    - {{observable.remoteAttachment.filename}} -
    -
    - - -
    +
    + {{observable.data | fang | ellipsis:250}} +
    +
    + {{observable.attachment.name}} ({{observable.attachment.size}} bytes) +
    +
    + {{observable.remoteAttachment.filename}} +
    +
    + + +
    - {{observable._createdAt | shortDate}} + {{observable._createdAt + | shortDate}}
    @@ -69,24 +81,30 @@

    - - - - + + + + + + + + + +
    - - Name - - - - + + Name + + + + Colour @@ -48,17 +52,25 @@

    Dates - + C. - - - + + + - + U. - - - + + + Actions
    - + - + + {{tag.extraData.usage.case}}{{tag.extraData.usage.alert}}{{tag.extraData.usage.observable}}{{tag.extraData.usage.caseTemplate}}{{tag.extraData.usage.case | limitedCount}}{{tag.extraData.usage.alert | limitedCount}}{{tag.extraData.usage.observable | limitedCount}}{{tag.extraData.usage.caseTemplate | limitedCount}} - diff --git a/frontend/app/views/directives/psearch.html b/frontend/app/views/directives/psearch.html index 03c0512f66..72fe794a29 100644 --- a/frontend/app/views/directives/psearch.html +++ b/frontend/app/views/directives/psearch.html @@ -1,3 +1,13 @@ -
    -
      -
      +
      +
      +
        +
      +
      + +
      +
        +
        +
        diff --git a/frontend/app/views/directives/updatable-user.html b/frontend/app/views/directives/updatable-user.html index a64a013626..fd9d33a7c6 100644 --- a/frontend/app/views/directives/updatable-user.html +++ b/frontend/app/views/directives/updatable-user.html @@ -1,6 +1,7 @@
        - + {{blankText || 'Not Specified'}} + diff --git a/frontend/app/views/partials/admin/attack/import.html b/frontend/app/views/partials/admin/attack/import.html index 0a8fbc0786..7d3bfcd02f 100644 --- a/frontend/app/views/partials/admin/attack/import.html +++ b/frontend/app/views/partials/admin/attack/import.html @@ -6,7 +6,9 @@

        Download the official MITRE ATT&CK library

        -

        You can download the latest archive of the official MITRE ATT&CK patterns from here

        +

        You can download the latest archive of the official MITRE ATT&CK patterns from + here

        @@ -17,6 +19,7 @@

        Download the official MITRE ATT&CK library

        diff --git a/frontend/app/views/partials/admin/platform/status.html b/frontend/app/views/partials/admin/platform/status.html index e514771def..12b4c23851 100644 --- a/frontend/app/views/partials/admin/platform/status.html +++ b/frontend/app/views/partials/admin/platform/status.html @@ -31,7 +31,8 @@

        Database Schema status

        {{schema.error === null ? 'OK' : 'ERROR'}} + false: 'label-danger'}[schema.error === null]">{{schema.error === null ? 'OK' : + 'ERROR'}}

        @@ -49,37 +50,33 @@

        -

        Data index status Reload

        +

        Data index status Reload

        Loading index status...
        -
        +
        -
        Status
        -
        Index name
        +
        Index: {{index.name | uppercase}}
        # Entities
        -
        +
        -
        +
        -
        - {{data.status | uppercase}} -

        - {{indexName}} + {{item.name}}

        - {{data.mixedCount}} + {{item.count}}
        - +
        @@ -102,7 +99,7 @@

        Trigger + class="fa fa-cog mr-xxxs"> Trigger

        diff --git a/frontend/app/views/partials/admin/taxonomy/import.html b/frontend/app/views/partials/admin/taxonomy/import.html index 634dce0d94..a163857568 100644 --- a/frontend/app/views/partials/admin/taxonomy/import.html +++ b/frontend/app/views/partials/admin/taxonomy/import.html @@ -6,7 +6,9 @@

        Download the official MISP taxonomies archive

        -

        You can download the latest archive of the official MISP Taxonomies from here

        +

        You can download the latest archive of the official MISP Taxonomies from + here

        @@ -15,12 +17,14 @@

        Download the official MISP taxonomies archive

        - The taxonomies archive must be a valid ZIP file containing at least one file names machinetag.json + The taxonomies archive must be a valid ZIP file containing at least one file names + machinetag.json

        diff --git a/frontend/app/views/partials/alert/list.html b/frontend/app/views/partials/alert/list.html index 7282826230..553dc6c736 100644 --- a/frontend/app/views/partials/alert/list.html +++ b/frontend/app/views/partials/alert/list.html @@ -2,54 +2,65 @@
        -

        List of alerts ({{$vm.list.total || 0}} of {{$vm.alertListCount}})

        +

        + +

        -
        +
        -
        +
        -
        -
        + +
        No records
        -
        +
        @@ -115,7 +144,8 @@

        List of alerts ({{$vm.list.total || 0}} of {{$vm.alertList {{::event.sourceRef}} - + @@ -123,68 +153,98 @@

        List of alerts ({{$vm.list.total || 0}} of {{$vm.alertList

        - + diff --git a/frontend/app/views/partials/case/case.list.html b/frontend/app/views/partials/case/case.list.html index 04dc3a3e6e..2c67c8ed7f 100644 --- a/frontend/app/views/partials/case/case.list.html +++ b/frontend/app/views/partials/case/case.list.html @@ -2,7 +2,9 @@
        - + - - Reference - - - - + + Reference + + + + - - Type - - - - + + Type + + + + Imported @@ -58,47 +69,65 @@

        List of alerts ({{$vm.list.total || 0}} of {{$vm.alertList Read

        - Title + Title - - Source - - - - + + Source + + + + - - Severity - - - - + + Severity + + + + - Observables + Observables Dates - + O. - - - + + + - + C. - - - + + + - + U. - - - + + + - {{::event.type}} + {{::event.type}} - {{event.caseId ? 'Imported' : 'New'}} + {{event.caseId + ? + 'Imported' : 'New'}} - {{event.read ? 'Read' : 'Unread'}} + {{event.read ? + 'Read' : + 'Unread'}}
        {{::event.title}} - + {{::event.title}}
        {{event.source}}{{event.source}} + -
        +
        {{::event.observableCount || 0}} - @@ -195,7 +255,8 @@

        List of alerts ({{$vm.list.total || 0}} of {{$vm.alertList

        - None + None
        @@ -205,7 +266,9 @@

        List of alerts ({{$vm.list.total || 0}} of {{$vm.alertList

        - + +
        @@ -40,68 +43,101 @@

        List of cases ({{$vm.list.total || 0}} of {{$vm.caseCount}

        @@ -109,62 +145,81 @@

        List of cases ({{$vm.list.total || 0}} of {{$vm.caseCount}

        - - + + diff --git a/frontend/app/views/partials/case/case.tasks.html b/frontend/app/views/partials/case/case.tasks.html index 5eb8fd57e3..ab13974183 100755 --- a/frontend/app/views/partials/case/case.tasks.html +++ b/frontend/app/views/partials/case/case.tasks.html @@ -1,23 +1,23 @@
        -
        +

        - Tasks List ({{list.values.length || 0}} of {{list.total}}) +

        -
        -
        +
        No task found for this case.
        @@ -28,278 +28,325 @@

        -
        - -
        +
        + +
        -
        - - - - - -
        +
        + + + + + +

        -
        +
        -
        - + - + Status - - - + + + - + # Number - - - + + + - + Title - - - + + + - + Severity - - - + + + Details - + Assignee - - - + + + Dates - + S. - - - + + + - + C. - - - + + + - + U. - - - + + +
        - +
        {{currentCase.status === 'Resolved' ? 'Closed' : currentCase.status}} + }[currentCase.status]" + ng-click="$vm.addFilterValue('status', currentCase.status)">{{currentCase.status + === 'Resolved' ? 'Closed' : currentCase.status}}
        - - + +
        - None - + None + +
        - +
        - (Closed at {{currentCase.endDate | shortDate}} as {{$vm.CaseResolutionStatus[currentCase.resolutionStatus]}}) + (Closed at {{currentCase.endDate | shortDate}} as {{$vm.CaseResolutionStatus[currentCase.resolutionStatus]}})
        +
        -
        +
        - - - - + +
        - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + -
        -
        +
        + + + +
        - - GroupTaskDateAssigneeActions
        - - - - - - - {{task.group}} - - -
        - Closed after {{(task.endDate - task.startDate) | amDurationFormat : 'milliseconds'}} -
        -
        - Started -
        -
        - - - {{task.startDate | shortDate}} - - - - - - - - - - - - Not Assigned - - + +
        + + + + + + + + + + + + + + + + + + + + + + + - + + ({{task.extraData.shareCount || 0}}) + + - - - - -
        + + GroupTaskDateAssigneeActions
        + + + + + + + + + + {{task.group}} + + +
        + Closed after {{(task.endDate - task.startDate) | amDurationFormat : + 'milliseconds'}} +
        +
        + Started +
        +
        +
        -
        -
        +
        {{task.startDate | shortDate}} + + + + + + + + + + + + Not Assigned + + - - - + + + + + + + + + + + + + + + +
        +
        +
        +
        - + +
        +
        +
        +
        +
        -
        diff --git a/frontend/app/views/partials/case/case.tasks.item.html b/frontend/app/views/partials/case/case.tasks.item.html index 6190531c0e..ef8d9ad1ad 100644 --- a/frontend/app/views/partials/case/case.tasks.item.html +++ b/frontend/app/views/partials/case/case.tasks.item.html @@ -6,7 +6,8 @@

        This task requires an action from your organisation. - +
        @@ -55,7 +56,8 @@

        - + | @@ -88,7 +90,7 @@

        Title
        - +
        {{task.title}} @@ -98,7 +100,7 @@

        Group
        - +
        {{task.group}} @@ -108,7 +110,8 @@

        Assignee
        - +
        @@ -120,7 +123,7 @@

        Start date
        - +
        @@ -162,14 +165,15 @@

        -

        Description

        -
        - -
        -
        -
        - Not specified -
        +

        Description

        +
        + +
        +
        +
        + Not specified +
        @@ -206,17 +210,24 @@

        Task logs

        - -
        + +
        - - + - -
        -
        -
        - -
        +

        Task sharing

        + +
        + +
        diff --git a/frontend/app/views/partials/case/case.templates.selector.html b/frontend/app/views/partials/case/case.templates.selector.html index 839661d649..63830c19e7 100644 --- a/frontend/app/views/partials/case/case.templates.selector.html +++ b/frontend/app/views/partials/case/case.templates.selector.html @@ -3,42 +3,42 @@