From 710386a6b0d6fcc797790100ea1bd9ba1d160a4a Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Thu, 25 Mar 2021 06:03:01 +0100 Subject: [PATCH 01/18] Update github issue templates --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ .github/ISSUE_TEMPLATE/thehive3_bug_report.md | 16 +++++++--------- .github/ISSUE_TEMPLATE/thehive4_bug_report.md | 16 +++++++--------- .../ISSUE_TEMPLATE/thehive4_feature_request.md | 11 +---------- 4 files changed, 23 insertions(+), 28 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..6a23db85a9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: TheHive Community Support + url: https://chat.thehive-project.org/ + about: Please ask and answer questions here. + - name: TheHive Documentation + url: https://docs.thehive-project.org/ + about: Please read documentation here. diff --git a/.github/ISSUE_TEMPLATE/thehive3_bug_report.md b/.github/ISSUE_TEMPLATE/thehive3_bug_report.md index 69218ae1c4..f73685e1cc 100644 --- a/.github/ISSUE_TEMPLATE/thehive3_bug_report.md +++ b/.github/ISSUE_TEMPLATE/thehive3_bug_report.md @@ -7,20 +7,18 @@ assignees: '' --- -# EDIT THIS TITLE BEFORE POSTING. Use this template for bug reports. If you'd like to request a feature, please be as descriptive as possible and delete the template except the first section (Request Type) - ### Request Type Bug ### Work Environment -| Question | Answer -|---------------------------|-------------------- -| OS version (server) | Debian, Ubuntu, CentOS, RedHat, ... -| OS version (client) | XP, Seven, 10, Ubuntu, ... -| TheHive version / git hash | 3.x, hash of the commit -| Package Type | RPM, DEB, Docker, Binary, From source -| Browser type & version | If applicable +| Question | Answer | +| -------------------------- | ------------------------------------- | +| OS version (server) | Debian, Ubuntu, CentOS, RedHat, ... | +| OS version (client) | XP, Seven, 10, Ubuntu, ... | +| TheHive version / git hash | 3.x, hash of the commit | +| Package Type | RPM, DEB, Docker, Binary, From source | +| Browser type & version | If applicable | ### Problem Description diff --git a/.github/ISSUE_TEMPLATE/thehive4_bug_report.md b/.github/ISSUE_TEMPLATE/thehive4_bug_report.md index 4c7cbcb330..3e158e3a33 100644 --- a/.github/ISSUE_TEMPLATE/thehive4_bug_report.md +++ b/.github/ISSUE_TEMPLATE/thehive4_bug_report.md @@ -7,20 +7,18 @@ assignees: '' --- -# EDIT THIS TITLE BEFORE POSTING. Use this template for bug reports. If you'd like to request a feature, please be as descriptive as possible and delete the template except the first section (Request Type) - ### Request Type Bug ### Work Environment -| Question | Answer -|---------------------------|-------------------- -| OS version (server) | Debian, Ubuntu, CentOS, RedHat, ... -| OS version (client) | XP, Seven, 10, Ubuntu, ... -| TheHive version / git hash | 4.x, hash of the commit -| Package Type | RPM, DEB, Docker, Binary, From source -| Browser type & version | If applicable +| Question | Answer | +| -------------------------- | ------------------------------------- | +| OS version (server) | Debian, Ubuntu, CentOS, RedHat, ... | +| OS version (client) | XP, Seven, 10, Ubuntu, ... | +| TheHive version / git hash | 4.x, hash of the commit | +| Package Type | RPM, DEB, Docker, Binary, From source | +| Browser type & version | If applicable | ### Problem Description diff --git a/.github/ISSUE_TEMPLATE/thehive4_feature_request.md b/.github/ISSUE_TEMPLATE/thehive4_feature_request.md index 4bf32d9fa1..de898ed8ef 100644 --- a/.github/ISSUE_TEMPLATE/thehive4_feature_request.md +++ b/.github/ISSUE_TEMPLATE/thehive4_feature_request.md @@ -2,22 +2,13 @@ name: Feature Request for TheHive4 about: Create a feature request for TheHive 4. title: "[Bug]" -labels: "feature request", TheHive4 +labels: feature request, TheHive4 assignees: '' --- - -# EDIT THIS TITLE BEFORE POSTING. Use this template for bug reports. If you'd like to request a feature, please be as descriptive as possible and delete the template except the first section (Request Type) - ### Request Type Feature Request -### Work Environment - -| Question | Answer -|---------------------------|-------------------- -| TheHive version | 4.x - ### Feature Description Describe feature as clearly as possible. From 56c37fc75305a4037fe6dacc8cb3b237b6a11b67 Mon Sep 17 00:00:00 2001 From: To-om Date: Thu, 25 Mar 2021 16:56:07 +0100 Subject: [PATCH 02/18] #1875 Fix taskId format in logs --- .../models/TheHiveSchemaDefinition.scala | 37 ++++++++++++++++--- .../controllers/v0/StatusCtrlTest.scala | 2 +- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/thehive/app/org/thp/thehive/models/TheHiveSchemaDefinition.scala b/thehive/app/org/thp/thehive/models/TheHiveSchemaDefinition.scala index 3f1c57023a..1643a79176 100644 --- a/thehive/app/org/thp/thehive/models/TheHiveSchemaDefinition.scala +++ b/thehive/app/org/thp/thehive/models/TheHiveSchemaDefinition.scala @@ -217,7 +217,7 @@ class TheHiveSchemaDefinition @Inject() extends Schema with UpdatableSchema { tags.foreach(vertex.property(Cardinality.list, "tags", _)) vertex.property("organisationId", organisationId.value) - caseId.foreach(vertex.property("caseId", _)) + caseId.foreach(cid => vertex.property("caseId", cid.value)) case _ => } Success(()) @@ -292,12 +292,12 @@ class TheHiveSchemaDefinition @Inject() extends Schema with UpdatableSchema { traversal .project( _.by - .by(_.in("TaskLog")._id) + .by(_.in("TaskLog")._id.option) .by(_.in("TaskLog").in("ShareTask").in("OrganisationShare")._id.fold) ) .foreach { case (vertex, taskId, organisationIds) => - vertex.property("taskId", taskId) + taskId.foreach(tid => vertex.property("taskId", tid.value)) organisationIds.foreach(id => vertex.property(Cardinality.set, "organisationIds", id.value)) } Success(()) @@ -312,7 +312,7 @@ class TheHiveSchemaDefinition @Inject() extends Schema with UpdatableSchema { traversal .project( _.by - .by(_.out("ObservableObservableType").property("name", UMapping.string)) + .by(_.out("ObservableObservableType").property("name", UMapping.string).option) .by(_.out("ObservableTag").valueMap("namespace", "predicate", "value").fold) .by(_.out("ObservableData").property("data", UMapping.string).option) .by(_.out("ObservableAttachment").property("attachmentId", UMapping.string).option) @@ -338,7 +338,7 @@ class TheHiveSchemaDefinition @Inject() extends Schema with UpdatableSchema { (if (predicate.headOption.getOrElse('_') == '_') "" else predicate) + value.fold("")(v => f"""="$v"""") - vertex.property("dataType", dataType) + dataType.foreach(vertex.property("dataType", _)) tags.foreach(vertex.property(Cardinality.list, "tags", _)) data.foreach(vertex.property("data", _)) attachmentId.foreach(vertex.property("attachmentId", _)) @@ -399,6 +399,21 @@ class TheHiveSchemaDefinition @Inject() extends Schema with UpdatableSchema { .removeIndex("Log", IndexType.fulltext, "message") .removeIndex("Tag", IndexType.fulltext, "description") .removeIndex("Task", IndexType.fulltext, "description") +// .updateGraph("Set caseId in imported alerts", "Alert") { traversal => +// traversal +// .project( +// _.by +// .by(_.out("AlertCase")._id.option) +// ) +// .foreach { +// case (vertex, caseId) => +// caseId.foreach(cid => vertex.property("caseId", cid.value)) +// case _ => +// } +// Success(()) +// } + .noop + //=====[release 4.1.1]===== .updateGraph("Set caseId in imported alerts", "Alert") { traversal => traversal .project( @@ -407,7 +422,17 @@ class TheHiveSchemaDefinition @Inject() extends Schema with UpdatableSchema { ) .foreach { case (vertex, caseId) => - caseId.foreach(vertex.property("caseId", _)) + caseId.foreach(cid => vertex.property("caseId", cid.value)) + case _ => + } + Success(()) + } + .updateGraph("Set taskId in logs", "Log") { traversal => + traversal + .project(_.by.by(_.in("TaskLog")._id.option)) + .foreach { + case (vertex, Some(taskId)) => + vertex.property("taskId", taskId.value) case _ => } Success(()) diff --git a/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala b/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala index 1557f0ddeb..5b9c597568 100644 --- a/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala +++ b/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala @@ -70,7 +70,7 @@ class StatusCtrlTest extends PlaySpecification with TestAppBuilder { "pollingDuration" -> 1000 ), "schemaStatus" -> Json.arr( - Json.obj("name" -> "thehive", "currentVersion" -> 67, "expectedVersion" -> 67, "error" -> JsNull) + Json.obj("name" -> "thehive", "currentVersion" -> 69, "expectedVersion" -> 69, "error" -> JsNull) ) ) From c2f8b23c2b2dcf9f782a2e6d4a98483f753d6764 Mon Sep 17 00:00:00 2001 From: To-om Date: Thu, 25 Mar 2021 16:57:09 +0100 Subject: [PATCH 03/18] #1875 Include task parent in logs in search result --- .../scala/org/thp/thehive/dto/v0/Log.scala | 3 ++- .../thehive/controllers/v0/Conversion.scala | 22 +++++++++++++++++++ .../thp/thehive/controllers/v0/LogCtrl.scala | 15 ++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/dto/src/main/scala/org/thp/thehive/dto/v0/Log.scala b/dto/src/main/scala/org/thp/thehive/dto/v0/Log.scala index ec0b7d077a..9e691401d7 100644 --- a/dto/src/main/scala/org/thp/thehive/dto/v0/Log.scala +++ b/dto/src/main/scala/org/thp/thehive/dto/v0/Log.scala @@ -19,7 +19,8 @@ case class OutputLog( startDate: Date, attachment: Option[OutputAttachment] = None, status: String, - owner: String + owner: String, + case_task: Option[OutputTask] ) object OutputLog { diff --git a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala index e1c739654d..c43f3c45fe 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala @@ -327,9 +327,31 @@ object Conversion { .withFieldComputed(_.owner, _._createdBy) .withFieldConst(_.status, "Ok") .withFieldComputed(_.attachment, _.attachments.headOption.map(_.toValue)) + .withFieldConst(_.task, None) .transform ) + implicit val logOutputWithParent: Renderer.Aux[(RichLog, (RichTask, Option[RichCase])), OutputLog] = + Renderer.toJson[(RichLog, (RichTask, Option[RichCase])), OutputLog] { + case (richLog, richTask) => + richLog + .into[OutputLog] + .withFieldConst(_._type, "case_task_log") + .withFieldComputed(_.id, _._id.toString) + .withFieldComputed(_._id, _._id.toString) + .withFieldComputed(_.updatedAt, _._updatedAt) + .withFieldComputed(_.updatedBy, _._updatedBy) + .withFieldComputed(_.createdAt, _._createdAt) + .withFieldComputed(_.createdBy, _._createdBy) + .withFieldComputed(_.message, _.message) + .withFieldComputed(_.startDate, _._createdAt) + .withFieldComputed(_.owner, _._createdBy) + .withFieldConst(_.status, "Ok") + .withFieldComputed(_.attachment, _.attachments.headOption.map(_.toValue)) + .withFieldConst(_.task, Some(richTask.toValue)) + .transform + } + implicit class InputLogOps(inputLog: InputLog) { def toLog: Log = diff --git a/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala index 7f3135ee2d..d15aa34020 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala @@ -9,6 +9,7 @@ import org.thp.scalligraph.traversal.{IteratorOutput, Traversal} import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.dto.v0.InputLog import org.thp.thehive.models.{Log, Permissions, RichLog} +import org.thp.thehive.services.CaseOps._ import org.thp.thehive.services.LogOps._ import org.thp.thehive.services.TaskOps._ import org.thp.thehive.services.{LogSrv, OrganisationSrv, TaskSrv} @@ -82,8 +83,20 @@ class PublicLog @Inject() (logSrv: LogSrv, organisationSrv: OrganisationSrv) ext ) override val pageQuery: ParamQuery[OutputParam] = Query.withParam[OutputParam, Traversal.V[Log], IteratorOutput]( "page", - (range, logSteps, _) => logSteps.richPage(range.from, range.to, withTotal = true)(_.richLog) + { + + case (OutputParam(from, to, _, 0), logSteps, _) => logSteps.richPage(from, to, withTotal = true)(_.richLog) + case (OutputParam(from, to, _, _), logSteps, authContext) => + logSteps.richPage(from, to, withTotal = true)( + _.richLogWithCustomRenderer( + _.task.richTaskWithCustomRenderer( + _.`case`.richCase(authContext).option + ) + ) + ) + } ) + override val outputQuery: Query = Query.output[RichLog, Traversal.V[Log]](_.richLog) override val publicProperties: PublicProperties = PublicPropertyListBuilder[Log] From 2bf9fce1c5a4a7380dd8df23dcfb52ff1ad4fd36 Mon Sep 17 00:00:00 2001 From: To-om Date: Thu, 25 Mar 2021 17:53:43 +0100 Subject: [PATCH 04/18] #1875 Fix compilation error --- thehive/app/org/thp/thehive/controllers/v0/Conversion.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala index c43f3c45fe..0a5bb393ef 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala @@ -327,7 +327,7 @@ object Conversion { .withFieldComputed(_.owner, _._createdBy) .withFieldConst(_.status, "Ok") .withFieldComputed(_.attachment, _.attachments.headOption.map(_.toValue)) - .withFieldConst(_.task, None) + .withFieldConst(_.case_task, None) .transform ) @@ -348,7 +348,7 @@ object Conversion { .withFieldComputed(_.owner, _._createdBy) .withFieldConst(_.status, "Ok") .withFieldComputed(_.attachment, _.attachments.headOption.map(_.toValue)) - .withFieldConst(_.task, Some(richTask.toValue)) + .withFieldConst(_.case_task, Some(richTask.toValue)) .transform } From 5eb03de99285d2a886d67ed0b68eac4e749e708b Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Fri, 26 Mar 2021 06:50:53 +0100 Subject: [PATCH 05/18] #1869 Exclude free tags with default colour from the free tags cache --- frontend/app/scripts/services/api/TagSrv.js | 50 +++++++++++-------- .../thehive/controllers/v0/StatusCtrl.scala | 5 +- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/frontend/app/scripts/services/api/TagSrv.js b/frontend/app/scripts/services/api/TagSrv.js index 83a6057a21..9527d15c92 100644 --- a/frontend/app/scripts/services/api/TagSrv.js +++ b/frontend/app/scripts/services/api/TagSrv.js @@ -1,47 +1,53 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveServices') - .service('TagSrv', function(QuerySrv, $q, $http) { + .service('TagSrv', function (QuerySrv, $q, VersionSrv, $http) { - this.getFreeTags = function() { + this.getFreeTags = function () { var defer = $q.defer(); - var operations = [ - { _name: 'listTag'}, - { _name: 'freetags'}, - ] - - QuerySrv.query('v1', operations, { - params: { - name: 'list-tags' - } - }).then(function(response) { - defer.resolve(response.data); - }); + VersionSrv.get() + .then(function (appConfig) { + var defaultColour = appConfig.config.freeTagDefaultColour; + + return QuerySrv.query('v1', [ + { _name: 'listTag' }, + { _name: 'freetags' }, + { _name: 'filter', _not: { colour: defaultColour } } + ], { + params: { + name: 'list-tags' + } + }) + }) + + .then(function (response) { + defer.resolve(response.data); + }); return defer.promise; }; - this.updateTag = function(id, patch) { + this.updateTag = function (id, patch) { return $http.patch('./api/v1/tag/' + id, patch); } - this.removeTag = function(id) { + this.removeTag = function (id) { return $http.delete('./api/v1/tag/' + id); } - this.autoComplete = function(term) { + this.autoComplete = function (term) { var defer = $q.defer(); var operations = [ - { _name: 'tagAutoComplete', freeTag: term, limit: 20} + { _name: 'tagAutoComplete', freeTag: term, limit: 20 } ] QuerySrv.call('v1', operations, { name: 'tags-auto-complete' - }).then(function(response) { - defer.resolve(_.map(response, function(tag) { - return {text: tag}; + }).then(function (response) { + defer.resolve(_.map(response, function (tag) { + return { text: tag }; })); }); diff --git a/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala index f2d0aa0a6a..28973f563e 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala @@ -34,6 +34,8 @@ class StatusCtrl @Inject() ( appConfig.item[FiniteDuration]("stream.longPolling.pollingDuration", "amount of time the UI have to wait before polling the stream") def streamPollingDuration: FiniteDuration = streamPollingDurationConfig.get + val tagsDefaultColourConfig = appConfig.item[String]("tags.freeTagColour", "Default free tag colour") + private def getVersion(c: Class[_]): String = Option(c.getPackage.getImplementationVersion).getOrElse("SNAPSHOT") def get: Action[AnyContent] = @@ -55,7 +57,8 @@ class StatusCtrl @Inject() ( }), "capabilities" -> authSrv.capabilities.map(c => JsString(c.toString)), "ssoAutoLogin" -> authSrv.capabilities.contains(AuthCapability.sso), - "pollingDuration" -> streamPollingDuration.toMillis + "pollingDuration" -> streamPollingDuration.toMillis, + "freeTagDefaultColour" -> tagsDefaultColourConfig.get ), "schemaStatus" -> schemas.flatMap(_.schemaStatus).map { schemaStatus => Json.obj( From 8779cdcb77a3623f2f6f29ea7ea9c2425c0629a4 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Fri, 26 Mar 2021 07:14:08 +0100 Subject: [PATCH 06/18] #1869 Improve the way default tag colour is taken into account --- frontend/app/scripts/directives/tag-item.js | 26 +++++++----- frontend/app/scripts/services/api/TagSrv.js | 6 +++ .../scripts/services/api/TaxonomyCacheSrv.js | 42 +++++++++---------- .../views/partials/admin/taxonomy/view.html | 7 ++-- .../misc/taxonomy-selection.modal.html | 30 ++++++++----- 5 files changed, 64 insertions(+), 47 deletions(-) diff --git a/frontend/app/scripts/directives/tag-item.js b/frontend/app/scripts/directives/tag-item.js index 0d060ec5c0..20edaca32f 100644 --- a/frontend/app/scripts/directives/tag-item.js +++ b/frontend/app/scripts/directives/tag-item.js @@ -1,6 +1,6 @@ -(function() { +(function () { 'use strict'; - angular.module('theHiveDirectives').directive('tagItem', function(TaxonomyCacheSrv) { + angular.module('theHiveDirectives').directive('tagItem', function (TaxonomyCacheSrv, TagSrv) { return { restrict: 'E', replace: true, @@ -9,15 +9,16 @@ colour: '=' }, templateUrl: 'views/directives/tag-item.html', - link: function(scope/*, element, attrs*/) { - if(!scope.value) { + link: function (scope/*, element, attrs*/) { + if (!scope.value) { return; } - if(_.isString(scope.value)) { + if (_.isString(scope.value)) { scope.tag = scope.value; scope.bgColor = scope.colour || TaxonomyCacheSrv.getColour(scope.value) || TaxonomyCacheSrv.getColour('_freetags_:' + scope.value) || + TagSrv.tagsDefaultColour || '#000000'; } else { scope.tag = _.without([ @@ -26,22 +27,25 @@ scope.value.predicate, scope.value.value ? ("=\"" + scope.value.value + "\"") : null ], null).join(''); - scope.bgColor = scope.value.colour || scope.colour || '#000000'; + scope.bgColor = scope.value.colour || + scope.colour || + TagSrv.tagsDefaultColour || + '#000000'; } - scope.$watch('colour', function(value) { - if(!value) { + scope.$watch('colour', function (value) { + if (!value) { return; } scope.bgColor = value; }); - scope.$watch('value', function(value) { - if(!value) { + scope.$watch('value', function (value) { + if (!value) { return; } - if(_.isString(value)) { + if (_.isString(value)) { scope.tag = value; } else { scope.tag = _.without([ diff --git a/frontend/app/scripts/services/api/TagSrv.js b/frontend/app/scripts/services/api/TagSrv.js index 9527d15c92..ff9914a730 100644 --- a/frontend/app/scripts/services/api/TagSrv.js +++ b/frontend/app/scripts/services/api/TagSrv.js @@ -3,6 +3,10 @@ angular.module('theHiveServices') .service('TagSrv', function (QuerySrv, $q, VersionSrv, $http) { + var self = this; + + this.tagsDefaultColour = '#000000'; + this.getFreeTags = function () { var defer = $q.defer(); @@ -10,6 +14,8 @@ .then(function (appConfig) { var defaultColour = appConfig.config.freeTagDefaultColour; + self.tagsDefaultColour = defaultColour; + return QuerySrv.query('v1', [ { _name: 'listTag' }, { _name: 'freetags' }, diff --git a/frontend/app/scripts/services/api/TaxonomyCacheSrv.js b/frontend/app/scripts/services/api/TaxonomyCacheSrv.js index 9a72d87700..cfae25df92 100644 --- a/frontend/app/scripts/services/api/TaxonomyCacheSrv.js +++ b/frontend/app/scripts/services/api/TaxonomyCacheSrv.js @@ -1,17 +1,17 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveServices') - .service('TaxonomyCacheSrv', function($http, $q, $filter, $uibModal, TagSrv, QuerySrv) { + .service('TaxonomyCacheSrv', function ($http, $q, $filter, $uibModal, VersionSrv, TagSrv, QuerySrv) { var self = this; this.cache = null; this.tagsCache = null; - this.list = function() { + this.list = function () { return QuerySrv.call('v1', [ { _name: 'listTaxonomy' } ], { - name:'list-taxonomies' + name: 'list-taxonomies' }, { name: 'filter', _field: 'enabled', @@ -19,50 +19,50 @@ }); }; - this.clearCache = function() { + this.clearCache = function () { self.cache = null; self.tagsCache = null; }; - this.getCache = function(name) { + this.getCache = function (name) { return self.cache[name]; }; - this.getColour = function(tag) { + this.getColour = function (tag) { return self.tagsCache[tag]; }; - this.cacheTagColors = function(tags) { + this.cacheTagColors = function (tags) { var fn = $filter('tagValue'); - _.each(tags, function(tag) { + _.each(tags, function (tag) { var name = fn(tag); - if(!_.isEmpty(name)) { - self.tagsCache[name] = tag.colour; + if (!_.isEmpty(name)) { + self.tagsCache[name] = tag.colour; } }); }; - this.all = function(reload) { + this.all = function (reload) { var deferred = $q.defer(); if (self.cache === null || reload === true) { self.list() - .then(function(response) { + .then(function (response) { self.cache = {}; self.tagsCache = {}; - _.each(response, function(taxonomy) { + _.each(response, function (taxonomy) { self.cache[taxonomy.namespace] = taxonomy; self.cacheTagColors(taxonomy.tags); }); }) - .then(function() { + .then(function () { return TagSrv.getFreeTags(); }) - .then(function(freeTags) { + .then(function (freeTags) { self.cacheTagColors(freeTags); deferred.resolve(self.cache); @@ -74,7 +74,7 @@ return deferred.promise; }; - self.openTagLibrary = function() { + self.openTagLibrary = function () { var defer = $q.defer(); var modalInstance = $uibModal.open({ @@ -84,18 +84,18 @@ templateUrl: 'views/partials/misc/taxonomy-selection.modal.html', size: 'lg', resolve: { - taxonomies: function() { + taxonomies: function () { return self.all(); } } }); modalInstance.result - .then(function(selectedTags) { + .then(function (selectedTags) { var filterFn = $filter('tagValue'), tags = []; - _.each(selectedTags, function(tag) { + _.each(selectedTags, function (tag) { tags.push({ text: filterFn(tag) }); @@ -104,7 +104,7 @@ //$scope.tags = $scope.tags.concat(tags); defer.resolve(tags); }) - .catch(function(err) { + .catch(function (err) { if (err && !_.isString(err)) { NotificationSrv.error('Tag selection', err.data, err.status); } diff --git a/frontend/app/views/partials/admin/taxonomy/view.html b/frontend/app/views/partials/admin/taxonomy/view.html index bcfbdf1b61..5e687a7782 100644 --- a/frontend/app/views/partials/admin/taxonomy/view.html +++ b/frontend/app/views/partials/admin/taxonomy/view.html @@ -40,15 +40,14 @@ - - - + {{::tag.predicate}} {{::tag.value || '-'}} {{::tag.colour}} - + diff --git a/frontend/app/views/partials/misc/taxonomy-selection.modal.html b/frontend/app/views/partials/misc/taxonomy-selection.modal.html index 5a5ce376d5..dcba7df369 100644 --- a/frontend/app/views/partials/misc/taxonomy-selection.modal.html +++ b/frontend/app/views/partials/misc/taxonomy-selection.modal.html @@ -17,8 +17,8 @@
- +

Click on a tag to unselect it

@@ -32,9 +32,11 @@
@@ -44,13 +46,15 @@ - Show all taxonomies + Show all + taxonomies
- +
@@ -58,17 +62,20 @@ @@ -76,6 +83,7 @@
From 85c308109e59528fd64aad1970684cc34485abb9 Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 26 Mar 2021 08:42:37 +0100 Subject: [PATCH 07/18] #1892 Change default S3 region (empty value is not permitted) --- ScalliGraph | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScalliGraph b/ScalliGraph index e9122723b8..b46932dddf 160000 --- a/ScalliGraph +++ b/ScalliGraph @@ -1 +1 @@ -Subproject commit e9122723b83fd87a4c1e808fe5fbe3d5626ef2ad +Subproject commit b46932dddf31d24caf3ca65a462fc86175ac029b From 468c092fa371b1b4881076a060c1f7ae459a485e Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 26 Mar 2021 08:47:05 +0100 Subject: [PATCH 08/18] #1891 Update caseId in alert when merge to existing case --- thehive/app/org/thp/thehive/services/AlertSrv.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/thehive/app/org/thp/thehive/services/AlertSrv.scala b/thehive/app/org/thp/thehive/services/AlertSrv.scala index e237b30997..c39e6387fd 100644 --- a/thehive/app/org/thp/thehive/services/AlertSrv.scala +++ b/thehive/app/org/thp/thehive/services/AlertSrv.scala @@ -291,6 +291,7 @@ class AlertSrv @Inject() ( _ <- importCustomFields(alert, `case`) _ <- caseSrv.addTags(`case`, alert.tags.toSet) _ <- alertCaseSrv.create(AlertCase(), alert, `case`) + _ <- get(alert).update(_.caseId, Some(`case`._id)).getOrFail("Alert") c <- caseSrv.get(`case`).update(_.description, description).getOrFail("Case") details <- Success( Json.obj( From 33aacaed2dddaa789512bad9ba1b87cfc70e7ac2 Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 26 Mar 2021 09:17:12 +0100 Subject: [PATCH 09/18] #1893 Add patternId property in case (type is "patternId") --- thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala | 2 ++ thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala | 2 ++ thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala | 1 + thehive/app/org/thp/thehive/controllers/v1/Properties.scala | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala index 35cd5d7e39..35baed7a5a 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala @@ -17,6 +17,7 @@ import org.thp.thehive.services.CaseTemplateOps._ import org.thp.thehive.services.CustomFieldOps._ import org.thp.thehive.services.ObservableOps._ import org.thp.thehive.services.OrganisationOps._ +import org.thp.thehive.services.ProcedureOps._ import org.thp.thehive.services.UserOps._ import org.thp.thehive.services._ import play.api.libs.json._ @@ -313,5 +314,6 @@ class PublicCase @Inject() ( .property("owningOrganisation", UMapping.string)( _.authSelect((cases, authContext) => cases.origin.visible(authContext).value(_.name)).readonly ) + .property("patternId", UMapping.string.sequence)(_.select(_.procedure.pattern.value(_.patternId)).readonly) .build } diff --git a/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala index b6d5140f80..97aec1d625 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala @@ -208,6 +208,8 @@ class DescribeCtrl @Inject() ( ) case ("dashboard", "status") => Some(Seq(PropertyDescription("status", "enumeration", Seq(JsString("Shared"), JsString("Private"), JsString("Deleted"))))) + case (_, "patternId") => + Some(Seq(PropertyDescription("patternId", "patternId", Nil))) case _ => None } diff --git a/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala index 9b2b3df80f..0643474110 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala @@ -185,6 +185,7 @@ class DescribeCtrl @Inject() ( case (_, "_createdBy") => Some(Seq(PropertyDescription("_createdBy", "user"))) case (_, "_updatedBy") => Some(Seq(PropertyDescription("_updatedBy", "user"))) case (_, "customFields") => Some(customFields) + case (_, "patternId") => Some(Seq(PropertyDescription("patternId", "patternId", Nil))) case _ => None } diff --git a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala index 931676302d..819844c133 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala @@ -247,7 +247,7 @@ class Properties @Inject() ( .property("owningOrganisation", UMapping.string)( _.authSelect((cases, authContext) => cases.origin.visible(authContext).value(_.name)).readonly ) - .property("procedures", UMapping.entityId.sequence)(_.select(_.procedure.value(_._id)).readonly) + .property("patternId", UMapping.string.sequence)(_.select(_.procedure.pattern.value(_.patternId)).readonly) .build lazy val caseTemplate: PublicProperties = From 824b55a58702e25d1999ebfa6987ba8d425a4969 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Fri, 26 Mar 2021 14:46:11 +0100 Subject: [PATCH 10/18] Update about dialog --- frontend/app/views/partials/about.html | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/frontend/app/views/partials/about.html b/frontend/app/views/partials/about.html index 0f9587ae4c..c2e0d8ff4f 100644 --- a/frontend/app/views/partials/about.html +++ b/frontend/app/views/partials/about.html @@ -1,26 +1,29 @@ From 66cbe14421a728b705295999e47d421448adb1da Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 26 Mar 2021 16:15:42 +0100 Subject: [PATCH 11/18] #1896 Limit number of flow element by age --- thehive/app/org/thp/thehive/services/FlowActor.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/thehive/app/org/thp/thehive/services/FlowActor.scala b/thehive/app/org/thp/thehive/services/FlowActor.scala index 578852f73e..bd715117d7 100644 --- a/thehive/app/org/thp/thehive/services/FlowActor.scala +++ b/thehive/app/org/thp/thehive/services/FlowActor.scala @@ -3,7 +3,7 @@ package org.thp.thehive.services import akka.actor.{Actor, ActorRef, ActorSystem, PoisonPill, Props} import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings, ClusterSingletonProxy, ClusterSingletonProxySettings} import com.google.inject.Injector -import org.apache.tinkerpop.gremlin.process.traversal.Order +import org.apache.tinkerpop.gremlin.process.traversal.{Order, P} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.Database import org.thp.scalligraph.services.EventSrv @@ -61,6 +61,7 @@ class FlowActor extends Actor { auditSrv .startTraversal .has(_.mainAction, true) + .has(_._createdAt, P.gt(fromDate)) .sort(_.by("_createdAt", Order.desc)) .visible(organisationSrv) .limit(10) From bedb40556fb804466a81cf5af8dcebd5fe700b94 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Fri, 26 Mar 2021 16:50:58 +0100 Subject: [PATCH 12/18] #1877 Show integer and flow custom fields in aggregation field list --- .../app/scripts/services/api/DashboardSrv.js | 64 +++++++++---------- .../directives/dashboard/counter/series.html | 7 +- .../directives/dashboard/line/series.html | 16 ++--- .../dashboard/multiline/series.html | 16 ++--- .../directives/dashboard/text/series.html | 7 +- 5 files changed, 54 insertions(+), 56 deletions(-) diff --git a/frontend/app/scripts/services/api/DashboardSrv.js b/frontend/app/scripts/services/api/DashboardSrv.js index ba5394b896..4e96544320 100644 --- a/frontend/app/scripts/services/api/DashboardSrv.js +++ b/frontend/app/scripts/services/api/DashboardSrv.js @@ -1,6 +1,6 @@ -(function() { +(function () { 'use strict'; - angular.module('theHiveServices').service('DashboardSrv', function(QueryBuilderSrv, localStorageService, $q, AuthenticationSrv, $http) { + angular.module('theHiveServices').service('DashboardSrv', function (QueryBuilderSrv, localStorageService, $q, AuthenticationSrv, $http) { var baseUrl = './api/dashboard'; var self = this; @@ -141,43 +141,43 @@ } ]; - this.skipFields = function(fields, types) { - return _.filter(fields, function(item) { + this.skipFields = function (fields, types) { + return _.filter(fields, function (item) { return types.indexOf(item.type) === -1; }); }; - this.pickFields = function(fields, types) { - return _.filter(fields, function(item) { + this.pickFields = function (fields, types) { + return _.filter(fields, function (item) { return types.indexOf(item.type) !== -1; }); }; - this.fieldsForAggregation = function(fields, agg) { - if(agg === 'count') { + this.fieldsForAggregation = function (fields, agg) { + if (agg === 'count') { return []; - } else if(agg === 'sum' || agg === 'avg') { - return self.pickFields(fields, ['number']); + } else if (agg === 'sum' || agg === 'avg') { + return self.pickFields(fields, ['number', 'integer', 'float']); } else { return fields; } }; this.renderers = { - severity: function() {} + severity: function () { } }; - this.create = function(dashboard) { + this.create = function (dashboard) { return $http.post(baseUrl, dashboard); }; - this.update = function(id, dashboard) { + this.update = function (id, dashboard) { var db = _.pick(dashboard, 'id', 'title', 'description', 'status', 'definition'); return $http.patch(baseUrl + '/' + id, db); }; - this.list = function() { + this.list = function () { return $http.post(baseUrl + '/_search', { range: 'all', sort: ['-status', '-updatedAt', '-createdAt'], @@ -194,28 +194,28 @@ }); }; - this.get = function(id) { + this.get = function (id) { return $http.get(baseUrl + '/' + id); }; - this.remove = function(id) { + this.remove = function (id) { return $http.delete(baseUrl + '/' + id); }; - this._objectifyBy = function(collection, field) { + this._objectifyBy = function (collection, field) { var obj = {}; - _.each(collection, function(item) { + _.each(collection, function (item) { obj[item[field]] = item; }); return obj; }; - this.getMetadata = function(version) { + this.getMetadata = function (version) { var defer = $q.defer(); - if(!version) { + if (!version) { version = 'v0'; } @@ -224,13 +224,13 @@ } else { $http .get('./api/' + version + '/describe/_all') - .then(function(response) { + .then(function (response) { var data = response.data; var metadata = { entities: _.keys(data).sort() }; - _.each(metadata.entities, function(entity) { + _.each(metadata.entities, function (entity) { metadata[entity] = _.omit(data[entity], 'attributes'); metadata[entity].attributes = self._objectifyBy(data[entity].attributes, 'name'); metadata[entity].attributeKeys = _.keys(metadata[entity].attributes).sort(); @@ -240,7 +240,7 @@ defer.resolve(metadata); }) - .catch(function(err) { + .catch(function (err) { defer.reject(err); }); } @@ -248,7 +248,7 @@ return defer.promise; }; - this.hasMinimalConfiguration = function(component) { + this.hasMinimalConfiguration = function (component) { switch (component.type) { case 'multiline': case 'text': @@ -258,22 +258,22 @@ } }; - this.buildFiltersQuery = function(fields, filters) { + this.buildFiltersQuery = function (fields, filters) { return QueryBuilderSrv.buildFiltersQuery(fields, filters); }; - this.buildChartQuery = function(filter, query) { - var criteria = _.filter(_.without([filter, query], null, undefined, '', '*'), function(c){return !_.isEmpty(c);}); + this.buildChartQuery = function (filter, query) { + var criteria = _.filter(_.without([filter, query], null, undefined, '', '*'), function (c) { return !_.isEmpty(c); }); - if(criteria.length === 0) { + if (criteria.length === 0) { return {}; } else { return criteria.length === 1 ? criteria[0] : { _and: criteria }; } }; - this.buildPeriodQuery = function(period, field, start, end) { - if(!period && !start && !end) { + this.buildPeriodQuery = function (period, field, start, end) { + if (!period && !start && !end) { return null; } @@ -287,7 +287,7 @@ from = moment(today).subtract(30, 'days'); } else if (period === 'last3Months') { from = moment(today).subtract(3, 'months'); - } else if(period === 'custom') { + } else if (period === 'custom') { from = start && start !== null ? moment(start).valueOf() : null; to = end && end !== null ? moment(end).hours(23).minutes(59).seconds(59).milliseconds(999).valueOf() : null; @@ -311,7 +311,7 @@ }; }; - this.exportDashboard = function(dashboard) { + this.exportDashboard = function (dashboard) { var fileName = dashboard.title.replace(/\s/gi, '_') + '.json'; var content = _.omit(dashboard, '_type', diff --git a/frontend/app/views/directives/dashboard/counter/series.html b/frontend/app/views/directives/dashboard/counter/series.html index 4ce488e140..3cf37983b0 100644 --- a/frontend/app/views/directives/dashboard/counter/series.html +++ b/frontend/app/views/directives/dashboard/counter/series.html @@ -11,15 +11,14 @@
diff --git a/frontend/app/views/directives/dashboard/line/series.html b/frontend/app/views/directives/dashboard/line/series.html index 11bc2bd11c..aaf7ea657e 100644 --- a/frontend/app/views/directives/dashboard/line/series.html +++ b/frontend/app/views/directives/dashboard/line/series.html @@ -10,20 +10,18 @@
- +
@@ -32,8 +30,10 @@
-
diff --git a/frontend/app/views/directives/dashboard/multiline/series.html b/frontend/app/views/directives/dashboard/multiline/series.html index 88abb6e2d3..9f9260c800 100644 --- a/frontend/app/views/directives/dashboard/multiline/series.html +++ b/frontend/app/views/directives/dashboard/multiline/series.html @@ -23,20 +23,18 @@
- +
@@ -45,8 +43,10 @@
-
diff --git a/frontend/app/views/directives/dashboard/text/series.html b/frontend/app/views/directives/dashboard/text/series.html index 04c4a36e0c..e8feec4af8 100644 --- a/frontend/app/views/directives/dashboard/text/series.html +++ b/frontend/app/views/directives/dashboard/text/series.html @@ -16,15 +16,14 @@
From 537765182dea7bfbded3f7930f5fea8f67c8af92 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Fri, 26 Mar 2021 16:53:32 +0100 Subject: [PATCH 13/18] #1893 Update patternId field type in case filter field list --- thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala | 2 +- thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala index 97aec1d625..85b5889188 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala @@ -209,7 +209,7 @@ class DescribeCtrl @Inject() ( case ("dashboard", "status") => Some(Seq(PropertyDescription("status", "enumeration", Seq(JsString("Shared"), JsString("Private"), JsString("Deleted"))))) case (_, "patternId") => - Some(Seq(PropertyDescription("patternId", "patternId", Nil))) + Some(Seq(PropertyDescription("patternId", "string", Nil))) case _ => None } diff --git a/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala index 0643474110..c034572401 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala @@ -185,7 +185,7 @@ class DescribeCtrl @Inject() ( case (_, "_createdBy") => Some(Seq(PropertyDescription("_createdBy", "user"))) case (_, "_updatedBy") => Some(Seq(PropertyDescription("_updatedBy", "user"))) case (_, "customFields") => Some(customFields) - case (_, "patternId") => Some(Seq(PropertyDescription("patternId", "patternId", Nil))) + case (_, "patternId") => Some(Seq(PropertyDescription("patternId", "string", Nil))) case _ => None } From 0d9a6a340d24a0a91fca6bf594aacd40c06fbac5 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Fri, 26 Mar 2021 21:03:55 +0100 Subject: [PATCH 14/18] #1901 Call dashboard call with the required fields only --- .../dashboard/DashboardViewCtrl.js | 71 ++++----- .../controllers/dashboard/DashboardsCtrl.js | 137 ++++++++++-------- .../directives/dashboard/counter/series.html | 2 +- .../directives/dashboard/line/series.html | 2 +- .../dashboard/multiline/series.html | 2 +- .../directives/dashboard/text/series.html | 2 +- .../partials/dashboard/create.dialog.html | 6 +- 7 files changed, 118 insertions(+), 104 deletions(-) diff --git a/frontend/app/scripts/controllers/dashboard/DashboardViewCtrl.js b/frontend/app/scripts/controllers/dashboard/DashboardViewCtrl.js index 64946c42c8..459a43aae6 100644 --- a/frontend/app/scripts/controllers/dashboard/DashboardViewCtrl.js +++ b/frontend/app/scripts/controllers/dashboard/DashboardViewCtrl.js @@ -1,9 +1,9 @@ -(function() { +(function () { 'use strict'; angular .module('theHiveControllers') - .controller('DashboardViewCtrl', function($scope, $q, $interval, $timeout, $uibModal, AuthenticationSrv, DashboardSrv, NotificationSrv, ModalUtilsSrv, UtilsSrv, dashboard, metadata) { + .controller('DashboardViewCtrl', function ($scope, $q, $interval, $timeout, $uibModal, AuthenticationSrv, DashboardSrv, NotificationSrv, ModalUtilsSrv, UtilsSrv, dashboard, metadata) { var self = this; this.currentUser = AuthenticationSrv.currentUser; @@ -15,13 +15,13 @@ this.autoRefresh = null; this.authRefreshRunner = null; - this.buildDashboardPeriodFilter = function(period) { + this.buildDashboardPeriodFilter = function (period) { return period === 'custom' ? DashboardSrv.buildPeriodQuery(period, 'createdAt', this.definition.customPeriod.fromDate, this.definition.customPeriod.toDate) : DashboardSrv.buildPeriodQuery(period, 'createdAt'); }; - this.loadDashboard = function(dashboard) { + this.loadDashboard = function (dashboard) { this.dashboard = dashboard; this.definition = JSON.parse(dashboard.definition) || { period: 'all', @@ -37,22 +37,22 @@ this.loadDashboard(dashboard); - $scope.$watch('$vm.autoRefresh', function(value) { - if(value === self.authRefreshRunner || self.options.editLayout === true) { + $scope.$watch('$vm.autoRefresh', function (value) { + if (value === self.authRefreshRunner || self.options.editLayout === true) { return; } - if(value === null) { + if (value === null) { $interval.cancel(self.authRefreshRunner); } else { $interval.cancel(self.authRefreshRunner); - self.authRefreshRunner = $interval(function() { + self.authRefreshRunner = $interval(function () { $scope.$broadcast('refresh-chart', self.periodFilter); }, value * 1000); } }); - this.canEditDashboard = function() { + this.canEditDashboard = function () { return (this.createdBy === this.currentUser.login) || this.dashboardStatus === 'Shared'; }; @@ -70,23 +70,23 @@ text: 'Text', multiline: 'Multi Lines' }, - editLayout: !_.find(this.definition.items, function(row) { + editLayout: !_.find(this.definition.items, function (row) { return row.items.length > 0; }) && this.canEditDashboard() }; - this.applyPeriod = function(period) { + this.applyPeriod = function (period) { this.definition.period = period; this.periodFilter = this.buildDashboardPeriodFilter(period); $scope.$broadcast('refresh-chart', this.periodFilter); }; - this.removeContainer = function(index) { + this.removeContainer = function (index) { var row = this.definition.items[index]; var promise; - if(row.items.length === 0) { + if (row.items.length === 0) { // If the container is empty, don't ask for confirmation promise = $q.resolve(); } else { @@ -96,54 +96,55 @@ }); } - promise.then(function() { + promise.then(function () { self.definition.items.splice(index, 1); }); }; - this.saveDashboard = function() { - var copy = _.pick(this.dashboard, 'title', 'description', 'status'); - copy.definition = angular.toJson(this.definition); + this.saveDashboard = function () { + var copy = { + definition: angular.toJson(this.definition) + }; DashboardSrv.update(this.dashboard.id, copy) - .then(function(/*response*/) { + .then(function (/*response*/) { self.options.editLayout = false; self.resizeCharts(); NotificationSrv.log('The dashboard has been successfully updated', 'success'); }) - .catch(function(err) { + .catch(function (err) { NotificationSrv.error('DashboardEditCtrl', err.data, err.status); }); }; - this.removeItem = function(rowIndex, colIndex) { + this.removeItem = function (rowIndex, colIndex) { ModalUtilsSrv.confirm('Remove widget', 'Are you sure you want to remove this item', { okText: 'Yes, remove it', flavor: 'danger' - }).then(function() { + }).then(function () { var row = self.definition.items[rowIndex]; row.items.splice(colIndex, 1); - $timeout(function() { + $timeout(function () { $scope.$broadcast('resize-chart-' + rowIndex); }, 0); }); }; - this.itemInserted = function(item, rows/*, rowIndex, index*/) { - if(!item.id){ + this.itemInserted = function (item, rows/*, rowIndex, index*/) { + if (!item.id) { item.id = UtilsSrv.guid(); } - for(var i=0; i < rows.length; i++) { + for (var i = 0; i < rows.length; i++) { $scope.$broadcast('resize-chart-' + i); } if (this.options.containerAllowedTypes.indexOf(item.type) !== -1 && !item.options.entity) { // The item is a widget - $timeout(function() { + $timeout(function () { $scope.$broadcast('edit-chart-' + item.id); }, 0); } @@ -151,34 +152,34 @@ return item; }; - this.itemDragStarted = function(colIndex, row) { + this.itemDragStarted = function (colIndex, row) { row.items.splice(colIndex, 1); }; - this.exportDashboard = function() { + this.exportDashboard = function () { DashboardSrv.exportDashboard(this.dashboard); }; - this.resizeCharts = function() { - $timeout(function() { - for(var i=0; i < self.definition.items.length; i++) { + this.resizeCharts = function () { + $timeout(function () { + for (var i = 0; i < self.definition.items.length; i++) { $scope.$broadcast('resize-chart-' + i); } }, 100); }; - this.enableEditMode = function() { + this.enableEditMode = function () { this.options.editLayout = true; this.resizeCharts(); }; - this.enableViewMode = function() { + this.enableViewMode = function () { DashboardSrv.get(this.dashboard.id) - .then(function(response) { + .then(function (response) { self.loadDashboard(response.data); self.options.editLayout = false; self.resizeCharts(); - }, function(err) { + }, function (err) { NotificationSrv.error('DashboardViewCtrl', err.data, err.status); }); }; diff --git a/frontend/app/scripts/controllers/dashboard/DashboardsCtrl.js b/frontend/app/scripts/controllers/dashboard/DashboardsCtrl.js index 307c17ba0b..a9f679d18c 100644 --- a/frontend/app/scripts/controllers/dashboard/DashboardsCtrl.js +++ b/frontend/app/scripts/controllers/dashboard/DashboardsCtrl.js @@ -1,28 +1,28 @@ -(function() { +(function () { 'use strict'; angular .module('theHiveControllers') - .controller('DashboardImportCtrl', function($scope, $uibModalInstance) { + .controller('DashboardImportCtrl', function ($scope, $uibModalInstance) { var self = this; this.formData = { fileContent: {} }; - $scope.$watch('vm.formData.attachment', function(file) { - if(!file) { + $scope.$watch('vm.formData.attachment', function (file) { + if (!file) { self.formData.fileContent = {}; return; } var aReader = new FileReader(); aReader.readAsText(self.formData.attachment, 'UTF-8'); aReader.onload = function (evt) { - $scope.$apply(function() { + $scope.$apply(function () { self.formData.fileContent = JSON.parse(aReader.result); }); } aReader.onerror = function (evt) { - $scope.$apply(function() { + $scope.$apply(function () { self.formData.fileContent = {}; }); } @@ -39,23 +39,26 @@ $uibModalInstance.dismiss('cancel'); }; }) - .controller('DashboardModalCtrl', function($uibModalInstance, $state, statuses, dashboard) { + .controller('DashboardModalCtrl', function ($uibModalInstance, AuthenticationSrv, $state, statuses, dashboard) { this.dashboard = dashboard; this.statuses = statuses; + this.currentUser = AuthenticationSrv.currentUser; - this.cancel = function() { + this.cancel = function () { $uibModalInstance.dismiss(); }; - this.ok = function() { + this.ok = function () { return $uibModalInstance.close(dashboard); }; }) - .controller('DashboardsCtrl', function($scope, $state, $uibModal, PaginatedQuerySrv, FilteringSrv, ModalUtilsSrv, NotificationSrv, DashboardSrv, AuthenticationSrv) { + .controller('DashboardsCtrl', function ($scope, $state, $uibModal, PaginatedQuerySrv, FilteringSrv, ModalUtilsSrv, NotificationSrv, DashboardSrv, AuthenticationSrv) { this.dashboards = []; var self = this; - this.$onInit = function() { + this.currentUser = AuthenticationSrv.currentUser; + + this.$onInit = function () { self.filtering = new FilteringSrv('dashboard', 'dashboard.list', { version: 'v0', defaults: { @@ -68,7 +71,7 @@ }); self.filtering.initContext('list') - .then(function() { + .then(function () { self.load(); $scope.$watch('$vm.list.pageSize', function (newValue) { @@ -77,7 +80,7 @@ }); } - this.load = function() { + this.load = function () { self.list = new PaginatedQuerySrv({ name: 'dashboard-list', @@ -88,10 +91,10 @@ pageSize: self.filtering.context.pageSize, filter: this.filtering.buildQuery(), operations: [ - {'_name': 'listDashboard'} + { '_name': 'listDashboard' } ], - onFailure: function(err) { - if(err && err.status === 400) { + onFailure: function (err) { + if (err && err.status === 400) { self.filtering.resetContext(); self.load(); } @@ -99,24 +102,24 @@ }); }; - this.openDashboardModal = function(dashboard) { + this.openDashboardModal = function (dashboard) { return $uibModal.open({ templateUrl: 'views/partials/dashboard/create.dialog.html', controller: 'DashboardModalCtrl', controllerAs: '$vm', size: 'lg', resolve: { - statuses: function() { + statuses: function () { return ['Private', 'Shared']; }, - dashboard: function() { + dashboard: function () { return dashboard; } } }); }; - this.addDashboard = function() { + this.addDashboard = function () { var modalInstance = this.openDashboardModal({ title: null, description: null, @@ -125,79 +128,87 @@ }); modalInstance.result - .then(function(dashboard) { + .then(function (dashboard) { return DashboardSrv.create(dashboard); }) - .then(function(response) { - $state.go('app.dashboards-view', {id: response.data.id}); + .then(function (response) { + $state.go('app.dashboards-view', { id: response.data.id }); NotificationSrv.log('The dashboard has been successfully created', 'success'); }) - .catch(function(err) { + .catch(function (err) { if (err && err.status) { NotificationSrv.error('DashboardsCtrl', err.data, err.status); } }); }; - this.duplicateDashboard = function(dashboard) { + this.duplicateDashboard = function (dashboard) { var copy = _.pick(dashboard, 'title', 'description', 'status', 'definition'); copy.title = 'Copy of ' + copy.title; this.openDashboardModal(copy) - .result.then(function(dashboard) { + .result.then(function (dashboard) { return DashboardSrv.create(dashboard); }) - .then(function(response) { - $state.go('app.dashboards-view', {id: response.data.id}); + .then(function (response) { + $state.go('app.dashboards-view', { id: response.data.id }); NotificationSrv.log('The dashboard has been successfully created', 'success'); }) - .catch(function(err) { + .catch(function (err) { if (err && err.status) { NotificationSrv.error('DashboardsCtrl', err.data, err.status); } }); }; - this.editDashboard = function(dashboard) { + this.editDashboard = function (dashboard) { var copy = _.extend({}, dashboard); - this.openDashboardModal(copy).result.then(function(dashboard) { - return DashboardSrv.update(dashboard.id, _.omit(dashboard, 'id')); - }) - .then(function(response) { - self.load() + this.openDashboardModal(copy).result + .then(function (dashboard) { - NotificationSrv.log('The dashboard has been successfully updated', 'success'); - }) - .catch(function(err) { - if (err && err.status) { - NotificationSrv.error('DashboardsCtrl', err.data, err.status); - } - }); + if (dashboard.createdBy === self.currentUser.login) { + return DashboardSrv.update(dashboard.id, _.omit(dashboard, 'id', 'definition')); + } else { + return DashboardSrv.update(dashboard.id, _.omit(dashboard, 'id', 'status', 'definition')); + } + + + }) + .then(function (response) { + self.load() + + NotificationSrv.log('The dashboard has been successfully updated', 'success'); + }) + .catch(function (err) { + if (err && err.status) { + NotificationSrv.error('DashboardsCtrl', err.data, err.status); + } + }); }; - this.deleteDashboard = function(id) { + this.deleteDashboard = function (id) { ModalUtilsSrv.confirm('Remove dashboard', 'Are you sure you want to remove this dashboard', { okText: 'Yes, remove it', flavor: 'danger' }) - .then(function() { + .then(function () { return DashboardSrv.remove(id); }) - .then(function(response) { + .then(function (response) { self.load(); NotificationSrv.log('The dashboard has been successfully removed', 'success'); }); }; - this.exportDashboard = function(dashboard) { + this.exportDashboard = function (dashboard) { DashboardSrv.exportDashboard(dashboard); } - this.importDashboard = function() { + this.importDashboard = function () { var modalInstance = $uibModal.open({ animation: true, templateUrl: 'views/partials/dashboard/import.dialog.html', @@ -206,19 +217,19 @@ size: 'lg' }); - modalInstance.result.then(function(dashboard) { + modalInstance.result.then(function (dashboard) { return DashboardSrv.create(dashboard); }) - .then(function(response) { - $state.go('app.dashboards-view', {id: response.data.id}); + .then(function (response) { + $state.go('app.dashboards-view', { id: response.data.id }); - NotificationSrv.log('The dashboard has been successfully imported', 'success'); - }) - .catch(function(err) { - if (err && err.status) { - NotificationSrv.error('DashboardsCtrl', err.data, err.status); - } - }); + NotificationSrv.log('The dashboard has been successfully imported', 'success'); + }) + .catch(function (err) { + if (err && err.status) { + NotificationSrv.error('DashboardsCtrl', err.data, err.status); + } + }); } // Filtering @@ -249,28 +260,28 @@ this.search(); }; - this.filterBy = function(field, value) { + this.filterBy = function (field, value) { self.filtering.clearFilters() - .then(function(){ + .then(function () { self.addFilterValue(field, value); }); }; - this.sortBy = function(sort) { + this.sortBy = function (sort) { self.list.sort = sort; self.list.update(); self.filtering.setSort(sort); }; - this.sortByField = function(field) { + this.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]; } self.list.sort = sort; diff --git a/frontend/app/views/directives/dashboard/counter/series.html b/frontend/app/views/directives/dashboard/counter/series.html index 3cf37983b0..bbb3722365 100644 --- a/frontend/app/views/directives/dashboard/counter/series.html +++ b/frontend/app/views/directives/dashboard/counter/series.html @@ -17,7 +17,7 @@
diff --git a/frontend/app/views/directives/dashboard/line/series.html b/frontend/app/views/directives/dashboard/line/series.html index aaf7ea657e..a560ad9662 100644 --- a/frontend/app/views/directives/dashboard/line/series.html +++ b/frontend/app/views/directives/dashboard/line/series.html @@ -16,7 +16,7 @@
diff --git a/frontend/app/views/directives/dashboard/multiline/series.html b/frontend/app/views/directives/dashboard/multiline/series.html index 9f9260c800..593cf13acd 100644 --- a/frontend/app/views/directives/dashboard/multiline/series.html +++ b/frontend/app/views/directives/dashboard/multiline/series.html @@ -29,7 +29,7 @@
diff --git a/frontend/app/views/directives/dashboard/text/series.html b/frontend/app/views/directives/dashboard/text/series.html index e8feec4af8..f1e12a9d65 100644 --- a/frontend/app/views/directives/dashboard/text/series.html +++ b/frontend/app/views/directives/dashboard/text/series.html @@ -22,7 +22,7 @@
diff --git a/frontend/app/views/partials/dashboard/create.dialog.html b/frontend/app/views/partials/dashboard/create.dialog.html index 0c32f324a9..9507a70c1f 100644 --- a/frontend/app/views/partials/dashboard/create.dialog.html +++ b/frontend/app/views/partials/dashboard/create.dialog.html @@ -27,7 +27,8 @@