From c7a446d2b10cedd577cbbb95024b9ab20f7171b5 Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 26 Oct 2021 07:47:08 +0200 Subject: [PATCH 1/7] #2224 Fix ClassCastException in zip upload --- .../app/org/thp/thehive/controllers/v0/ObservableCtrl.scala | 4 ++-- .../app/org/thp/thehive/controllers/v1/ObservableCtrl.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala index e42332554d..098f320d34 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala @@ -343,8 +343,8 @@ class ObservableCtrl @Inject() ( private def getZipFiles(observable: InputObservable, zipPassword: Option[String]): Seq[InputObservable] = observable.attachment.flatMap(_.swap.toSeq).flatMap { attachment => - val zipFile = new ZipFile(attachment.filepath.toFile) - val files: Seq[FileHeader] = zipFile.getFileHeaders.asScala.asInstanceOf[Seq[FileHeader]] + val zipFile = new ZipFile(attachment.filepath.toFile) + val files = zipFile.getFileHeaders.asScala if (zipFile.isEncrypted) zipFile.setPassword(zipPassword.getOrElse(configuration.get[String]("datastore.attachment.password")).toCharArray) diff --git a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala index fdcada7e17..ede9f538ec 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala @@ -372,8 +372,8 @@ class ObservableCtrl @Inject() ( private def getZipFiles(observable: InputObservable, zipPassword: Option[String]): Seq[InputObservable] = observable.attachment.flatMap(_.swap.toSeq).flatMap { attachment => - val zipFile = new ZipFile(attachment.filepath.toFile) - val files: Seq[FileHeader] = zipFile.getFileHeaders.asScala.asInstanceOf[Seq[FileHeader]] + val zipFile = new ZipFile(attachment.filepath.toFile) + val files = zipFile.getFileHeaders.asScala if (zipFile.isEncrypted) zipFile.setPassword(zipPassword.getOrElse(configuration.get[String]("datastore.attachment.password")).toCharArray) From d9bfdb40afa65a3522cb9c2855f51d4f415dbf6b Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 26 Oct 2021 07:54:14 +0200 Subject: [PATCH 2/7] #2226 Ensure to close files --- ScalliGraph | 2 +- .../app/org/thp/thehive/services/AttachmentSrv.scala | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ScalliGraph b/ScalliGraph index 3069fcc626..837d461fc1 160000 --- a/ScalliGraph +++ b/ScalliGraph @@ -1 +1 @@ -Subproject commit 3069fcc62619fe5063d354acde42e5e999f22317 +Subproject commit 837d461fc13908a1aabb3712baee7c5f37e7a55f diff --git a/thehive/app/org/thp/thehive/services/AttachmentSrv.scala b/thehive/app/org/thp/thehive/services/AttachmentSrv.scala index 9f70d36a78..4bb209f105 100644 --- a/thehive/app/org/thp/thehive/services/AttachmentSrv.scala +++ b/thehive/app/org/thp/thehive/services/AttachmentSrv.scala @@ -33,12 +33,10 @@ class AttachmentSrv @Inject() (configuration: Configuration, storageSrv: Storage val hs = hashers.fromPath(file.filepath) val id = hs.head.toString val is = Files.newInputStream(file.filepath) - val result = - storageSrv - .saveBinary("attachment", id, is) - .flatMap(_ => createEntity(Attachment(file.filename, Files.size(file.filepath), file.contentType, hs, id))) - is.close() - result + try storageSrv + .saveBinary("attachment", id, is) + .flatMap(_ => createEntity(Attachment(file.filename, Files.size(file.filepath), file.contentType, hs, id))) + finally is.close() } def create(filename: String, contentType: String, data: Array[Byte])(implicit From 928b4ba54b8d831a6bdab05b4f0abf5578de991f Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 26 Oct 2021 16:45:19 +0200 Subject: [PATCH 3/7] #2225 Optimise filters on observables --- thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala | 2 +- thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala index 098f320d34..c71af8ca49 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala @@ -367,7 +367,7 @@ class PublicObservable @Inject() ( override val initialQuery: Query = Query.init[Traversal.V[Observable]]( "listObservable", - (graph, authContext) => organisationSrv.get(authContext.organisation)(graph).shares.observables + (graph, authContext) => observableSrv.startTraversal(graph).visible(organisationSrv)(authContext) ) override val getQuery: ParamQuery[EntityIdOrName] = Query.initWithParam[EntityIdOrName, Traversal.V[Observable]]( "getObservable", diff --git a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala index ede9f538ec..4118aea90e 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala @@ -56,7 +56,7 @@ class ObservableCtrl @Inject() ( override val initialQuery: Query = Query.init[Traversal.V[Observable]]( "listObservable", - (graph, authContext) => organisationSrv.get(authContext.organisation)(graph).shares.observables + (graph, authContext) => observableSrv.startTraversal(graph).visible(organisationSrv)(authContext) ) override val getQuery: ParamQuery[EntityIdOrName] = Query.initWithParam[EntityIdOrName, Traversal.V[Observable]]( "getObservable", From 13f141239b1fdba72b1584c94d15304323ec7e6b Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 26 Oct 2021 16:58:02 +0200 Subject: [PATCH 4/7] #2225 Optimise filters --- .../cortex/controllers/v0/ActionCtrl.scala | 3 ++- .../thehive/controllers/v0/AlertCtrl.scala | 9 +++++-- .../controllers/v0/DashboardCtrl.scala | 1 - .../thp/thehive/controllers/v0/TaskCtrl.scala | 2 +- .../controllers/v0/TheHiveQueryExecutor.scala | 25 ++++--------------- .../thp/thehive/controllers/v1/TaskCtrl.scala | 2 +- .../thp/thehive/services/ObservableSrv.scala | 2 +- 7 files changed, 17 insertions(+), 27 deletions(-) diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala index ccbe2999bd..eaacf34de7 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala @@ -93,7 +93,8 @@ class PublicAction @Inject() (actionSrv: ActionSrv, organisationSrv: Organisatio val actionsQuery: Query = new Query { override val name: String = "actions" override def checkFrom(t: ru.Type): Boolean = - SubType(t, ru.typeOf[Traversal.V[Case]]) || SubType(t, ru.typeOf[Traversal.V[Observable]]) || + SubType(t, ru.typeOf[Traversal.V[Case]]) || + SubType(t, ru.typeOf[Traversal.V[Observable]]) || SubType(t, ru.typeOf[Traversal.V[Task]]) || SubType(t, ru.typeOf[Traversal.V[Log]]) || SubType(t, ru.typeOf[Traversal.V[Alert]]) diff --git a/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala index 2c74291814..9f73e4b0bc 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala @@ -1,7 +1,7 @@ package org.thp.thehive.controllers.v0 import io.scalaland.chimney.dsl._ -import org.apache.tinkerpop.gremlin.process.traversal.{Compare, Contains} +import org.apache.tinkerpop.gremlin.process.traversal.{Compare, Contains, P} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.controllers._ import org.thp.scalligraph.models.{Database, Entity, UMapping} @@ -367,6 +367,7 @@ class PublicAlert @Inject() ( alertSrv: AlertSrv, organisationSrv: OrganisationSrv, customFieldSrv: CustomFieldSrv, + observableSrv: ObservableSrv, db: Database ) extends PublicData { override val entityName: String = "alert" @@ -392,7 +393,11 @@ class PublicAlert @Inject() ( override val outputQuery: Query = Query.output[RichAlert, Traversal.V[Alert]](_.richAlert) override val extraQueries: Seq[ParamQuery[_]] = Seq( Query[Traversal.V[Alert], Traversal.V[Case]]("cases", (alertSteps, _) => alertSteps.`case`), - Query[Traversal.V[Alert], Traversal.V[Observable]]("observables", (alertSteps, _) => alertSteps.observables), + Query[Traversal.V[Alert], Traversal.V[Observable]]( + "observables", + (alertSteps, authContext) => + observableSrv.startTraversal(alertSteps.graph).has(_.relatedId, P.within(alertSteps._id.toSeq: _*)).visible(organisationSrv)(authContext) + ), Query[ Traversal.V[Alert], Traversal[(RichAlert, Seq[RichObservable]), JMap[String, Any], Converter[(RichAlert, Seq[RichObservable]), JMap[String, Any]]] diff --git a/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala index c012d820a8..8e0289c44d 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala @@ -23,7 +23,6 @@ import scala.util.{Failure, Success} class DashboardCtrl @Inject() ( override val entrypoint: Entrypoint, dashboardSrv: DashboardSrv, - userSrv: UserSrv, implicit val db: Database, override val publicData: PublicDashboard, @Named("v0") override val queryExecutor: QueryExecutor diff --git a/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala index c4d11881dd..ee76571452 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala @@ -99,7 +99,7 @@ class PublicTask @Inject() (taskSrv: TaskSrv, organisationSrv: OrganisationSrv, override val initialQuery: Query = Query.init[Traversal.V[Task]]( "listTask", - (graph, authContext) => taskSrv.startTraversal(graph).inOrganisation(organisationSrv.currentId(graph, authContext)) + (graph, authContext) => taskSrv.startTraversal(graph).visible(organisationSrv)(authContext) ) //organisationSrv.get(authContext.organisation)(graph).shares.tasks) override val pageQuery: ParamQuery[OutputParam] = Query.withParam[OutputParam, Traversal.V[Task], IteratorOutput]( diff --git a/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala b/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala index 5db1c5c90e..2c4e1b2c5c 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala @@ -9,7 +9,7 @@ import org.thp.scalligraph.services.config.{ApplicationConfig, ConfigItem} import org.thp.scalligraph.traversal.Traversal import org.thp.scalligraph.traversal.TraversalOps._ import org.thp.scalligraph.utils.RichType -import org.thp.scalligraph.{BadRequestError, EntityId, EntityIdOrName, GlobalQueryExecutor} +import org.thp.scalligraph.{BadRequestError, EntityId, GlobalQueryExecutor} import org.thp.thehive.models._ import org.thp.thehive.services.AlertOps._ import org.thp.thehive.services.CaseOps._ @@ -90,7 +90,7 @@ class TheHiveQueryExecutor @Inject() ( override val customFilterQuery: FilterQuery = FilterQuery(publicProperties) { (tpe, globalParser) => FieldsParser("parentChildFilter") { case (_, FObjOne("_parent", ParentIdFilter(parentType, parentId))) if parentTypes.isDefinedAt((tpe, parentType)) => - Good(new ParentIdInputFilter(parentType, parentId)) + Good(new ParentIdInputFilter(parentId)) case (path, FObjOne("_parent", ParentQueryFilter(parentType, parentFilterField))) if parentTypes.isDefinedAt((tpe, parentType)) => globalParser(parentTypes((tpe, parentType))).apply(path, parentFilterField).map(query => new ParentQueryInputFilter(parentType, query)) case (path, FObjOne("_child", ChildQueryFilter(childType, childQueryField))) if childTypes.isDefinedAt((tpe, childType)) => @@ -118,7 +118,7 @@ object ParentIdFilter { .fold(Some(_), _ => None) } -class ParentIdInputFilter(parentType: String, parentId: String) extends InputQuery[Traversal.Unk, Traversal.Unk] { +class ParentIdInputFilter(parentId: String) extends InputQuery[Traversal.Unk, Traversal.Unk] { override def apply( publicProperties: PublicProperties, traversalType: ru.Type, @@ -129,35 +129,20 @@ class ParentIdInputFilter(parentType: String, parentId: String) extends InputQue .getTypeArgs(traversalType, ru.typeOf[Traversal[_, _, _]]) .headOption .collect { - case t if t <:< ru.typeOf[Task] && parentType == "caseTemplate" => - traversal - .asInstanceOf[Traversal.V[Task]] - .filter(_.caseTemplate.get(EntityIdOrName(parentId))) - .asInstanceOf[Traversal.Unk] case t if t <:< ru.typeOf[Task] => traversal .asInstanceOf[Traversal.V[Task]] - .filter(_.`case`.get(EntityIdOrName(parentId))) + .has(_.relatedId, EntityId(parentId)) .asInstanceOf[Traversal.Unk] case t if t <:< ru.typeOf[Observable] => traversal .asInstanceOf[Traversal.V[Observable]] .has(_.relatedId, EntityId(parentId)) .asInstanceOf[Traversal.Unk] -// && parentType == "alert" => -// traversal -// .asInstanceOf[Traversal.V[Observable]] -// .filter(_.alert.get(EntityIdOrName(parentId))) -// .asInstanceOf[Traversal.Unk] -// case t if t <:< ru.typeOf[Observable] => -// traversal -// .asInstanceOf[Traversal.V[Observable]] -// .filter(_.`case`.get(EntityIdOrName(parentId))) -// .asInstanceOf[Traversal.Unk] case t if t <:< ru.typeOf[Log] => traversal .asInstanceOf[Traversal.V[Log]] - .filter(_.task.get(EntityIdOrName(parentId))) + .has(_.taskId, EntityId(parentId)) .asInstanceOf[Traversal.Unk] } .getOrElse(throw BadRequestError(s"$traversalType hasn't parent")) diff --git a/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala index 8acb8fcfb6..d2f19169f6 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala @@ -36,7 +36,7 @@ class TaskCtrl @Inject() ( override val initialQuery: Query = Query.init[Traversal.V[Task]]( "listTask", - (graph, authContext) => taskSrv.startTraversal(graph).inOrganisation(organisationSrv.currentId(graph, authContext)) + (graph, authContext) => taskSrv.startTraversal(graph).visible(organisationSrv)(authContext) // organisationSrv.get(authContext.organisation)(graph).shares.tasks) ) override val pageQuery: ParamQuery[OutputParam] = Query.withParam[OutputParam, Traversal.V[Task], IteratorOutput]( diff --git a/thehive/app/org/thp/thehive/services/ObservableSrv.scala b/thehive/app/org/thp/thehive/services/ObservableSrv.scala index 4365f75750..713d4bb59b 100644 --- a/thehive/app/org/thp/thehive/services/ObservableSrv.scala +++ b/thehive/app/org/thp/thehive/services/ObservableSrv.scala @@ -253,7 +253,7 @@ object ObservableOps { def organisations: Traversal.V[Organisation] = traversal - .unionFlat(identity, _.in("ReportObservable").in("ObservableJob").v[Observable]) + .optional(_.in("ReportObservable").in("ObservableJob").v[Observable]) .unionFlat(_.shares.organisation, _.alert.organisation) // traversal.coalesceIdent(_.in[ShareObservable].in[OrganisationShare], _.in[AlertObservable].out[AlertOrganisation]).v[Organisation] From 9389ff3559b1bba833d66963c6de71bb23521a65 Mon Sep 17 00:00:00 2001 From: To-om Date: Thu, 28 Oct 2021 13:42:12 +0200 Subject: [PATCH 5/7] #2231 Add NoAuthentication method in json writes --- .../src/main/scala/org/thp/client/Authentication.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client-common/src/main/scala/org/thp/client/Authentication.scala b/client-common/src/main/scala/org/thp/client/Authentication.scala index c20887eb57..a5bd38f7b2 100644 --- a/client-common/src/main/scala/org/thp/client/Authentication.scala +++ b/client-common/src/main/scala/org/thp/client/Authentication.scala @@ -28,6 +28,8 @@ object Authentication { case PasswordAuthentication(username, password) => Json.obj("type" -> "basic", "username" -> username, "password" -> password) case KeyAuthentication(key, "") => Json.obj("type" -> "key", "key" -> key) case KeyAuthentication(key, "Bearer ") => Json.obj("type" -> "bearer", "key" -> key) + case NoAuthentication => Json.obj("type" -> "none") + case KeyAuthentication(key, other) => Json.obj("type" -> other, "key" -> key) } implicit val format: Format[Authentication] = Format(reads, writes) } From 6059dd36c4ade9c3a30cb2f8fb0904b34e46cbf6 Mon Sep 17 00:00:00 2001 From: To-om Date: Thu, 28 Oct 2021 14:14:09 +0200 Subject: [PATCH 6/7] #1969 Fix webhook object format (add case merge event) --- .../org/thp/thehive/services/AuditSrv.scala | 11 +- .../org/thp/thehive/services/CaseSrv.scala | 114 +++++++++--------- .../notification/notifiers/Webhook.scala | 4 +- 3 files changed, 68 insertions(+), 61 deletions(-) diff --git a/thehive/app/org/thp/thehive/services/AuditSrv.scala b/thehive/app/org/thp/thehive/services/AuditSrv.scala index 259c751da9..806e20ca47 100644 --- a/thehive/app/org/thp/thehive/services/AuditSrv.scala +++ b/thehive/app/org/thp/thehive/services/AuditSrv.scala @@ -176,11 +176,6 @@ class AuditSrv @Inject() ( def delete(entity: E with Entity, context: C with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = auditSrv.create(Audit(Audit.delete, entity, None), context, None) - def merge(entity: E with Entity, destination: C with Entity, details: Option[JsObject] = None)(implicit - graph: Graph, - authContext: AuthContext - ): Try[Unit] = - auditSrv.create(Audit(Audit.merge, destination, details.map(_.toString())), destination, Some(destination)) } class SelfContextObjectAudit[E <: Product] { @@ -197,6 +192,12 @@ class AuditSrv @Inject() ( authContext: AuthContext ): Try[Unit] = auditSrv.create(Audit(Audit.delete, entity, details.map(_.toString())), context, None) + + def merge(entity: E with Entity, details: Option[JsObject] = None)(implicit + graph: Graph, + authContext: AuthContext + ): Try[Unit] = + auditSrv.create(Audit(Audit.merge, entity, details.map(_.toString())), entity, Some(entity)) } class UserAudit extends SelfContextObjectAudit[User] { diff --git a/thehive/app/org/thp/thehive/services/CaseSrv.scala b/thehive/app/org/thp/thehive/services/CaseSrv.scala index 582f75a26c..27304f1520 100644 --- a/thehive/app/org/thp/thehive/services/CaseSrv.scala +++ b/thehive/app/org/thp/thehive/services/CaseSrv.scala @@ -352,61 +352,67 @@ class CaseSrv @Inject() ( } def merge(cases: Seq[Case with Entity])(implicit graph: Graph, authContext: AuthContext): Try[RichCase] = - if (cases.size > 1 && canMerge(cases)) { - val mergedCase = Case( - cases.map(_.title).mkString(" / "), - cases.map(_.description).mkString("\n\n"), - cases.map(_.severity).max, - cases.map(_.startDate).min, - None, - cases.exists(_.flag), - cases.map(_.tlp).max, - cases.map(_.pap).max, - CaseStatus.Open, - cases.map(_.summary).fold(None)((s1, s2) => (s1 ++ s2).reduceOption(_ + "\n\n" + _)), - cases.flatMap(_.tags).distinct - ) + if (cases.size > 1 && canMerge(cases)) + auditSrv.mergeAudits { + val mergedCase = Case( + cases.map(_.title).mkString(" / "), + cases.map(_.description).mkString("\n\n"), + cases.map(_.severity).max, + cases.map(_.startDate).min, + None, + cases.exists(_.flag), + cases.map(_.tlp).max, + cases.map(_.pap).max, + CaseStatus.Open, + cases.map(_.summary).fold(None)((s1, s2) => (s1 ++ s2).reduceOption(_ + "\n\n" + _)), + cases.flatMap(_.tags).distinct + ) - val allProfilesOrgas: Seq[(Profile with Entity, Organisation with Entity)] = get(cases.head) - .shares - .project(_.by(_.profile).by(_.organisation)) - .toSeq - - for { - user <- userSrv.current.getOrFail("User") - currentOrga <- organisationSrv.current.getOrFail("Organisation") - richCase <- create(mergedCase, Some(user), currentOrga, Seq(), None, Seq()) - // Share case with all organisations except the one who created the merged case - _ <- - allProfilesOrgas - .filterNot(_._2._id == currentOrga._id) - .toTry(profileOrg => shareSrv.shareCase(owner = false, richCase.`case`, profileOrg._2, profileOrg._1)) - _ <- cases.toTry { c => - for { - - _ <- shareMergedCaseTasks(allProfilesOrgas.map(_._2), c, richCase.`case`) - _ <- shareMergedCaseObservables(allProfilesOrgas.map(_._2), c, richCase.`case`) - _ <- - get(c) - .alert - .update(_.caseId, richCase._id) - .toSeq - .toTry(alertSrv.alertCaseSrv.create(AlertCase(), _, richCase.`case`)) - _ <- - get(c) - .procedure - .toSeq - .toTry(caseProcedureSrv.create(CaseProcedure(), richCase.`case`, _)) - _ <- - get(c) - .richCustomFields - .toSeq - .toTry(c => createCustomField(richCase.`case`, EntityIdOrName(c.customField.name), c.value, c.order)) - } yield Success(()) - } - _ <- cases.toTry(super.delete(_)) - } yield richCase - } else + val allProfilesOrgas: Seq[(Profile with Entity, Organisation with Entity)] = get(cases.head) + .shares + .project(_.by(_.profile).by(_.organisation)) + .toSeq + + for { + user <- userSrv.current.getOrFail("User") + currentOrga <- organisationSrv.current.getOrFail("Organisation") + richCase <- create(mergedCase, Some(user), currentOrga, Seq(), None, Seq()) + // Share case with all organisations except the one who created the merged case + _ <- + allProfilesOrgas + .filterNot(_._2._id == currentOrga._id) + .toTry(profileOrg => shareSrv.shareCase(owner = false, richCase.`case`, profileOrg._2, profileOrg._1)) + _ <- cases.toTry { c => + for { + + _ <- shareMergedCaseTasks(allProfilesOrgas.map(_._2), c, richCase.`case`) + _ <- shareMergedCaseObservables(allProfilesOrgas.map(_._2), c, richCase.`case`) + _ <- + get(c) + .alert + .update(_.caseId, richCase._id) + .toSeq + .toTry(alertSrv.alertCaseSrv.create(AlertCase(), _, richCase.`case`)) + _ <- + get(c) + .procedure + .toSeq + .toTry(caseProcedureSrv.create(CaseProcedure(), richCase.`case`, _)) + _ <- + get(c) + .richCustomFields + .toSeq + .toTry(c => createCustomField(richCase.`case`, EntityIdOrName(c.customField.name), c.value, c.order)) + } yield Success(()) + } + _ <- cases.toTry(super.delete(_)) + } yield richCase + }(mergedCase => + auditSrv + .`case` + .merge(mergedCase.`case`, Some(Json.obj("cases" -> cases.map(c => Json.obj("_id" -> c._id, "number" -> c.number, "title" -> c.title))))) + ) + else Failure(BadRequestError("To be able to merge, cases must have same organisation / profile pair and user must be org-admin")) private def canMerge(cases: Seq[Case with Entity])(implicit graph: Graph, authContext: AuthContext): Boolean = { diff --git a/thehive/app/org/thp/thehive/services/notification/notifiers/Webhook.scala b/thehive/app/org/thp/thehive/services/notification/notifiers/Webhook.scala index 718d33a36d..c4b99340a3 100644 --- a/thehive/app/org/thp/thehive/services/notification/notifiers/Webhook.scala +++ b/thehive/app/org/thp/thehive/services/notification/notifiers/Webhook.scala @@ -86,7 +86,7 @@ class Webhook( object v0 extends AuditRenderer object v1 { - import org.thp.thehive.controllers.v0.Conversion._ + import org.thp.thehive.controllers.v1.Conversion._ def caseToJson: Traversal.V[Case] => Traversal[JsObject, JMap[String, Any], Converter[JsObject, JMap[String, Any]]] = _.richCaseWithoutPerms.domainMap[JsObject](_.toJson.as[JsObject]) @@ -226,7 +226,7 @@ class Webhook( Json.obj( "operation" -> audit.action, "details" -> audit.details.fold[JsValue](JsObject.empty)(fixCustomFieldDetails(objectType, _)), - "objectType" -> fromObjectType(objectType), + "objectType" -> objectType, "objectId" -> audit.objectId, "base" -> audit.mainAction, "startDate" -> audit._createdAt, From 870b2fe79c1e14e107a80bff284b480216d0a921 Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 29 Oct 2021 07:17:07 +0200 Subject: [PATCH 7/7] Release 4.1.12 --- CHANGELOG.md | 10 ++++++++++ build.sbt | 2 +- frontend/bower.json | 2 +- frontend/package.json | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd8aa8a49..c49ee55a06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## [4.1.12](https://github.com/TheHive-Project/TheHive/milestone/82) (2021-10-29) + +**Fixed bugs:** + +- [Bug] Upon case merge: missing webhook events about the operation performed on merged cases [\#1969](https://github.com/TheHive-Project/TheHive/issues/1969) +- [Bug] Uploading zipped observables raises a ClassCastException [\#2224](https://github.com/TheHive-Project/TheHive/issues/2224) +- [Bug] The search for an observable data is slow [\#2225](https://github.com/TheHive-Project/TheHive/issues/2225) +- [Bug] Uploading files could cause TheHive crash with "too many open files" errror [\#2226](https://github.com/TheHive-Project/TheHive/issues/2226) +- [Bug] Configuration containing endpoint with "authentication: none" cannot be written [\#2231](https://github.com/TheHive-Project/TheHive/issues/2231) + ## [4.1.11](https://github.com/TheHive-Project/TheHive/milestone/81) (2021-10-06) **Implemented enhancements:** diff --git a/build.sbt b/build.sbt index 660593ad36..875d8ecd00 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.11-1" +val thehiveVersion = "4.1.12-1" val scala212 = "2.12.13" val scala213 = "2.13.1" val supportedScalaVersions = List(scala212, scala213) diff --git a/frontend/bower.json b/frontend/bower.json index 09c88c068e..34f799e860 100644 --- a/frontend/bower.json +++ b/frontend/bower.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.11-1", + "version": "4.1.12-1", "license": "AGPL-3.0", "dependencies": { "jquery": "^3.4.1", diff --git a/frontend/package.json b/frontend/package.json index cf156ca1df..76a4762a14 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.11-1", + "version": "4.1.12-1", "license": "AGPL-3.0", "repository": { "type": "git",