Skip to content

Commit

Permalink
#1969 Fix webhook object format (add case merge event)
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Oct 28, 2021
1 parent 9389ff3 commit 6059dd3
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 61 deletions.
11 changes: 6 additions & 5 deletions thehive/app/org/thp/thehive/services/AuditSrv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
Expand All @@ -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] {
Expand Down
114 changes: 60 additions & 54 deletions thehive/app/org/thp/thehive/services/CaseSrv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 6059dd3

Please sign in to comment.