Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Case Reporting using user defined HTML templates #678

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions thehive-backend/app/controllers/CaseReportingTemplateCtrl.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package controllers

import javax.inject.{ Inject, Singleton }

import scala.concurrent.{ ExecutionContext, Future }
import scala.util.control.NonFatal
import scala.io.Source

import play.api.Logger
import play.api.http.Status
import play.api.mvc._
import play.api.libs.json.{ JsTrue, JsFalse }

import models.Roles
import services.CaseReportingTemplateSrv

import org.elastic4play.{ BadRequestError, Timed }
import org.elastic4play.controllers.{ Authenticated, Fields, FieldsBodyParser, Renderer, FileInputValue }
import org.elastic4play.models.JsonFormat.baseModelEntityWrites
import org.elastic4play.services.JsonFormat.queryReads
import org.elastic4play.services.{ AuxSrv, QueryDSL, QueryDef }

@Singleton
class CaseReportingTemplateCtrl @Inject() (
caseReportingTemplateSrv: CaseReportingTemplateSrv,
auxSrv: AuxSrv,
authenticated: Authenticated,
renderer: Renderer,
components: ControllerComponents,
fieldsBodyParser: FieldsBodyParser,
implicit val ec: ExecutionContext) extends AbstractController(components) with Status {

private[CaseReportingTemplateCtrl] lazy val logger = Logger(getClass)

@Timed
def create: Action[Fields] = authenticated(Roles.admin).async(fieldsBodyParser) { implicit request ⇒
caseReportingTemplateSrv.create(request.body)
.map(caseReporting ⇒ renderer.toOutput(CREATED, caseReporting))
}

@Timed
def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
caseReportingTemplateSrv.get(id)
.map(caseReporting ⇒ renderer.toOutput(OK, caseReporting))
}

@Timed
def update(id: String): Action[Fields] = authenticated(Roles.admin).async(fieldsBodyParser) { implicit request ⇒
val updates = Fields.empty
.set("title", request.body.getString("title").getOrElse(""))
.set("content", request.body.getString("content").getOrElse(""))
.set("isDefault", request.body.getValue("isDefault").getOrElse(JsFalse))

caseReportingTemplateSrv.update(id, updates)
.map(caseReporting ⇒ renderer.toOutput(OK, caseReporting))
}

@Timed
def delete(id: String): Action[AnyContent] = authenticated(Roles.admin).async { implicit request ⇒
caseReportingTemplateSrv.delete(id)
.map(_ ⇒ NoContent)
}

@Timed
def find: 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 nparent = request.body.getLong("nparent").getOrElse(0L).toInt
val withStats = request.body.getBoolean("nstats").getOrElse(false)

val (caseReportingTemplates, total) = caseReportingTemplateSrv.find(query, range, sort)
val caseReportingTemplatesWithStats = auxSrv(caseReportingTemplates, nparent, withStats, removeUnaudited = false)
renderer.toOutput(OK, caseReportingTemplatesWithStats, total)
}

@Timed
def importTemplate: Action[Fields] = authenticated(Roles.write).async(fieldsBodyParser) { implicit request ⇒
val file = request.body.get("template") match {
case Some(FileInputValue(_, filepath, _)) ⇒ Source.fromFile(filepath.toFile)
case _ ⇒ throw BadRequestError("")
}
val title = request.body.get("template")

val templateTitle = title.toString().split('(')(2).split(',')(0)
val templateContent = file.mkString

val caseReportingTemplateFields = Fields.empty
.set("title", templateTitle)
.set("content", templateContent)
.set("isDefault", JsFalse)
caseReportingTemplateSrv.create(caseReportingTemplateFields)
.recoverWith {
case NonFatal(_) ⇒
caseReportingTemplateSrv.update(templateTitle, Fields.empty.set("content", templateContent))
}
.map(_.id → JsTrue)
.recoverWith {
case NonFatal(e) ⇒
logger.error(s"The import of the case reporting template $templateTitle has failed", e)
Future.successful(templateTitle → JsFalse)
}
Future { renderer.toOutput(OK, templateTitle) }
}
}

19 changes: 19 additions & 0 deletions thehive-backend/app/models/CaseReportingTemplate.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package models

import javax.inject.{ Inject, Singleton }

import play.api.libs.json.JsObject

import org.elastic4play.models.{ AttributeDef, EntityDef, ModelDef, AttributeFormat ⇒ F }
trait CaseReportingTemplateAttributes { _: AttributeDef ⇒

val title: A[String] = attribute("title", F.stringFmt, "Title of the template")
val content = attribute("content", F.textFmt, "Content of the template")
val isDefault = attribute("isDefault", F.booleanFmt, "Is the template set as default")
}

@Singleton
class CaseReportingTemplateModel @Inject() extends ModelDef[CaseReportingTemplateModel, CaseReportingTemplate]("caseReportingTemplate", "Case reporting template", "/case/reporting") with CaseReportingTemplateAttributes {

}
class CaseReportingTemplate(model: CaseReportingTemplateModel, attributes: JsObject) extends EntityDef[CaseReportingTemplateModel, CaseReportingTemplate](model, attributes) with CaseReportingTemplateAttributes
54 changes: 54 additions & 0 deletions thehive-backend/app/services/CaseReportingTemplateSrv.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package services

import javax.inject.{ Inject, Singleton }

import scala.concurrent.{ ExecutionContext, Future }

import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.{ Sink, Source }
import models.{ CaseReportingTemplate, CaseReportingTemplateModel }

import org.elastic4play.NotFoundError
import org.elastic4play.controllers.Fields
import org.elastic4play.database.ModifyConfig
import org.elastic4play.services._

@Singleton
class CaseReportingTemplateSrv @Inject() (
caseReportingTemplateModel: CaseReportingTemplateModel,
createSrv: CreateSrv,
getSrv: GetSrv,
updateSrv: UpdateSrv,
deleteSrv: DeleteSrv,
findSrv: FindSrv,
implicit val ec: ExecutionContext,
implicit val mat: Materializer) {

def create(fields: Fields)(implicit authContext: AuthContext): Future[CaseReportingTemplate] =
createSrv[CaseReportingTemplateModel, CaseReportingTemplate](caseReportingTemplateModel, fields)

def get(id: String): Future[CaseReportingTemplate] =
getSrv[CaseReportingTemplateModel, CaseReportingTemplate](caseReportingTemplateModel, id)

def getByName(name: String): Future[CaseReportingTemplate] = {
import org.elastic4play.services.QueryDSL._
findSrv[CaseReportingTemplateModel, CaseReportingTemplate](caseReportingTemplateModel, "name" ~= name, Some("0-1"), Nil)
._1
.runWith(Sink.headOption)
.map(_.getOrElse(throw NotFoundError(s"Case reporting template $name not found")))
}

def update(id: String, fields: Fields)(implicit authContext: AuthContext): Future[CaseReportingTemplate] =
update(id, fields, ModifyConfig.default)

def update(id: String, fields: Fields, modifyConfig: ModifyConfig)(implicit authContext: AuthContext): Future[CaseReportingTemplate] =
updateSrv[CaseReportingTemplateModel, CaseReportingTemplate](caseReportingTemplateModel, id, fields, modifyConfig)

def delete(id: String)(implicit authContext: AuthContext): Future[Unit] =
deleteSrv.realDelete[CaseReportingTemplateModel, CaseReportingTemplate](caseReportingTemplateModel, id)

def find(queryDef: QueryDef, range: Option[String], sortBy: Seq[String]): (Source[CaseReportingTemplate, NotUsed], Future[Long]) = {
findSrv[CaseReportingTemplateModel, CaseReportingTemplate](caseReportingTemplateModel, queryDef, range, sortBy)
}
}
Loading