Skip to content

Commit

Permalink
#1344 Add _child filter operator
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed May 20, 2020
1 parent aad8f31 commit fd3b07a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package org.thp.thehive.connector.cortex.controllers.v0

import scala.reflect.runtime.{currentMirror => rm, universe => ru}

import javax.inject.{Inject, Singleton}
import org.scalactic.Accumulation._
import org.scalactic.Good
import org.thp.scalligraph.BadRequestError
import org.thp.scalligraph.auth.AuthContext
import org.thp.scalligraph.controllers.{FSeq, FieldsParser}
import org.thp.scalligraph.controllers.FieldsParser
import org.thp.scalligraph.models._
import org.thp.scalligraph.query.InputFilter.{and, not, or}
import org.thp.scalligraph.query._
import org.thp.scalligraph.steps.StepsOps._
import org.thp.scalligraph.steps.{BaseTraversal, BaseVertexSteps}
import org.thp.thehive.connector.cortex.services.JobSteps
import org.thp.thehive.connector.cortex.services.{JobSteps, RichObservableSteps}
import org.thp.thehive.controllers.v0._
import org.thp.thehive.services.ObservableSteps

import scala.reflect.runtime.{universe => ru}

@Singleton
class CortexQueryExecutor @Inject() (
jobCtrl: JobCtrl,
Expand All @@ -27,8 +26,7 @@ class CortexQueryExecutor @Inject() (
override lazy val publicProperties: List[PublicProperty[_, _]] =
jobCtrl.publicProperties ++ reportCtrl.publicProperties ++ actionCtrl.publicProperties
override lazy val queries: Seq[ParamQuery[_]] =
new CortexParentFilterQuery(db, publicProperties) ::
actionCtrl.initialQuery ::
actionCtrl.initialQuery ::
actionCtrl.pageQuery ::
actionCtrl.outputQuery ::
jobCtrl.initialQuery ::
Expand All @@ -38,13 +36,43 @@ class CortexQueryExecutor @Inject() (
reportCtrl.pageQuery ::
reportCtrl.outputQuery ::
Nil
override lazy val filterQuery = new CortexParentFilterQuery(db, publicProperties)

val childTypes: PartialFunction[(ru.Type, String), ru.Type] = {
case (tpe, "case_artifact_job") if SubType(tpe, ru.typeOf[ObservableSteps]) => ru.typeOf[ObservableSteps]
}
val parentTypes: PartialFunction[ru.Type, ru.Type] = {
case tpe if SubType(tpe, ru.typeOf[JobSteps]) => ru.typeOf[ObservableSteps]
}

override val customFilterQuery: FilterQuery = FilterQuery(db, publicProperties) { (tpe, globalParser) =>
FieldsParser("parentChildFilter") {
case (_, FObjOne("_parent", ParentIdFilter(_, parentId))) if parentTypes.isDefinedAt(tpe) =>
Good(new CortexParentIdInputFilter(parentId))
case (path, FObjOne("_parent", ParentQueryFilter(_, parentFilterField))) if parentTypes.isDefinedAt(tpe) =>
globalParser(parentTypes(tpe)).apply(path, parentFilterField).map(query => new CortexParentQueryInputFilter(query))
case (path, FObjOne("_child", ChildQueryFilter(childType, childQueryField))) if childTypes.isDefinedAt((tpe, childType)) =>
globalParser(childTypes((tpe, childType))).apply(path, childQueryField).map(query => new CortexChildQueryInputFilter(childType, query))
}
}

override val version: (Int, Int) = 0 -> 0
val job: QueryCtrl = queryCtrlBuilder.apply(jobCtrl, this)
val report: QueryCtrl = queryCtrlBuilder.apply(reportCtrl, this)
val action: QueryCtrl = queryCtrlBuilder.apply(actionCtrl, this)
}

class CortexParentIdInputFilter(parentId: String) extends InputFilter {
override def apply[S <: BaseTraversal](
db: Database,
publicProperties: List[PublicProperty[_, _]],
stepType: ru.Type,
step: S,
authContext: AuthContext
): S =
if (stepType =:= ru.typeOf[JobSteps]) step.asInstanceOf[JobSteps].filter(_.observable.getByIds(parentId)).asInstanceOf[S]
else throw BadRequestError(s"$stepType hasn't parent")
}

/**
* The parent query parser traversing properly to appropriate parent
*
Expand All @@ -60,37 +88,18 @@ class CortexParentQueryInputFilter(parentFilter: InputFilter) extends InputFilte
): S =
if (stepType =:= ru.typeOf[JobSteps])
step.filter(t => parentFilter.apply(db, publicProperties, ru.typeOf[ObservableSteps], t.asInstanceOf[JobSteps].observable, authContext))
else ???
else throw BadRequestError(s"$stepType hasn't parent")
}

/**
* Query parser for parent traversing heavily relaying on the v0/TheHiveQueryExecutor ParentFilterQuery and dependencies
*
* @param publicProperties the models properties
*/
class CortexParentFilterQuery(db: Database, publicProperties: List[PublicProperty[_, _]]) extends FilterQuery(db, publicProperties) {
override val name: String = "filter"

override def paramParser(tpe: ru.Type, properties: Seq[PublicProperty[_, _]]): FieldsParser[InputFilter] =
FieldsParser("parentIdFilter") {
case (path, FObjOne("_and", FSeq(fields))) =>
fields.zipWithIndex.validatedBy { case (field, index) => paramParser(tpe, properties)((path :/ "_and").toSeq(index), field) }.map(and)
case (path, FObjOne("_or", FSeq(fields))) =>
fields.zipWithIndex.validatedBy { case (field, index) => paramParser(tpe, properties)((path :/ "_or").toSeq(index), field) }.map(or)
case (path, FObjOne("_not", field)) => paramParser(tpe, properties)(path :/ "_not", field).map(not)
case (_, FObjOne("_parent", ParentIdFilter(_, parentId))) => Good(new ParentIdInputFilter(parentId))
case (path, FObjOne("_parent", ParentQueryFilter(_, queryField))) =>
paramParser(tpe, properties).apply(path, queryField).map(query => new CortexParentQueryInputFilter(query))
}.orElse(InputFilter.fieldsParser(tpe, properties))

override def checkFrom(t: ru.Type): Boolean = SubType(t, ru.typeOf[JobSteps])
override def toType(t: ru.Type): ru.Type = t
override def apply(inputFilter: InputFilter, from: Any, authContext: AuthContext): Any =
inputFilter(
db,
publicProperties,
rm.classSymbol(from.getClass).toType,
from.asInstanceOf[BaseVertexSteps],
authContext
)
class CortexChildQueryInputFilter(childType: String, childFilter: InputFilter) extends InputFilter {
override def apply[S <: BaseVertexSteps](
db: Database,
publicProperties: List[PublicProperty[_, _]],
stepType: ru.Type,
step: S,
authContext: AuthContext
): S =
if (stepType =:= ru.typeOf[ObservableSteps] && childType == "case_artifact_job")
step.filter(t => childFilter.apply(db, publicProperties, ru.typeOf[JobSteps], t.asInstanceOf[ObservableSteps].jobs, authContext))
else throw BadRequestError(s"$stepType hasn't child of type $childType")
}
6 changes: 3 additions & 3 deletions thehive/app/org/thp/thehive/controllers/v0/QueryCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ class QueryCtrl(entrypoint: Entrypoint, db: Database, ctrl: QueryableCtrl, query
lazy val logger: Logger = Logger(getClass)

val publicProperties: List[PublicProperty[_, _]] = queryExecutor.publicProperties
val filterQuery: FilterQuery = new FilterQuery(db, publicProperties)
val filterQuery: FilterQuery = queryExecutor.filterQuery
val queryType: ru.Type = ctrl.initialQuery.toType(ru.typeOf[Graph])

val inputFilterParser: FieldsParser[InputFilter] = queryExecutor
.filterQuery
.paramParser(queryType, publicProperties)
.paramParser(queryType)

val aggregationParser: FieldsParser[GroupAggregation[_, _, _]] = queryExecutor
.aggregationQuery
.paramParser(queryType, publicProperties)
.paramParser(queryType)

val sortParser: FieldsParser[InputSort] = FieldsParser("sort") {
case (_, FAny(s)) => Good(s)
Expand Down
101 changes: 52 additions & 49 deletions thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package org.thp.thehive.controllers.v0

import javax.inject.{Inject, Singleton}
import org.scalactic.Accumulation._
import org.scalactic.Good
import org.thp.scalligraph.BadRequestError
import org.thp.scalligraph.auth.AuthContext
import org.thp.scalligraph.controllers.{FSeq, Field, FieldsParser}
import org.thp.scalligraph.controllers.{Field, FieldsParser}
import org.thp.scalligraph.models._
import org.thp.scalligraph.query.{InputFilter, _}
import org.thp.scalligraph.steps.StepsOps._
import org.thp.scalligraph.steps.{BaseTraversal, BaseVertexSteps}
import org.thp.thehive.services.{ObservableSteps, _}

import scala.reflect.runtime.{currentMirror => rm, universe => ru}
import scala.reflect.runtime.{universe => ru}

case class OutputParam(from: Long, to: Long, withStats: Boolean, withParents: Int)

Expand All @@ -38,7 +38,27 @@ class TheHiveQueryExecutor @Inject() (
lazy val controllers: List[QueryableCtrl] =
caseCtrl :: taskCtrl :: logCtrl :: observableCtrl :: alertCtrl :: userCtrl :: caseTemplateCtrl :: dashboardCtrl :: organisationCtrl :: auditCtrl :: profileCtrl :: tagCtrl :: pageCtrl :: observableTypeCtrl :: Nil
override lazy val publicProperties: List[PublicProperty[_, _]] = controllers.flatMap(_.publicProperties)
override lazy val filterQuery = new ParentFilterQuery(db, publicProperties)

val childTypes: PartialFunction[(ru.Type, String), ru.Type] = {
case (tpe, "case_task_log") if SubType(tpe, ru.typeOf[TaskSteps]) => ru.typeOf[LogSteps]
case (tpe, "case_task") if SubType(tpe, ru.typeOf[CaseSteps]) => ru.typeOf[TaskSteps]
case (tpe, "case_artifact") if SubType(tpe, ru.typeOf[CaseSteps]) => ru.typeOf[ObservableSteps]
}
val parentTypes: PartialFunction[ru.Type, ru.Type] = {
case tpe if SubType(tpe, ru.typeOf[TaskSteps]) => ru.typeOf[CaseSteps]
case tpe if SubType(tpe, ru.typeOf[ObservableSteps]) => ru.typeOf[CaseSteps]
case tpe if SubType(tpe, ru.typeOf[LogSteps]) => ru.typeOf[ObservableSteps]
}
override val customFilterQuery: FilterQuery = FilterQuery(db, publicProperties) { (tpe, globalParser) =>
FieldsParser.debug("parentChildFilter") {
case (_, FObjOne("_parent", ParentIdFilter(_, parentId))) if parentTypes.isDefinedAt(tpe) =>
Good(new ParentIdInputFilter(parentId))
case (path, FObjOne("_parent", ParentQueryFilter(_, parentFilterField))) if parentTypes.isDefinedAt(tpe) =>
globalParser(parentTypes(tpe)).apply(path, parentFilterField).map(query => new ParentQueryInputFilter(query))
case (path, FObjOne("_child", ChildQueryFilter(childType, childQueryField))) if childTypes.isDefinedAt((tpe, childType)) =>
globalParser(childTypes((tpe, childType))).apply(path, childQueryField).map(query => new ChildQueryInputFilter(childType, query))
}
}

override lazy val queries: Seq[ParamQuery[_]] =
controllers.map(_.initialQuery) :::
Expand Down Expand Up @@ -85,7 +105,7 @@ class ParentIdInputFilter(parentId: String) extends InputFilter {
if (stepType =:= ru.typeOf[TaskSteps]) step.asInstanceOf[TaskSteps].filter(_.`case`.getByIds(parentId)).asInstanceOf[S]
else if (stepType =:= ru.typeOf[ObservableSteps]) step.asInstanceOf[ObservableSteps].filter(_.`case`.getByIds(parentId)).asInstanceOf[S]
else if (stepType =:= ru.typeOf[LogSteps]) step.asInstanceOf[LogSteps].filter(_.task.getByIds(parentId)).asInstanceOf[S]
else ???
else throw BadRequestError(s"$stepType hasn't parent")
}

object ParentQueryFilter {
Expand Down Expand Up @@ -113,51 +133,34 @@ class ParentQueryInputFilter(parentFilter: InputFilter) extends InputFilter {
step.filter(t => parentFilter.apply(db, publicProperties, ru.typeOf[CaseSteps], t.asInstanceOf[ObservableSteps].`case`, authContext))
else if (stepType =:= ru.typeOf[LogSteps])
step.filter(t => parentFilter.apply(db, publicProperties, ru.typeOf[TaskSteps], t.asInstanceOf[LogSteps].task, authContext))
else ???

// vertexSteps
// .filter { s =>
// if (stepType =:= ru.typeOf[TaskSteps])
// parentFilter.apply(db, publicProperties, ru.typeOf[CaseSteps], new TaskSteps(s)(db, graph).`case`, authContext).raw
// else if (stepType =:= ru.typeOf[ObservableSteps])
// parentFilter.apply(db, publicProperties, ru.typeOf[CaseSteps], new ObservableSteps(s)(db, graph).`case`, authContext).raw
// else if (stepType =:= ru.typeOf[LogSteps])
// parentFilter.apply(db, publicProperties, ru.typeOf[TaskSteps], new LogSteps(s)(db, graph).task, authContext).raw
// else ???
// }
// .asInstanceOf[S]
else throw BadRequestError(s"$stepType hasn't parent")
}

class ParentFilterQuery(db: Database, publicProperties: List[PublicProperty[_, _]]) extends FilterQuery(db, publicProperties) {
override val name: String = "filter"

override def paramParser(tpe: ru.Type, properties: Seq[PublicProperty[_, _]]): FieldsParser[InputFilter] =
FieldsParser("parentIdFilter") {
case (path, FObjOne("_and", FSeq(fields))) =>
fields
.zipWithIndex
.validatedBy { case (field, index) => paramParser(tpe, properties)((path :/ "_and").toSeq(index), field) }
.map(InputFilter.and)
case (path, FObjOne("_or", FSeq(fields))) =>
fields
.zipWithIndex
.validatedBy { case (field, index) => paramParser(tpe, properties)((path :/ "_or").toSeq(index), field) }
.map(InputFilter.or)
case (path, FObjOne("_not", field)) => paramParser(tpe, properties)(path :/ "_not", field).map(InputFilter.not)
case (_, FObjOne("_parent", ParentIdFilter(_, parentId))) => Good(new ParentIdInputFilter(parentId))
case (path, FObjOne("_parent", ParentQueryFilter(_, queryField))) =>
paramParser(tpe, properties).apply(path, queryField).map(query => new ParentQueryInputFilter(query))
}.orElse(InputFilter.fieldsParser(tpe, properties))
object ChildQueryFilter {
def unapply(field: Field): Option[(String, Field)] =
FieldsParser
.string
.on("_type")
.map("childQuery")(childType => (childType, field.get("_query")))
.apply(field)
.fold(Some(_), _ => None)
}

override def checkFrom(t: ru.Type): Boolean =
SubType(t, ru.typeOf[TaskSteps]) || SubType(t, ru.typeOf[ObservableSteps]) || SubType(t, ru.typeOf[LogSteps])
override def toType(t: ru.Type): ru.Type = t
override def apply(inputFilter: InputFilter, from: Any, authContext: AuthContext): Any =
inputFilter(
db,
publicProperties,
rm.classSymbol(from.getClass).toType,
from.asInstanceOf[BaseVertexSteps],
authContext
)
class ChildQueryInputFilter(childType: String, childFilter: InputFilter) extends InputFilter {
override def apply[S <: BaseVertexSteps](
db: Database,
publicProperties: List[PublicProperty[_, _]],
stepType: ru.Type,
step: S,
authContext: AuthContext
): S =
if (stepType =:= ru.typeOf[CaseSteps] && childType == "case_task")
step.filter(t => childFilter.apply(db, publicProperties, ru.typeOf[TaskSteps], t.asInstanceOf[CaseSteps].tasks(authContext), authContext))
else if (stepType =:= ru.typeOf[CaseSteps] && childType == "case_artifact")
step.filter(t =>
childFilter.apply(db, publicProperties, ru.typeOf[ObservableSteps], t.asInstanceOf[CaseSteps].observables(authContext), authContext)
)
else if (stepType =:= ru.typeOf[TaskSteps] && childType == "case_task_log")
step.filter(t => childFilter.apply(db, publicProperties, ru.typeOf[LogSteps], t.asInstanceOf[TaskSteps].logs, authContext))
else throw BadRequestError(s"$stepType hasn't child of type $childType")
}

0 comments on commit fd3b07a

Please sign in to comment.