Skip to content

Commit

Permalink
#609 initial support of active reponse
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Jul 6, 2018
1 parent e2a031a commit 9d5c3bb
Show file tree
Hide file tree
Showing 8 changed files with 443 additions and 67 deletions.
90 changes: 78 additions & 12 deletions thehive-cortex/app/connectors/cortex/controllers/CortexCtrl.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package connectors.cortex.controllers

import javax.inject.{ Inject, Singleton }

import scala.concurrent.{ ExecutionContext, Future }

import play.api.Logger
Expand All @@ -19,16 +18,17 @@ import org.elastic4play.models.JsonFormat.baseModelEntityWrites
import org.elastic4play.services.{ Agg, AuxSrv, QueryDSL, QueryDef }
import org.elastic4play.services.JsonFormat.{ aggReads, queryReads }
import connectors.Connector
import connectors.cortex.models.JsonFormat.analyzerFormats
import connectors.cortex.services.{ CortexConfig, CortexSrv }
import connectors.cortex.models.JsonFormat.{ analyzerFormat, workerFormat }
import connectors.cortex.services.{ CortexActionSrv, CortexAnalyzerSrv, CortexConfig }
import models.HealthStatus.Type
import models.{ HealthStatus, Roles }

@Singleton
class CortexCtrl @Inject() (
reportTemplateCtrl: ReportTemplateCtrl,
cortexConfig: CortexConfig,
cortexSrv: CortexSrv,
cortexAnalyzerSrv: CortexAnalyzerSrv,
cortexActionSrv: CortexActionSrv,
auxSrv: AuxSrv,
authenticated: Authenticated,
fieldsBodyParser: FieldsBodyParser,
Expand Down Expand Up @@ -71,9 +71,22 @@ class CortexCtrl @Inject() (
case GET(p"/job/$jobId<[^/]*>") getJob(jobId)
case POST(p"/job/_search") findJob
case POST(p"/job/_stats") statsJob

case GET(p"/analyzer/$analyzerId<[^/]*>") getAnalyzer(analyzerId)
case GET(p"/analyzer/type/$dataType<[^/]*>") getAnalyzerFor(dataType)
case GET(p"/analyzer") listAnalyzer

case GET(p"/worker/$workerId<[^/]*>") getWorker(workerId)
case GET(p"/worker") findWorker
case POST(p"/worker/_search") findWorker
case GET(p"/worker/$entityType<[^/]*>/$entityId<[^/]*>") getWorkers(entityType, entityId)

case POST(p"/action") createAction
case GET(p"/action") findAction
case POST(p"/action/_search") findAction
case GET(p"/action/$entityType<[^/]*>/$entityId<[^/]*>") getActions(entityType, entityId)
case GET(p"/action/$actionId<[^/]*>") getAction(actionId)

case POST(p"/report/template/_search") reportTemplateCtrl.find()
case POST(p"/report/template") reportTemplateCtrl.create()
case GET(p"/report/template/$caseTemplateId<[^/]*>") reportTemplateCtrl.get(caseTemplateId)
Expand All @@ -89,7 +102,7 @@ class CortexCtrl @Inject() (
val analyzerId = request.body.getString("analyzerId").getOrElse(throw BadRequestError(s"analyzerId is missing"))
val artifactId = request.body.getString("artifactId").getOrElse(throw BadRequestError(s"artifactId is missing"))
val cortexId = request.body.getString("cortexId")
cortexSrv.submitJob(cortexId, analyzerId, artifactId).map { job
cortexAnalyzerSrv.submitJob(cortexId, analyzerId, artifactId).map { job
renderer.toOutput(OK, job)
}
}
Expand All @@ -98,9 +111,9 @@ class CortexCtrl @Inject() (
def getJob(jobId: String): Action[Fields] = authenticated(Roles.read).async(fieldsBodyParser) { implicit request
val withStats = request.body.getBoolean("nstats").getOrElse(false)
for {
job cortexSrv.getJob(jobId)
job cortexAnalyzerSrv.getJob(jobId)
jobJson = job.toJson
jobWithStats if (withStats) cortexSrv.addImportFieldInArtifacts(jobJson) else Future.successful(Json.toJson(job))
jobWithStats if (withStats) cortexAnalyzerSrv.addImportFieldInArtifacts(jobJson) else Future.successful(Json.toJson(job))
} yield Ok(jobWithStats)
}

Expand All @@ -112,7 +125,7 @@ class CortexCtrl @Inject() (
val nparent = request.body.getLong("nparent").getOrElse(0L).toInt
val withStats = request.body.getBoolean("nstats").getOrElse(false)

val (jobs, total) = cortexSrv.find(query, range, sort)
val (jobs, total) = cortexAnalyzerSrv.find(query, range, sort)
val jobWithoutReport = auxSrv.apply(jobs, nparent, withStats, removeUnaudited = true)
renderer.toOutput(OK, jobWithoutReport, total)
}
Expand All @@ -123,27 +136,80 @@ class CortexCtrl @Inject() (
.fold[QueryDef](QueryDSL.any)(_.as[QueryDef])
val aggs = request.body.getValue("stats")
.getOrElse(throw BadRequestError("Parameter \"stats\" is missing")).as[Seq[Agg]]
cortexSrv.stats(query, aggs).map(s Ok(s))
cortexAnalyzerSrv.stats(query, aggs).map(s Ok(s))
}

@Timed
def getAnalyzer(analyzerId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request
cortexSrv.getAnalyzer(analyzerId).map { analyzer
cortexAnalyzerSrv.getAnalyzer(analyzerId).map { analyzer
renderer.toOutput(OK, analyzer)
}
}

@Timed
def getAnalyzerFor(dataType: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request
cortexSrv.getAnalyzersFor(dataType).map { analyzers
cortexAnalyzerSrv.getAnalyzersFor(dataType).map { analyzers
renderer.toOutput(OK, analyzers)
}
}

@Timed
def listAnalyzer: Action[AnyContent] = authenticated(Roles.read).async { implicit request
cortexSrv.listAnalyzer.map { analyzers
cortexAnalyzerSrv.listAnalyzer.map { analyzers
renderer.toOutput(OK, analyzers)
}
}

def getWorker(workerId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request
cortexActionSrv.getWorkerById(workerId).map { worker
renderer.toOutput(OK, worker)
}
}

def getWorkers(entityType: String, entityId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request
val query = Json.obj(
"dataTypeList" -> s"thehive:$entityType")
cortexActionSrv.findWorkers(query).map { workers
renderer.toOutput(OK, workers)
}
}

def findWorker: Action[Fields] = authenticated(Roles.read).async(fieldsBodyParser) { implicit request
val query = request.body.getValue("query") match {
case Some(o: JsObject) o
case _ JsObject.empty
}
cortexActionSrv.findWorkers(query).map { workers
renderer.toOutput(OK, workers)
}
}

def createAction: Action[Fields] = authenticated(Roles.write).async(fieldsBodyParser) { implicit request
cortexActionSrv.executeAction(request.body).map { action
renderer.toOutput(OK, action)
}
}

def findAction: Action[Fields] = authenticated(Roles.read).async(fieldsBodyParser) { implicit request
val query = request.body.getValue("query").fold[QueryDef](QueryDSL.any)(_.as[QueryDef])
val range = request.body.getString("range")
val sort = request.body.getStrings("sort").getOrElse(Nil)

val (actions, total) = cortexActionSrv.find(query, range, sort)
renderer.toOutput(OK, actions, total)
}

def getActions(entityType: String, entityId: String): Action[Fields] = authenticated(Roles.read).async(fieldsBodyParser) { implicit request
import org.elastic4play.services.QueryDSL._
val range = request.body.getString("range")
val sort = request.body.getStrings("sort").getOrElse(Nil)
val (actions, total) = cortexActionSrv.find(and("objectType" ~= entityType, "objectId" ~= entityId), range, sort)
renderer.toOutput(OK, actions, total)
}

def getAction(actionId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request
cortexActionSrv.getAction(actionId).map { action
renderer.toOutput(OK, action)
}
}
}
38 changes: 38 additions & 0 deletions thehive-cortex/app/connectors/cortex/models/Action.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package connectors.cortex.models

import java.util.Date

import javax.inject.{ Inject, Singleton }

import scala.concurrent.Future
import play.api.libs.json.JsObject
import org.elastic4play.JsonFormat.dateFormat
import org.elastic4play.models.{ AttributeDef, BaseEntity, EntityDef, ModelDef, AttributeFormat F, AttributeOption O }
import org.elastic4play.utils.RichJson
import connectors.cortex.models.JsonFormat.jobStatusFormat
import services.AuditedModel

trait ActionAttributes { _: AttributeDef
val workerId = attribute("workerId", F.stringFmt, "Analyzer", O.readonly)
val workerName = optionalAttribute("workerName", F.stringFmt, "Name of the worker", O.readonly)
val workerDefinition = optionalAttribute("workerDefinition", F.stringFmt, "Name of the worker definition", O.readonly)
val status = attribute("status", F.enumFmt(JobStatus), "Status of the action job", JobStatus.InProgress)
val objectType = attribute("objectType", F.stringFmt, "Type of the object on which this job was executed")
val objectId = attribute("objectId", F.stringFmt, "Object ID on which this job was executed", O.readonly)
val startDate = attribute("startDate", F.dateFmt, "Timestamp of the job start") // , O.model)
val endDate = optionalAttribute("endDate", F.dateFmt, "Timestamp of the job completion (or fail)")
val report = optionalAttribute("report", F.textFmt, "Action output", O.unaudited)
val cortexId = optionalAttribute("cortexId", F.stringFmt, "Id of cortex where the job is run", O.readonly)
val cortexJobId = optionalAttribute("cortexJobId", F.stringFmt, "Id of job in cortex", O.readonly)
val operations = multiAttribute("operations", F.textFmt, "Update operations applied at the end of the job", O.readonly)
}
@Singleton
class ActionModel @Inject() extends ModelDef[ActionModel, Action]("action", "Action", "/connector/cortex/action") with ActionAttributes with AuditedModel {

override def creationHook(parent: Option[BaseEntity], attrs: JsObject): Future[JsObject] = Future.successful {
attrs
.setIfAbsent("status", JobStatus.InProgress)
.setIfAbsent("startDate", new Date)
}
}
class Action(model: ActionModel, attributes: JsObject) extends EntityDef[ActionModel, Action](model, attributes) with ActionAttributes
5 changes: 1 addition & 4 deletions thehive-cortex/app/connectors/cortex/models/Job.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,4 @@ case class CortexJob(
analyzerDefinition: String,
artifact: CortexArtifact,
date: Date,
status: JobStatus.Type,
cortexIds: List[String] = Nil) {
def onCortex(cortexId: String) = copy(cortexIds = cortexId :: cortexIds)
}
status: JobStatus.Type)
29 changes: 26 additions & 3 deletions thehive-cortex/app/connectors/cortex/models/JsonFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ object JsonFormat {
description (json \ "description").validate[String]
dataTypeList (json \ "dataTypeList").validate[Seq[String]]
} yield Analyzer(id, renamed, version, description, dataTypeList))
implicit val analyzerFormats: Format[Analyzer] = Format(analyzerReads, analyzerWrites)
implicit val analyzerFormat: Format[Analyzer] = Format(analyzerReads, analyzerWrites)

private val fileArtifactWrites = OWrites[FileArtifact](fileArtifact Json.obj(
"attributes" fileArtifact.attributes))
Expand Down Expand Up @@ -53,7 +53,7 @@ object JsonFormat {
JsObject(attributes.flatMap(a (json \ a).asOpt[JsValue].map(a -> _)))
}

implicit val cortexJobReads = Reads[CortexJob](json
implicit val cortexJobReads: Reads[CortexJob] = Reads[CortexJob](json
for {
id (json \ "id").validate[String]
analyzerId (json \ "analyzerId").validate[String]
Expand All @@ -67,7 +67,30 @@ object JsonFormat {
}
date (json \ "date").validate[Date]
status (json \ "status").validate[JobStatus.Type]
} yield CortexJob(id, analyzerId, analyzerName, analyzerDefinition, artifact, date, status, Nil))
} yield CortexJob(id, analyzerId, analyzerName, analyzerDefinition, artifact, date, status))

implicit val reportTypeFormat: Format[ReportType.Type] = enumFormat(ReportType)

private val workerWrites = Writes[Worker](worker Json.obj(
"id" worker.id,
"name" worker.name,
"version" worker.version,
"description" worker.description,
"dataTypeList" worker.dataTypeList,
"maxTlp" -> worker.maxTlp,
"maxPap" -> worker.maxPap,
"cortexIds" worker.cortexIds))
private val workerReads = Reads[Worker](json
for {
name (json \ "name").validate[String]
version (json \ "version").validate[String]
definition = (name + "_" + version).replaceAll("\\.", "_")
id = (json \ "id").asOpt[String].getOrElse(definition)
renamed = if (id == definition) definition else name
description (json \ "description").validate[String]
dataTypeList (json \ "dataTypeList").validate[Seq[String]]
maxTlp (json \ "maxTlp").validateOpt[Long]
maxPap (json \ "maxPap").validateOpt[Long]
} yield Worker(id, renamed, version, description, dataTypeList, maxTlp, maxPap))
implicit val workerFormat: Format[Worker] = Format(workerReads, workerWrites)
}
16 changes: 16 additions & 0 deletions thehive-cortex/app/connectors/cortex/models/Worker.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package connectors.cortex.models

case class Worker(
id: String,
name: String,
version: String,
description: String,
dataTypeList: Seq[String],
maxTlp: Option[Long],
maxPap: Option[Long],
cortexIds: List[String] = Nil) {

def addCortexId(cid: String): Worker = copy(cortexIds = cid :: cortexIds)

def join(worker: Worker): Worker = copy(cortexIds = cortexIds ::: worker.cortexIds)
}
Loading

0 comments on commit 9d5c3bb

Please sign in to comment.