Skip to content

Commit

Permalink
#1817 Add dashboard controller for APIv1
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Mar 5, 2021
1 parent a973837 commit 425d16c
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 39 deletions.
2 changes: 2 additions & 0 deletions thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ class DescribeCtrl @Inject() (
)
)
)
case ("dashboard", "status") =>
Some(Seq(PropertyDescription("status", "enumeration", Seq(JsString("Shared"), JsString("Private"), JsString("Deleted")))))
case _ => None
}

Expand Down
108 changes: 108 additions & 0 deletions thehive/app/org/thp/thehive/controllers/v1/DashboardCtrl.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.thp.thehive.controllers.v1

import org.thp.scalligraph.EntityIdOrName
import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser}
import org.thp.scalligraph.models.Database
import org.thp.scalligraph.query._
import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.{IteratorOutput, Traversal}
import org.thp.thehive.controllers.v0.Conversion._
import org.thp.thehive.dto.v0.InputDashboard
import org.thp.thehive.models.{Dashboard, RichDashboard}
import org.thp.thehive.services.DashboardOps._
import org.thp.thehive.services.OrganisationOps._
import org.thp.thehive.services.UserOps._
import org.thp.thehive.services.{DashboardSrv, OrganisationSrv, UserSrv}
import play.api.mvc.{Action, AnyContent, Results}

import javax.inject.{Inject, Singleton}
import scala.util.Success

@Singleton
class DashboardCtrl @Inject() (
entrypoint: Entrypoint,
properties: Properties,
db: Database,
dashboardSrv: DashboardSrv,
userSrv: UserSrv,
organisationSrv: OrganisationSrv
) extends QueryableCtrl {

override val entityName: String = "dashboard"
override val publicProperties: PublicProperties = properties.dashboard
override val initialQuery: Query =
Query.init[Traversal.V[Dashboard]](
"listDashboard",
(graph, authContext) =>
graph
.union(
organisationSrv.filterTraversal(_).get(authContext.organisation).dashboards,
userSrv.filterTraversal(_).getByName(authContext.userId).dashboards
)
.dedup
)

override val getQuery: ParamQuery[EntityIdOrName] = Query.initWithParam[EntityIdOrName, Traversal.V[Dashboard]](
"getDashboard",
(idOrName, graph, authContext) => dashboardSrv.get(idOrName)(graph).visible(authContext)
)

override val pageQuery: ParamQuery[OutputParam] = Query.withParam[OutputParam, Traversal.V[Dashboard], IteratorOutput](
"page",
(range, dashboardSteps, _) => dashboardSteps.richPage(range.from, range.to, withTotal = true)(_.richDashboard)
)
override val outputQuery: Query = Query.output[RichDashboard, Traversal.V[Dashboard]](_.richDashboard)

def create: Action[AnyContent] =
entrypoint("create dashboard")
.extract("dashboard", FieldsParser[InputDashboard])
.authTransaction(db) { implicit request => implicit graph =>
val dashboard: InputDashboard = request.body("dashboard")
dashboardSrv
.create(dashboard.toDashboard)
.flatMap {
case richDashboard if dashboard.status == "Shared" =>
dashboardSrv
.share(richDashboard.dashboard, request.organisation, writable = false)
.flatMap(_ => dashboardSrv.get(richDashboard.dashboard).richDashboard.getOrFail("Dashboard"))
case richDashboard => Success(richDashboard)
}
.map(richDashboard => Results.Created(richDashboard.toJson))
}

def get(dashboardId: String): Action[AnyContent] =
entrypoint("get dashboard")
.authRoTransaction(db) { implicit request => implicit graph =>
dashboardSrv
.get(EntityIdOrName(dashboardId))
.visible
.richDashboard
.getOrFail("Dashboard")
.map(dashboard => Results.Ok(dashboard.toJson))
}

def update(dashboardId: String): Action[AnyContent] =
entrypoint("update dashboard")
.extract("dashboard", FieldsParser.update("dashboard", publicProperties))
.authTransaction(db) { implicit request => implicit graph =>
val propertyUpdaters: Seq[PropertyUpdater] = request.body("dashboard")
dashboardSrv
.update(_.get(EntityIdOrName(dashboardId)).canUpdate, propertyUpdaters) // TODO check permission
.flatMap { case (dashboardSteps, _) => dashboardSteps.richDashboard.getOrFail("Dashboard") }
.map(dashboard => Results.Ok(dashboard.toJson))
}

def delete(dashboardId: String): Action[AnyContent] =
entrypoint("delete dashboard")
.authTransaction(db) { implicit request => implicit graph =>
userSrv
.current
.dashboards
.get(EntityIdOrName(dashboardId))
.getOrFail("Dashboard")
.map { dashboard =>
dashboardSrv.remove(dashboard)
Results.NoContent
}
}
}
40 changes: 5 additions & 35 deletions thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class DescribeCtrl @Inject() (
caseCtrl: CaseCtrl,
caseTemplateCtrl: CaseTemplateCtrl,
customFieldCtrl: CustomFieldCtrl,
// dashboardCtrl: DashboardCtrl,
dashboardCtrl: DashboardCtrl,
logCtrl: LogCtrl,
observableCtrl: ObservableCtrl,
observableTypeCtrl: ObservableTypeCtrl,
Expand Down Expand Up @@ -102,7 +102,7 @@ class DescribeCtrl @Inject() (
EntityDescription("case", "listCase", caseCtrl.publicProperties.list.flatMap(propertyToJson("case", _))),
EntityDescription("caseTemplate", "listCaseTemplate", caseTemplateCtrl.publicProperties.list.flatMap(propertyToJson("caseTemplate", _))),
EntityDescription("customField", "listCustomField", customFieldCtrl.publicProperties.list.flatMap(propertyToJson("customField", _))),
// EntityDescription("dashboard", "listDashboard", dashboardCtrl.publicProperties.list.flatMap(propertyToJson("dashboard", _))),
EntityDescription("dashboard", "listDashboard", dashboardCtrl.publicProperties.list.flatMap(propertyToJson("dashboard", _))),
EntityDescription("log", "listLog", logCtrl.publicProperties.list.flatMap(propertyToJson("case_task_log", _))),
EntityDescription("observable", "listObservable", observableCtrl.publicProperties.list.flatMap(propertyToJson("observable", _))),
EntityDescription(
Expand Down Expand Up @@ -161,28 +161,8 @@ class DescribeCtrl @Inject() (
)
case ("case", "impactStatus") => Some(Seq(impactStatus))
case ("case", "resolutionStatus") => Some(Seq(resolutionStatus))
// //case ("observable", "status") =>
// // Some(PropertyDescription("status", "enumeration", Seq(JsString("Ok"))))
// //case ("observable", "dataType") =>
// // Some(PropertyDescription("status", "enumeration", Seq(JsString("sometesttype", "fqdn", "url", "regexp", "mail", "hash", "registry", "custom-type", "uri_path", "ip", "user-agent", "autonomous-system", "file", "mail_subject", "filename", "other", "domain"))))
// case ("alert", "status") =>
// Some(Seq(PropertyDescription("status", "enumeration", Seq(JsString("New"), JsString("Updated"), JsString("Ignored"), JsString("Imported")))))
// case ("case_task", "status") =>
// Some(
// Seq(PropertyDescription("status", "enumeration", Seq(JsString("Waiting"), JsString("InProgress"), JsString("Completed"), JsString("Cancel"))))
// )
// case ("case", "impactStatus") =>
// Some(Seq(PropertyDescription("impactStatus", "enumeration", Seq(JsString("NoImpact"), JsString("WithImpact"), JsString("NotApplicable")))))
// case ("case", "resolutionStatus") =>
// Some(
// Seq(
// PropertyDescription(
// "resolutionStatus",
// "enumeration",
// Seq(JsString("FalsePositive"), JsString("Duplicated"), JsString("Indeterminate"), JsString("TruePositive"), JsString("Other"))
// )
// )
// )
case ("dashboard", "status") =>
Some(Seq(PropertyDescription("status", "enumeration", Seq(JsString("Shared"), JsString("Private"), JsString("Deleted")))))
case (_, "tlp") =>
Some(
Seq(PropertyDescription("tlp", "number", Seq(JsNumber(0), JsNumber(1), JsNumber(2), JsNumber(3)), Seq("white", "green", "amber", "red")))
Expand All @@ -205,17 +185,7 @@ class DescribeCtrl @Inject() (
case (_, "_createdBy") => Some(Seq(PropertyDescription("_createdBy", "user")))
case (_, "_updatedBy") => Some(Seq(PropertyDescription("_updatedBy", "user")))
case (_, "customFields") => Some(customFields)
// case ("case_artifact_job" | "action", "status") =>
// Some(
// Seq(
// PropertyDescription(
// "status",
// "enumeration",
// Seq(JsString("InProgress"), JsString("Success"), JsString("Failure"), JsString("Waiting"), JsString("Deleted"))
// )
// )
// )
case _ => None
case _ => None
}

def propertyToJson(model: String, prop: PublicProperty): Seq[PropertyDescription] =
Expand Down
37 changes: 35 additions & 2 deletions thehive/app/org/thp/thehive/controllers/v1/Properties.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package org.thp.thehive.controllers.v1

import org.thp.scalligraph.controllers.{FPathElem, FPathEmpty}
import org.thp.scalligraph.controllers.{FPathElem, FPathEmpty, FString}
import org.thp.scalligraph.models.{Database, UMapping}
import org.thp.scalligraph.query.{PublicProperties, PublicPropertyListBuilder}
import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.{BadRequestError, EntityIdOrName, RichSeq}
import org.thp.scalligraph.{BadRequestError, EntityIdOrName, InvalidFormatAttributeError, RichSeq}
import org.thp.thehive.dto.v1.InputCustomFieldValue
import org.thp.thehive.models._
import org.thp.thehive.services.AlertOps._
import org.thp.thehive.services.AuditOps._
import org.thp.thehive.services.CaseOps._
import org.thp.thehive.services.CaseTemplateOps._
import org.thp.thehive.services.CustomFieldOps._
import org.thp.thehive.services.DashboardOps._
import org.thp.thehive.services.LogOps._
import org.thp.thehive.services.ObservableOps._
import org.thp.thehive.services.OrganisationOps._
Expand All @@ -34,6 +35,7 @@ class Properties @Inject() (
caseSrv: CaseSrv,
taskSrv: TaskSrv,
userSrv: UserSrv,
dashboardSrv: DashboardSrv,
caseTemplateSrv: CaseTemplateSrv,
observableSrv: ObservableSrv,
customFieldSrv: CustomFieldSrv,
Expand Down Expand Up @@ -428,4 +430,35 @@ class Properties @Inject() (
)
.build

lazy val dashboard: PublicProperties = PublicPropertyListBuilder[Dashboard]
.property("title", UMapping.string)(_.field.updatable)
.property("description", UMapping.string)(_.field.updatable)
.property("definition", UMapping.string)(_.field.updatable)
.property("status", UMapping.string)(
_.select(_.choose(_.organisation, "Shared", "Private"))
.custom {
case (_, "Shared", vertex, graph, authContext) =>
for {
dashboard <- dashboardSrv.get(vertex)(graph).filter(_.user.current(authContext)).getOrFail("Dashboard")
_ <- dashboardSrv.share(dashboard, authContext.organisation, writable = false)(graph, authContext)
} yield Json.obj("status" -> "Shared")

case (_, "Private", vertex, graph, authContext) =>
for {
d <- dashboardSrv.get(vertex)(graph).filter(_.user.current(authContext)).getOrFail("Dashboard")
_ <- dashboardSrv.unshare(d, authContext.organisation)(graph, authContext)
} yield Json.obj("status" -> "Private")

case (_, "Deleted", vertex, graph, authContext) =>
for {
d <- dashboardSrv.get(vertex)(graph).filter(_.user.current(authContext)).getOrFail("Dashboard")
_ <- dashboardSrv.remove(d)(graph, authContext)
} yield Json.obj("status" -> "Deleted")

case (_, status, _, _, _) =>
Failure(InvalidFormatAttributeError("status", "String", Set("Shared", "Private", "Deleted"), FString(status)))
}
)
.build

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class TheHiveQueryExecutor @Inject() (
taskCtrl: TaskCtrl,
userCtrl: UserCtrl,
taxonomyCtrl: TaxonomyCtrl,
// dashboardCtrl: DashboardCtrl,
dashboardCtrl: DashboardCtrl,
properties: Properties,
implicit val db: Database
) extends QueryExecutor {
Expand All @@ -50,7 +50,7 @@ class TheHiveQueryExecutor @Inject() (
caseCtrl,
caseTemplateCtrl,
customFieldCtrl,
// dashboardCtrl,
dashboardCtrl,
logCtrl,
observableCtrl,
observableTypeCtrl,
Expand Down

0 comments on commit 425d16c

Please sign in to comment.