diff --git a/thehive/app/org/thp/thehive/services/AlertSrv.scala b/thehive/app/org/thp/thehive/services/AlertSrv.scala index f18809bd4f..fc6a60b5a2 100644 --- a/thehive/app/org/thp/thehive/services/AlertSrv.scala +++ b/thehive/app/org/thp/thehive/services/AlertSrv.scala @@ -7,7 +7,7 @@ import org.thp.scalligraph.query.PropertyUpdater import org.thp.scalligraph.services._ import org.thp.scalligraph.traversal.TraversalOps._ import org.thp.scalligraph.traversal.{Converter, IdentityConverter, StepLabel, Traversal} -import org.thp.scalligraph.{CreateError, EntityId, EntityIdOrName, RichOptionTry, RichSeq} +import org.thp.scalligraph.{BadRequestError, CreateError, EntityId, EntityIdOrName, RichOptionTry, RichSeq} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.dto.v1.InputCustomFieldValue import org.thp.thehive.models._ @@ -280,28 +280,31 @@ class AlertSrv @Inject() ( } yield updatedCase def mergeInCase(alert: Alert with Entity, `case`: Case with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Case with Entity] = - auditSrv - .mergeAudits { - // No audit for markAsRead and observables - // Audits for customFields, description and tags - val description = `case`.description + s"\n \n#### Merged with alert #${alert.sourceRef} ${alert.title}\n\n${alert.description.trim}" - for { - _ <- markAsRead(alert._id) - _ <- importObservables(alert, `case`) - _ <- importCustomFields(alert, `case`) - _ <- caseSrv.addTags(`case`, get(alert).tags.toSeq.map(_.toString).toSet) - _ <- alertCaseSrv.create(AlertCase(), alert, `case`) - c <- caseSrv.get(`case`).update(_.description, description).getOrFail("Case") - details <- Success( - Json.obj( - "customFields" -> get(alert).richCustomFields.toSeq.map(_.toOutput.toJson), - "description" -> c.description, - "tags" -> caseSrv.get(`case`).tags.toSeq.map(_.toString) + if (get(alert).isImported) + Failure(BadRequestError("Alert is already imported")) + else + auditSrv + .mergeAudits { + // No audit for markAsRead and observables + // Audits for customFields, description and tags + val description = `case`.description + s"\n \n#### Merged with alert #${alert.sourceRef} ${alert.title}\n\n${alert.description.trim}" + for { + _ <- markAsRead(alert._id) + _ <- importObservables(alert, `case`) + _ <- importCustomFields(alert, `case`) + _ <- caseSrv.addTags(`case`, get(alert).tags.toSeq.map(_.toString).toSet) + _ <- alertCaseSrv.create(AlertCase(), alert, `case`) + c <- caseSrv.get(`case`).update(_.description, description).getOrFail("Case") + details <- Success( + Json.obj( + "customFields" -> get(alert).richCustomFields.toSeq.map(_.toOutput.toJson), + "description" -> c.description, + "tags" -> caseSrv.get(`case`).tags.toSeq.map(_.toString) + ) ) - ) - } yield details - }(details => auditSrv.alertToCase.merge(alert, `case`, Some(details))) - .flatMap(_ => caseSrv.getOrFail(`case`._id)) + } yield details + }(details => auditSrv.alertToCase.merge(alert, `case`, Some(details))) + .flatMap(_ => caseSrv.getOrFail(`case`._id)) def importObservables(alert: Alert with Entity, `case`: Case with Entity)(implicit graph: Graph, @@ -400,6 +403,9 @@ object AlertOps { def imported: Traversal[Boolean, Boolean, IdentityConverter[Boolean]] = traversal.choose(_.outE[AlertCase], onTrue = true, onFalse = false) + def isImported: Boolean = + traversal.outE[AlertCase].exists + def importDate: Traversal[Date, Date, Converter[Date, Date]] = traversal.outE[AlertCase].value(_._createdAt)