Skip to content

Commit

Permalink
#1454 Add APIs for task, observable and logs
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Dec 10, 2020
1 parent d542bd1 commit 1d507aa
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 61 deletions.
44 changes: 22 additions & 22 deletions thehive/app/org/thp/thehive/controllers/v0/Router.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@ import play.api.routing.sird._

@Singleton
class Router @Inject() (
statsCtrl: StatsCtrl,
authenticationCtrl: AuthenticationCtrl,
alertCtrl: AlertCtrl,
attachmentCtrl: AttachmentCtrl,
auditCtrl: AuditCtrl,
caseCtrl: CaseCtrl,
caseTemplateCtrl: CaseTemplateCtrl,
userCtrl: UserCtrl,
organisationCtrl: OrganisationCtrl,
taskCtrl: TaskCtrl,
logCtrl: LogCtrl,
observableCtrl: ObservableCtrl,
configCtrl: ConfigCtrl,
customFieldCtrl: CustomFieldCtrl,
alertCtrl: AlertCtrl,
auditCtrl: AuditCtrl,
statusCtrl: StatusCtrl,
dashboardCtrl: DashboardCtrl,
authenticationCtrl: AuthenticationCtrl,
listCtrl: ListCtrl,
streamCtrl: StreamCtrl,
attachmentCtrl: AttachmentCtrl,
describeCtrl: DescribeCtrl,
configCtrl: ConfigCtrl,
profileCtrl: ProfileCtrl,
shareCtrl: ShareCtrl,
tagCtrl: TagCtrl,
listCtrl: ListCtrl,
logCtrl: LogCtrl,
observableCtrl: ObservableCtrl,
organisationCtrl: OrganisationCtrl,
observableTypeCtrl: ObservableTypeCtrl,
pageCtrl: PageCtrl,
permissionCtrl: PermissionCtrl,
observableTypeCtrl: ObservableTypeCtrl
profileCtrl: ProfileCtrl,
taskCtrl: TaskCtrl,
shareCtrl: ShareCtrl,
statsCtrl: StatsCtrl,
userCtrl: UserCtrl,
statusCtrl: StatusCtrl,
streamCtrl: StreamCtrl,
tagCtrl: TagCtrl
) extends SimpleRouter {

override def routes: Routes = {
Expand Down Expand Up @@ -86,11 +86,11 @@ class Router @Inject() (
case POST(p"/case/artifact/$observableId/shares") => shareCtrl.shareObservable(observableId)

case GET(p"/case") => caseCtrl.search
case POST(p"/case") => caseCtrl.create // Audit ok
case POST(p"/case") => caseCtrl.create // Audit ok
case GET(p"/case/$caseId") => caseCtrl.get(caseId)
case PATCH(p"/case/_bulk") => caseCtrl.bulkUpdate // Not used by the frontend
case PATCH(p"/case/$caseId") => caseCtrl.update(caseId) // Audit ok
case POST(p"/case/_merge/$caseIds") => caseCtrl.merge(caseIds) // Not implemented in backend and not used by frontend
case PATCH(p"/case/_bulk") => caseCtrl.bulkUpdate // Not used by the frontend
case PATCH(p"/case/$caseId") => caseCtrl.update(caseId) // Audit ok
case POST(p"/case/_merge/$caseIds") => caseCtrl.merge(caseIds) // Not implemented in backend and not used by frontend
case POST(p"/case/_search") => caseCtrl.search
case POST(p"/case/_stats") => caseCtrl.stats
case DELETE(p"/case/$caseId") => caseCtrl.delete(caseId) // Not used by the frontend
Expand Down
97 changes: 65 additions & 32 deletions thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.thp.thehive.controllers.v1

import java.io.FilterInputStream
import java.nio.file.Files

import javax.inject.{Inject, Named, Singleton}
import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.model.FileHeader
Expand All @@ -22,22 +21,24 @@ import org.thp.thehive.services.OrganisationOps._
import org.thp.thehive.services.ShareOps._
import org.thp.thehive.services._
import play.api.libs.Files.DefaultTemporaryFileCreator
import play.api.libs.json.{JsArray, JsValue, Json}
import play.api.mvc.{Action, AnyContent, Results}
import play.api.{Configuration, Logger}

import scala.collection.JavaConverters._

@Singleton
class ObservableCtrl @Inject() (
entryPoint: Entrypoint,
entrypoint: Entrypoint,
@Named("with-thehive-schema") db: Database,
properties: Properties,
observableSrv: ObservableSrv,
observableTypeSrv: ObservableTypeSrv,
caseSrv: CaseSrv,
organisationSrv: OrganisationSrv,
temporaryFileCreator: DefaultTemporaryFileCreator,
configuration: Configuration
configuration: Configuration,
errorHandler: ErrorHandler
) extends QueryableCtrl
with ObservableRenderer {

Expand Down Expand Up @@ -79,41 +80,73 @@ class ObservableCtrl @Inject() (
)

def create(caseId: String): Action[AnyContent] =
entryPoint("create artifact")
.extract("artifact", FieldsParser[InputObservable])
entrypoint("create observable")
.extract("observable", FieldsParser[InputObservable])
.extract("isZip", FieldsParser.boolean.optional.on("isZip"))
.extract("zipPassword", FieldsParser.string.optional.on("zipPassword"))
.authTransaction(db) { implicit request => implicit graph =>
.auth { implicit request =>
val inputObservable: InputObservable = request.body("observable")
val isZip: Option[Boolean] = request.body("isZip")
val zipPassword: Option[String] = request.body("zipPassword")
val inputObservable: InputObservable = request.body("artifact")
val inputAttachObs = if (isZip.contains(true)) getZipFiles(inputObservable, zipPassword) else Seq(inputObservable)
for {
case0 <-
caseSrv
.get(EntityIdOrName(caseId))
.can(Permissions.manageObservable)
.getOrFail("Case")
observableType <- observableTypeSrv.getOrFail(EntityName(inputObservable.dataType))
observablesWithData <-
inputObservable
.data
.toTry(d => observableSrv.create(inputObservable.toObservable, observableType, d, inputObservable.tags, Nil))
observableWithAttachment <- inputAttachObs.toTry(
_.attachment
.map(a => observableSrv.create(inputObservable.toObservable, observableType, a, inputObservable.tags, Nil))
.flip
)
createdObservables <- (observablesWithData ++ observableWithAttachment.flatten).toTry { richObservables =>
caseSrv
.addObservable(case0, richObservables)
.map(_ => richObservables)

db
.roTransaction { implicit graph =>
for {
case0 <-
caseSrv
.get(EntityIdOrName(caseId))
.can(Permissions.manageObservable)
.orFail(AuthorizationError("Operation not permitted"))
observableType <- observableTypeSrv.getOrFail(EntityName(inputObservable.dataType))
} yield (case0, observableType)
}
.map {
case (case0, observableType) =>
val initialSuccessesAndFailures: (Seq[JsValue], Seq[JsValue]) =
inputAttachObs.foldLeft[(Seq[JsValue], Seq[JsValue])](Nil -> Nil) {
case ((successes, failures), inputObservable) =>
inputObservable.attachment.fold((successes, failures)) { attachment =>
db
.tryTransaction { implicit graph =>
observableSrv
.create(inputObservable.toObservable, observableType, attachment, inputObservable.tags, Nil)
.flatMap(o => caseSrv.addObservable(case0, o).map(_ => o.toJson))
}
.fold(
e =>
successes -> (failures :+ errorHandler.toErrorResult(e)._2 ++ Json
.obj(
"object" -> Json
.obj("data" -> s"file:${attachment.filename}", "attachment" -> Json.obj("name" -> attachment.filename))
)),
s => (successes :+ s) -> failures
)
}
}

val (successes, failures) = inputObservable
.data
.foldLeft(initialSuccessesAndFailures) {
case ((successes, failures), data) =>
db
.tryTransaction { implicit graph =>
observableSrv
.create(inputObservable.toObservable, observableType, data, inputObservable.tags, Nil)
.flatMap(o => caseSrv.addObservable(case0, o).map(_ => o.toJson))
}
.fold(
failure => (successes, failures :+ errorHandler.toErrorResult(failure)._2 ++ Json.obj("object" -> Json.obj("data" -> data))),
success => (successes :+ success, failures)
)
}
if (failures.isEmpty) Results.Created(JsArray(successes))
else Results.MultiStatus(Json.obj("success" -> successes, "failure" -> failures))
}
} yield Results.Created(createdObservables.toJson)
}

def get(observableId: String): Action[AnyContent] =
entryPoint("get observable")
entrypoint("get observable")
.authRoTransaction(db) { _ => implicit graph =>
observableSrv
.get(EntityIdOrName(observableId))
Expand All @@ -126,7 +159,7 @@ class ObservableCtrl @Inject() (
}

def update(observableId: String): Action[AnyContent] =
entryPoint("update observable")
entrypoint("update observable")
.extract("observable", FieldsParser.update("observable", publicProperties))
.authTransaction(db) { implicit request => implicit graph =>
val propertyUpdaters: Seq[PropertyUpdater] = request.body("observable")
Expand All @@ -139,7 +172,7 @@ class ObservableCtrl @Inject() (
}

def bulkUpdate: Action[AnyContent] =
entryPoint("bulk update")
entrypoint("bulk update")
.extract("input", FieldsParser.update("observable", publicProperties))
.extract("ids", FieldsParser.seq[String].on("ids"))
.authTransaction(db) { implicit request => implicit graph =>
Expand All @@ -154,7 +187,7 @@ class ObservableCtrl @Inject() (
}

def delete(obsId: String): Action[AnyContent] =
entryPoint("delete")
entrypoint("delete")
.authTransaction(db) { implicit request => implicit graph =>
for {
observable <-
Expand Down
46 changes: 39 additions & 7 deletions thehive/app/org/thp/thehive/controllers/v1/Router.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,29 @@ import play.api.routing.sird._

@Singleton
class Router @Inject() (
authenticationCtrl: AuthenticationCtrl,
alertCtrl: AlertCtrl,
// attachmentCtrl: AttachmentCtrl,
auditCtrl: AuditCtrl,
caseCtrl: CaseCtrl,
caseTemplateCtrl: CaseTemplateCtrl,
userCtrl: UserCtrl,
// configCtrl: ConfigCtrl,
customFieldCtrl: CustomFieldCtrl,
// dashboardCtrl: DashboardCtrl,
describeCtrl: DescribeCtrl,
logCtrl: LogCtrl,
observableCtrl: ObservableCtrl,
observableTypeCtrl: ObservableTypeCtrl,
organisationCtrl: OrganisationCtrl,
// pageCtrl: PageCtrl,
// permissionCtrl: PermissionCtrl,
profileCtrl: ProfileCtrl,
taskCtrl: TaskCtrl,
customFieldCtrl: CustomFieldCtrl,
alertCtrl: AlertCtrl,
auditCtrl: AuditCtrl,
statusCtrl: StatusCtrl,
authenticationCtrl: AuthenticationCtrl,
describeCtrl: DescribeCtrl
// shareCtrl: ShareCtrl,
userCtrl: UserCtrl,
statusCtrl: StatusCtrl
// streamCtrl: StreamCtrl,
// tagCtrl: TagCtrl
) extends SimpleRouter {

override def routes: Routes = {
Expand All @@ -40,6 +52,14 @@ class Router @Inject() (
// case POST(p"/case/_stats") => caseCtrl.stats()
// case GET(p"/case/$caseId/links") => caseCtrl.linkedCases(caseId)

case POST(p"/case/$caseId/observable") => observableCtrl.create(caseId)
case GET(p"/observable/$observableId") => observableCtrl.get(observableId)
case DELETE(p"/observable/$observableId") => observableCtrl.delete(observableId)
case PATCH(p"/observable/_bulk") => observableCtrl.bulkUpdate
case PATCH(p"/observable/$observableId") => observableCtrl.update(observableId)
// case GET(p"/observable/$observableId/similar") => observableCtrl.findSimilar(observableId)
// case POST(p"/observable/$observableId/shares") => shareCtrl.shareObservable(observableId)

case GET(p"/caseTemplate") => caseTemplateCtrl.list
case POST(p"/caseTemplate") => caseTemplateCtrl.create
case GET(p"/caseTemplate/$caseTemplateId") => caseTemplateCtrl.get(caseTemplateId)
Expand Down Expand Up @@ -75,6 +95,10 @@ class Router @Inject() (
// POST /case/:caseId/task/_search controllers.TaskCtrl.findInCase(caseId)
// POST /case/task/_stats controllers.TaskCtrl.stats()

case POST(p"/task/$taskId/log") => logCtrl.create(taskId)
case PATCH(p"/log/$logId") => logCtrl.update(logId)
case DELETE(p"/log/$logId") => logCtrl.delete(logId)

case GET(p"/customField") => customFieldCtrl.list
case POST(p"/customField") => customFieldCtrl.create

Expand All @@ -96,8 +120,16 @@ class Router @Inject() (
// POST /audit/_search controllers.AuditCtrl.find()
// POST /audit/_stats controllers.AuditCtrl.stats()

case POST(p"/profile") => profileCtrl.create
case GET(p"/profile/$profileId") => profileCtrl.get(profileId)
case PATCH(p"/profile/$profileId") => profileCtrl.update(profileId)
case DELETE(p"/profile/$profileId") => profileCtrl.delete(profileId)

case GET(p"/describe/_all") => describeCtrl.describeAll
case GET(p"/describe/$modelName") => describeCtrl.describe(modelName)

case GET(p"/observable/type/$idOrName") => observableTypeCtrl.get(idOrName)
case POST(p"/observable/type") => observableTypeCtrl.create
case DELETE(p"/observable/type/$idOrName") => observableTypeCtrl.delete(idOrName)
}
}

0 comments on commit 1d507aa

Please sign in to comment.