Skip to content

Commit

Permalink
#176 Store metrics in case template instead of metrics names
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Nov 14, 2017
1 parent bcc0c39 commit b188adb
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 69 deletions.
2 changes: 1 addition & 1 deletion thehive-backend/app/models/CaseTemplate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ trait CaseTemplateAttributes { _: AttributeDef ⇒
val flag: A[Option[Boolean]] = optionalAttribute("flag", F.booleanFmt, "Flag of the case")
val tlp: A[Option[Long]] = optionalAttribute("tlp", TlpAttributeFormat, "TLP level")
val status: A[CaseTemplateStatus.Value] = attribute("status", F.enumFmt(CaseTemplateStatus), "Status of the case", CaseTemplateStatus.Ok)
val metricNames: A[Seq[String]] = multiAttribute("metricNames", F.stringFmt, "List of acceptable metric name")
val metrics: A[JsValue] = attribute("metrics", F.metricsFmt, "List of acceptable metrics")
val customFields: A[Option[JsValue]] = optionalAttribute("customFields", F.customFields, "List of acceptable custom fields")
val tasks: A[Seq[JsObject]] = multiAttribute("tasks", F.objectFmt(taskAttributes), "List of created tasks")
}
Expand Down
75 changes: 12 additions & 63 deletions thehive-backend/app/models/Migration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package models
import java.util.Date
import javax.inject.{ Inject, Singleton }

import scala.collection.immutable.{ Set ISet }
import scala.concurrent.{ ExecutionContext, Future }
import scala.math.BigDecimal.int2bigDecimal
import scala.util.Try
Expand All @@ -17,11 +16,9 @@ import akka.stream.Materializer
import akka.stream.scaladsl.Source
import services.AlertSrv

import org.elastic4play.models.BaseModelDef
import org.elastic4play.services.JsonFormat.attachmentFormat
import org.elastic4play.services._
import org.elastic4play.utils
import org.elastic4play.utils.{ Hasher, RichJson }
import org.elastic4play.utils.Hasher

case class UpdateMispAlertArtifact() extends EventMessage

Expand All @@ -31,14 +28,12 @@ class Migration(
mainHash: String,
extraHashes: Seq[String],
datastoreName: String,
models: ISet[BaseModelDef],
dblists: DBLists,
eventSrv: EventSrv,
implicit val ec: ExecutionContext,
implicit val materializer: Materializer) extends MigrationOperations {
@Inject() def this(
configuration: Configuration,
models: ISet[BaseModelDef],
dblists: DBLists,
eventSrv: EventSrv,
ec: ExecutionContext,
Expand All @@ -48,7 +43,7 @@ class Migration(
configuration.get[String]("datastore.hash.main"),
configuration.get[Seq[String]]("datastore.hash.extra"),
configuration.get[String]("datastore.name"),
models, dblists,
dblists,
eventSrv, ec, materializer)
}

Expand Down Expand Up @@ -123,7 +118,7 @@ class Migration(
.getOrElse(2L)
val source = (misp \ "serverId").asOpt[String].getOrElse("<null>")
val _id = hasher.fromString(s"misp|$source|$eventId").head.toString()
(misp \ "caze").asOpt[JsString].fold(JsObject(Nil))(c Json.obj("caze" c)) ++
(misp \ "caze").asOpt[JsString].fold(JsObject.empty)(c Json.obj("caze" c)) ++
Json.obj(
"_type" "alert",
"_id" _id,
Expand Down Expand Up @@ -233,8 +228,8 @@ class Migration(
},
// Add empty metrics and custom fields in cases
mapEntity("case") { caze
val metrics = (caze \ "metrics").asOpt[JsObject].getOrElse(JsObject(Nil))
val customFields = (caze \ "customFields").asOpt[JsObject].getOrElse(JsObject(Nil))
val metrics = (caze \ "metrics").asOpt[JsObject].getOrElse(JsObject.empty)
val customFields = (caze \ "customFields").asOpt[JsObject].getOrElse(JsObject.empty)
caze + ("metrics" metrics) + ("customFields" customFields)
})
case DatabaseState(10) Nil
Expand All @@ -244,16 +239,15 @@ class Migration(
val owner = (log \ "createdBy").asOpt[JsString].getOrElse(JsString("init"))
log + ("owner" owner)
},
mapEntity(_ true, entity entity - "user"))
}

private val requestCounter = new java.util.concurrent.atomic.AtomicInteger(0)

def getRequestId: String = {
utils.Instance.id + ":mig:" + requestCounter.incrementAndGet()
mapEntity(_ true, entity entity - "user"),
mapEntity("caseTemplate") { caseTemplate
val metricsName = (caseTemplate \ "metricNames").asOpt[Seq[String]].getOrElse(Nil)
val metrics = JsObject(metricsName.map(_ -> JsNull))
caseTemplate - "metricNames" + ("metrics" -> metrics)
})
}

def convertDate(json: JsValue): JsValue = {
private def convertDate(json: JsValue): JsValue = {
val datePattern = "yyyyMMdd'T'HHmmssZ"
val dateReads = Reads.dateReads(datePattern).orElse(Reads.DefaultDateReads)
val date = dateReads.reads(json).getOrElse {
Expand All @@ -262,49 +256,4 @@ class Migration(
}
org.elastic4play.JsonFormat.dateFormat.writes(date)
}

def removeDot[A <: JsValue](json: A): A = json match {
case obj: JsObject
obj.map {
case (key, value) if key.contains(".")
val splittedKey = key.split("\\.")
splittedKey.head splittedKey.tail.foldRight(removeDot(value))((k, v) JsObject(Seq(k v)))
case (key, value) key removeDot(value)
}
.asInstanceOf[A]
case other other
}

def auditDetailsCleanup(audit: JsObject): JsObject = removeDot {
// get audit details
(audit \ "details").asOpt[JsObject]
.flatMap { details
// get object type of audited object
(audit \ "objectType")
.asOpt[String]
// find related model
.flatMap(objectType models.find(_.name == objectType))
// and get name of audited attributes
.map(_.attributes.collect {
case attr if !attr.isUnaudited attr.name
})
.map { attributes
// put audited attribute in details and unaudited in otherDetails
val otherDetails = (audit \ "otherDetails")
.asOpt[String]
.flatMap(od Try(Json.parse(od).as[JsObject]).toOption)
.getOrElse(JsObject(Nil))
val (in, notIn) = details.fields.partition(f attributes.contains(f._1.split("\\.").head))
val newOtherDetails = otherDetails ++ JsObject(notIn)
audit + ("details" JsObject(in)) + ("otherDetails" JsString(newOtherDetails.toString.take(10000)))
}
}
.getOrElse(audit)
}

def addAuditRequestId(audit: JsObject): JsObject = (audit \ "requestId").asOpt[String] match {
case None if (audit \ "base").toOption.isDefined audit + ("requestId" JsString(getRequestId))
case None audit + ("requestId" JsString(getRequestId)) + ("base" JsBoolean(true))
case _ audit
}
}
9 changes: 4 additions & 5 deletions thehive-backend/app/services/CaseSrv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,12 @@ class CaseSrv(
private[CaseSrv] lazy val logger = Logger(getClass)

def applyTemplate(template: CaseTemplate, originalFields: Fields): Fields = {
def getJsObjectOrEmpty(value: Option[JsValue]) = value.fold(JsObject(Nil)) {
def getJsObjectOrEmpty(value: Option[JsValue]) = value.fold(JsObject.empty) {
case obj: JsObject obj
case _ JsObject(Nil)
case _ JsObject.empty
}

val metricNames = (originalFields.getStrings("metricNames").getOrElse(Nil) ++ template.metricNames()).distinct
val metrics = JsObject(metricNames.map(_ JsNull))
val metrics = originalFields.getValue("metrics").fold(JsObject.empty)(_.as[JsObject]) deepMerge template.metrics().as[JsObject]
val tags = (originalFields.getStrings("tags").getOrElse(Nil) ++ template.tags()).distinct
val customFields = getJsObjectOrEmpty(template.customFields()) ++ getJsObjectOrEmpty(originalFields.getValue("customFields"))

Expand All @@ -75,7 +74,7 @@ class CaseSrv(
.set("tags", JsArray(tags.map(JsString)))
.set("flag", originalFields.getBoolean("flag").orElse(template.flag()).map(JsBoolean))
.set("tlp", originalFields.getLong("tlp").orElse(template.tlp()).map(JsNumber(_)))
.set("metrics", originalFields.getValue("metrics").flatMap(_.asOpt[JsObject]).getOrElse(JsObject(Nil)) ++ metrics)
.set("metrics", originalFields.getValue("metrics").flatMap(_.asOpt[JsObject]).getOrElse(JsObject.empty) ++ metrics)
.set("customFields", customFields)
}

Expand Down

0 comments on commit b188adb

Please sign in to comment.