From a7cdecefd5c7febacb70868c8724f9fa18ab53c9 Mon Sep 17 00:00:00 2001 From: To-om Date: Thu, 25 Jun 2020 14:17:36 +0200 Subject: [PATCH] #1404 Provide database instance to all service only when initialisation is complete --- .../connector/cortex/CortexModule.scala | 15 ++- .../cortex/controllers/v0/ActionCtrl.scala | 4 +- .../controllers/v0/AnalyzerTemplateCtrl.scala | 3 +- .../controllers/v0/CortexQueryExecutor.scala | 3 +- .../cortex/controllers/v0/JobCtrl.scala | 3 +- .../cortex/controllers/v0/ResponderCtrl.scala | 12 +- .../cortex/models/CortexSchema.scala | 11 +- .../cortex/models/SchemaUpdater.scala | 10 -- .../cortex/models/SchemaUpdaterActor.scala | 84 +++++++++++++ .../models/TheHiveCortexSchemaProvider.scala | 4 +- .../connector/cortex/services/ActionSrv.scala | 14 +-- .../cortex/services/AnalyzerTemplateSrv.scala | 18 +-- .../cortex/services/CortexAuditSrv.scala | 2 +- .../connector/cortex/services/JobSrv.scala | 15 ++- .../cortex/services/ResponderSrv.scala | 14 +-- .../cortex/services/ServiceHelper.scala | 6 +- .../controllers/v0/AnalyzerCtrlTest.scala | 12 +- .../v0/AnalyzerTemplateCtrlTest.scala | 16 ++- .../cortex/controllers/v0/JobCtrlTest.scala | 8 +- .../cortex/services/ActionSrvTest.scala | 12 +- .../cortex/services/AnalyzerSrvTest.scala | 6 +- .../cortex/services/JobSrvTest.scala | 3 +- .../cortex/services/ResponderSrvTest.scala | 10 +- .../misp/controllers/v0/MispCtrl.scala | 2 +- .../misp/services/MispExportSrv.scala | 13 +- .../misp/services/MispImportSrv.scala | 15 ++- .../app/org/thp/thehive/ClusterSetup.scala | 24 +++- .../org/thp/thehive/GuiceAkkaExtension.scala | 27 ++++ .../app/org/thp/thehive/TheHiveModule.scala | 42 +++++-- .../thp/thehive/controllers/dav/Router.scala | 5 +- .../thehive/controllers/v0/AlertCtrl.scala | 115 +++++++++--------- .../controllers/v0/AttachmentCtrl.scala | 9 +- .../thehive/controllers/v0/AuditCtrl.scala | 4 +- .../controllers/v0/AuthenticationCtrl.scala | 4 +- .../thp/thehive/controllers/v0/CaseCtrl.scala | 2 +- .../thehive/controllers/v0/CaseRenderer.scala | 32 +++-- .../controllers/v0/CaseTemplateCtrl.scala | 4 +- .../controllers/v0/CustomFieldCtrl.scala | 18 +-- .../controllers/v0/DashboardCtrl.scala | 4 +- .../thehive/controllers/v0/DescribeCtrl.scala | 4 +- .../thp/thehive/controllers/v0/ListCtrl.scala | 21 ++-- .../thp/thehive/controllers/v0/LogCtrl.scala | 4 +- .../controllers/v0/ObservableCtrl.scala | 4 +- .../controllers/v0/ObservableTypeCtrl.scala | 4 +- .../controllers/v0/OrganisationCtrl.scala | 8 +- .../thp/thehive/controllers/v0/PageCtrl.scala | 11 +- .../thehive/controllers/v0/ProfileCtrl.scala | 5 +- .../thehive/controllers/v0/QueryCtrl.scala | 6 +- .../thehive/controllers/v0/ShareCtrl.scala | 11 +- .../thehive/controllers/v0/StatsCtrl.scala | 4 +- .../thehive/controllers/v0/StatusCtrl.scala | 19 ++- .../thehive/controllers/v0/StreamCtrl.scala | 15 ++- .../thp/thehive/controllers/v0/TagCtrl.scala | 4 +- .../thp/thehive/controllers/v0/TaskCtrl.scala | 4 +- .../controllers/v0/TheHiveQueryExecutor.scala | 4 +- .../thp/thehive/controllers/v0/UserCtrl.scala | 14 +-- .../thehive/controllers/v1/AlertCtrl.scala | 4 +- .../controllers/v1/AuthenticationCtrl.scala | 19 +-- .../thp/thehive/controllers/v1/CaseCtrl.scala | 4 +- .../controllers/v1/CaseTemplateCtrl.scala | 4 +- .../controllers/v1/CustomFieldCtrl.scala | 11 +- .../controllers/v1/ObservableCtrl.scala | 4 +- .../controllers/v1/ObservableRenderer.scala | 14 ++- .../controllers/v1/OrganisationCtrl.scala | 7 +- .../thp/thehive/controllers/v1/TaskCtrl.scala | 4 +- .../controllers/v1/TheHiveQueryExecutor.scala | 4 +- .../thp/thehive/controllers/v1/UserCtrl.scala | 4 +- .../org/thp/thehive/models/CustomField.scala | 10 +- .../thp/thehive/models/SchemaUpdater.scala | 15 --- .../thehive/models/SchemaUpdaterActor.scala | 86 +++++++++++++ .../thp/thehive/models/TheHiveSchema.scala | 40 ++---- .../org/thp/thehive/services/AlertSrv.scala | 15 ++- .../thp/thehive/services/AttachmentSrv.scala | 22 ++-- .../thehive/services/CaseTemplateSrv.scala | 5 +- .../thp/thehive/services/ConfigContext.scala | 13 +- .../org/thp/thehive/services/ConfigSrv.scala | 15 ++- .../thp/thehive/services/CustomFieldSrv.scala | 27 +++- .../thp/thehive/services/DashboardSrv.scala | 19 +-- .../thp/thehive/services/KeyValueSrv.scala | 10 +- .../thehive/services/LocalKeyAuthSrv.scala | 17 ++- .../services/LocalPasswordAuthSrv.scala | 7 +- .../thp/thehive/services/LocalUserSrv.scala | 29 +++-- .../app/org/thp/thehive/services/LogSrv.scala | 16 +-- .../thp/thehive/services/ObservableSrv.scala | 16 +-- .../thehive/services/ObservableTypeSrv.scala | 50 ++++---- .../thehive/services/OrganisationSrv.scala | 52 +++++--- .../org/thp/thehive/services/PageSrv.scala | 7 +- .../org/thp/thehive/services/ProfileSrv.scala | 68 +++++------ .../thp/thehive/services/ReportTagSrv.scala | 16 +-- .../org/thp/thehive/services/RoleSrv.scala | 10 +- .../org/thp/thehive/services/ShareSrv.scala | 12 +- .../org/thp/thehive/services/StreamSrv.scala | 21 ++-- .../thp/thehive/services/TOTPAuthSrv.scala | 10 +- .../app/org/thp/thehive/services/TagSrv.scala | 29 ++--- .../org/thp/thehive/services/TaskSrv.scala | 6 +- .../org/thp/thehive/services/UserSrv.scala | 106 +++++++++++----- .../notification/NotificationActor.scala | 4 +- .../org/thp/thehive/services/package.scala | 25 +++- .../org/thp/thehive/DatabaseBuilder.scala | 17 ++- .../test/org/thp/thehive/TestAppBuilder.scala | 24 ++-- 100 files changed, 1002 insertions(+), 643 deletions(-) delete mode 100644 cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdater.scala create mode 100644 cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdaterActor.scala rename cortex/connector/src/{test => main}/scala/org/thp/thehive/connector/cortex/models/TheHiveCortexSchemaProvider.scala (51%) create mode 100644 thehive/app/org/thp/thehive/GuiceAkkaExtension.scala delete mode 100644 thehive/app/org/thp/thehive/models/SchemaUpdater.scala create mode 100644 thehive/app/org/thp/thehive/models/SchemaUpdaterActor.scala diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/CortexModule.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/CortexModule.scala index 9eb8601da1..7366bfcb1e 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/CortexModule.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/CortexModule.scala @@ -1,17 +1,16 @@ package org.thp.thehive.connector.cortex -import play.api.libs.concurrent.AkkaGuiceSupport -import play.api.routing.{Router => PlayRouter} -import play.api.{Configuration, Environment, Logger} - import com.google.inject.AbstractModule import net.codingwell.scalaguice.{ScalaModule, ScalaMultibinder} -import org.thp.scalligraph.models.Schema +import org.thp.scalligraph.models.{Database, Schema} import org.thp.scalligraph.query.QueryExecutor import org.thp.thehive.connector.cortex.controllers.v0.{CortexQueryExecutor => CortexQueryExecutorV0} -import org.thp.thehive.connector.cortex.models.{CortexSchema, SchemaUpdater} +import org.thp.thehive.connector.cortex.models.{CortexSchemaDefinition, DatabaseProvider} import org.thp.thehive.connector.cortex.services.{Connector, CortexActor} import org.thp.thehive.services.{Connector => TheHiveConnector} +import play.api.libs.concurrent.AkkaGuiceSupport +import play.api.routing.{Router => PlayRouter} +import play.api.{Configuration, Environment, Logger} class CortexModule(environment: Environment, configuration: Configuration) extends AbstractModule with ScalaModule with AkkaGuiceSupport { lazy val logger: Logger = Logger(getClass) @@ -24,9 +23,9 @@ class CortexModule(environment: Environment, configuration: Configuration) exten val connectorBindings = ScalaMultibinder.newSetBinder[TheHiveConnector](binder) connectorBindings.addBinding.to[Connector] val schemaBindings = ScalaMultibinder.newSetBinder[Schema](binder) - schemaBindings.addBinding.to[CortexSchema] + schemaBindings.addBinding.to[CortexSchemaDefinition] - bind[SchemaUpdater].asEagerSingleton() + bind[Database].annotatedWithName("with-thehive-cortex-schema").toProvider[DatabaseProvider] bindActor[CortexActor]("cortex-actor") () } diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala index 249f03b2ea..3f2e886583 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ActionCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.connector.cortex.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.query.{ParamQuery, PublicProperty, Query} @@ -22,7 +22,7 @@ import scala.concurrent.{ExecutionContext, Future} @Singleton class ActionCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, actionSrv: ActionSrv, entityHelper: EntityHelper, diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrl.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrl.scala index b600d6932a..bd7dcb5c82 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrl.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrl.scala @@ -2,6 +2,7 @@ package org.thp.thehive.connector.cortex.controllers.v0 import java.util.zip.ZipFile +import com.google.inject.name.Named import javax.inject.{Inject, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FFile, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} @@ -24,7 +25,7 @@ import scala.util.{Failure, Success} @Singleton class AnalyzerTemplateCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-cortex-schema") db: Database, properties: Properties, analyzerTemplateSrv: AnalyzerTemplateSrv ) extends QueryableCtrl { diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala index e03607fbb7..4a18f3672a 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/CortexQueryExecutor.scala @@ -1,5 +1,6 @@ package org.thp.thehive.connector.cortex.controllers.v0 +import com.google.inject.name.Named import javax.inject.{Inject, Singleton} import org.scalactic.Good import org.thp.scalligraph.BadRequestError @@ -19,7 +20,7 @@ import scala.reflect.runtime.{universe => ru} class CortexQueryExecutor @Inject() ( jobCtrl: JobCtrl, queryCtrlBuilder: QueryCtrlBuilder, - implicit val db: Database, + @Named("with-thehive-cortex-schema") implicit val db: Database, reportCtrl: AnalyzerTemplateCtrl, actionCtrl: ActionCtrl ) extends QueryExecutor { diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrl.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrl.scala index b4ba152987..a5e856aee9 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrl.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrl.scala @@ -1,5 +1,6 @@ package org.thp.thehive.connector.cortex.controllers.v0 +import com.google.inject.name.Named import javax.inject.{Inject, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database @@ -22,7 +23,7 @@ import scala.concurrent.{ExecutionContext, Future} @Singleton class JobCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-cortex-schema") db: Database, properties: Properties, jobSrv: JobSrv, observableSrv: ObservableSrv, diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ResponderCtrl.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ResponderCtrl.scala index 928a0c2325..70d8a82503 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ResponderCtrl.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/ResponderCtrl.scala @@ -1,21 +1,21 @@ package org.thp.thehive.connector.cortex.controllers.v0 -import scala.concurrent.ExecutionContext - -import play.api.libs.json.JsObject -import play.api.mvc.{Action, AnyContent, Results} - +import com.google.inject.name.Named import javax.inject.{Inject, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.thehive.connector.cortex.controllers.v0.Conversion._ import org.thp.thehive.connector.cortex.services.ResponderSrv import org.thp.thehive.controllers.v0.Conversion._ +import play.api.libs.json.JsObject +import play.api.mvc.{Action, AnyContent, Results} + +import scala.concurrent.ExecutionContext @Singleton class ResponderCtrl @Inject() ( entrypoint: Entrypoint, - implicit val db: Database, + @Named("with-thehive-cortex-schema") implicit val db: Database, responderSrv: ResponderSrv, implicit val ex: ExecutionContext ) { diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/CortexSchema.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/CortexSchema.scala index cae6ae13b7..c619e37b75 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/CortexSchema.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/CortexSchema.scala @@ -1,16 +1,17 @@ package org.thp.thehive.connector.cortex.models -import scala.collection.JavaConverters._ -import scala.reflect.runtime.{universe => ru} -import play.api.Logger import javax.inject.{Inject, Singleton} import org.reflections.Reflections import org.reflections.scanners.SubTypesScanner import org.reflections.util.ConfigurationBuilder -import org.thp.scalligraph.models.{HasModel, Model, Operations, Schema, UpdatableSchema} +import org.thp.scalligraph.models._ +import play.api.Logger + +import scala.collection.JavaConverters._ +import scala.reflect.runtime.{universe => ru} @Singleton -class CortexSchema @Inject() () extends Schema with UpdatableSchema { +class CortexSchemaDefinition @Inject() () extends Schema with UpdatableSchema { lazy val logger: Logger = Logger(getClass) val name: String = "thehive-cortex" diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdater.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdater.scala deleted file mode 100644 index 5276dad194..0000000000 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdater.scala +++ /dev/null @@ -1,10 +0,0 @@ -package org.thp.thehive.connector.cortex.models - -import javax.inject.{Inject, Singleton} -import org.thp.scalligraph.auth.UserSrv -import org.thp.scalligraph.models.Database - -@Singleton -class SchemaUpdater @Inject() (cortexSchema: CortexSchema, db: Database, userSrv: UserSrv) { - cortexSchema.update(db)(userSrv.getSystemAuthContext).get -} diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdaterActor.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdaterActor.scala new file mode 100644 index 0000000000..9a7bcde9cd --- /dev/null +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/SchemaUpdaterActor.scala @@ -0,0 +1,84 @@ +package org.thp.thehive.connector.cortex.models + +import akka.actor.{Actor, ActorRef, ActorSystem, PoisonPill, Props} +import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings, ClusterSingletonProxy, ClusterSingletonProxySettings} +import akka.pattern.ask +import akka.util.Timeout +import javax.inject.{Inject, Named, Provider, Singleton} +import org.thp.scalligraph.models.Database +import org.thp.thehive.services.LocalUserSrv +import play.api.Logger + +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import scala.util.Try + +@Singleton +class DatabaseProvider @Inject() ( + cortexSchema: CortexSchemaDefinition, + @Named("with-thehive-schema") database: Database, + actorSystem: ActorSystem +) extends Provider[Database] { + import SchemaUpdaterActor._ + lazy val schemaUpdaterActor: ActorRef = { + val singletonManager = + actorSystem.actorOf( + ClusterSingletonManager.props( + singletonProps = Props(classOf[SchemaUpdaterActor], cortexSchema, database), + terminationMessage = PoisonPill, + settings = ClusterSingletonManagerSettings(actorSystem) + ), + name = "theHiveCortexSchemaUpdaterSingletonManager" + ) + + actorSystem.actorOf( + ClusterSingletonProxy.props( + singletonManagerPath = singletonManager.path.toStringWithoutAddress, + settings = ClusterSingletonProxySettings(actorSystem) + ), + name = "theHiveCortexSchemaUpdaterSingletonProxy" + ) + } + + override def get(): Database = { + implicit val timeout: Timeout = Timeout(5.minutes) + Await.result(schemaUpdaterActor ? RequestDBStatus, timeout.duration) match { + case DBStatus(status) => + status.get + database + } + } +} + +object SchemaUpdaterActor { + case object RequestDBStatus + case class DBStatus(status: Try[Unit]) +} + +class SchemaUpdaterActor @Inject() (cortexSchema: CortexSchemaDefinition, database: Database) extends Actor { + import SchemaUpdaterActor._ + lazy val logger: Logger = Logger(getClass) + + def update(): Try[Unit] = + cortexSchema + .update(database)(LocalUserSrv.getSystemAuthContext) + .recover { + case error => logger.error(s"Database with CortexSchema schema update failure", error) + } + + override def receive: Receive = { + case RequestDBStatus => + val status = update() + sender ! DBStatus(status) + context.become(receive(status)) + } + + def receive(status: Try[Unit]): Receive = { + case RequestDBStatus => + status.fold({ _ => + val newStatus = update() + sender ! DBStatus(newStatus) + context.become(receive(newStatus)) + }, _ => sender ! DBStatus(status)) + } +} diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/models/TheHiveCortexSchemaProvider.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/TheHiveCortexSchemaProvider.scala similarity index 51% rename from cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/models/TheHiveCortexSchemaProvider.scala rename to cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/TheHiveCortexSchemaProvider.scala index 6f22e7d2b4..5ad29eea2e 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/models/TheHiveCortexSchemaProvider.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/TheHiveCortexSchemaProvider.scala @@ -2,9 +2,9 @@ package org.thp.thehive.connector.cortex.models import javax.inject.{Inject, Provider, Singleton} import org.thp.scalligraph.models.Schema -import org.thp.thehive.models.TheHiveSchema +import org.thp.thehive.models.TheHiveSchemaDefinition @Singleton -class TheHiveCortexSchemaProvider @Inject()(thehiveSchema: TheHiveSchema, cortexSchema: CortexSchema) extends Provider[Schema] { +class TheHiveCortexSchemaProvider @Inject() (thehiveSchema: TheHiveSchemaDefinition, cortexSchema: CortexSchemaDefinition) extends Provider[Schema] { override lazy val get: Schema = thehiveSchema + cortexSchema } diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala index 265496f1fe..4e6fd8d806 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala @@ -2,11 +2,6 @@ package org.thp.thehive.connector.cortex.services import java.util.Date -import scala.concurrent.{ExecutionContext, Future} -import scala.util.Try - -import play.api.libs.json.{JsObject, Json, OWrites} - import akka.actor.ActorRef import com.google.inject.name.Named import gremlin.scala._ @@ -26,6 +21,10 @@ import org.thp.thehive.connector.cortex.services.CortexActor.CheckJob import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.models.{Case, Task} import org.thp.thehive.services.{AlertSteps, CaseSteps, LogSteps, ObservableSteps, TaskSteps} +import play.api.libs.json.{JsObject, Json, OWrites} + +import scala.concurrent.{ExecutionContext, Future} +import scala.util.Try class ActionSrv @Inject() ( @Named("cortex-actor") cortexActor: ActorRef, @@ -34,7 +33,7 @@ class ActionSrv @Inject() ( serviceHelper: ServiceHelper, connector: Connector, implicit val schema: Schema, - implicit val db: Database, + @Named("with-thehive-cortex-schema") implicit val db: Database, implicit val ec: ExecutionContext, auditSrv: CortexAuditSrv ) extends VertexSrv[Action, ActionSteps] { @@ -183,7 +182,8 @@ class ActionSrv @Inject() ( } @EntitySteps[Action] -class ActionSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph, schema: Schema) extends VertexSteps[Action](raw) { +class ActionSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph, schema: Schema) + extends VertexSteps[Action](raw) { /** * Provides a RichAction model with additional Entity context diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala index cb16b69580..81cd4cecbd 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala @@ -2,28 +2,27 @@ package org.thp.thehive.connector.cortex.services import java.util.zip.{ZipEntry, ZipFile} -import scala.collection.JavaConverters._ -import scala.io.Source -import scala.util.{Failure, Try} - -import play.api.libs.json.{JsObject, Json} - +import com.google.inject.name.Named import gremlin.scala._ import javax.inject.{Inject, Singleton} -import org.thp.scalligraph.{CreateError, EntitySteps} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.query.PropertyUpdater import org.thp.scalligraph.services._ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.VertexSteps +import org.thp.scalligraph.{CreateError, EntitySteps} import org.thp.thehive.connector.cortex.controllers.v0.Conversion._ import org.thp.thehive.connector.cortex.models.AnalyzerTemplate import org.thp.thehive.controllers.v0.Conversion._ +import play.api.libs.json.{JsObject, Json} +import scala.collection.JavaConverters._ +import scala.io.Source +import scala.util.{Failure, Try} @Singleton class AnalyzerTemplateSrv @Inject() ( - implicit db: Database, + implicit @Named("with-thehive-cortex-schema") db: Database, auditSrv: CortexAuditSrv ) extends VertexSrv[AnalyzerTemplate, AnalyzerTemplateSteps] { @@ -108,7 +107,8 @@ class AnalyzerTemplateSrv @Inject() ( } @EntitySteps[AnalyzerTemplate] -class AnalyzerTemplateSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[AnalyzerTemplate](raw) { +class AnalyzerTemplateSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-cortex-schema") db: Database, graph: Graph) + extends VertexSteps[AnalyzerTemplate](raw) { def get(idOrAnalyzerId: String): AnalyzerTemplateSteps = if (db.isValidId(idOrAnalyzerId)) this.getByIds(idOrAnalyzerId) diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/CortexAuditSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/CortexAuditSrv.scala index f9f57d17a7..047e4be981 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/CortexAuditSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/CortexAuditSrv.scala @@ -15,7 +15,7 @@ class CortexAuditSrv @Inject() ( userSrvProvider: Provider[UserSrv], @Named("notification-actor") notificationActor: ActorRef, eventSrv: EventSrv -)(implicit db: Database, schema: Schema) +)(implicit @Named("with-thehive-cortex-schema") db: Database, schema: Schema) extends AuditSrv(userSrvProvider, notificationActor, eventSrv) { val job = new ObjectAudit[Job, Observable] diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala index 2152019bbd..0df0dad5d8 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala @@ -3,12 +3,6 @@ package org.thp.thehive.connector.cortex.services import java.nio.file.Files import java.util.Date -import scala.collection.JavaConverters._ -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Failure, Success, Try} - -import play.api.libs.json.Json - import akka.Done import akka.actor._ import akka.stream.Materializer @@ -33,6 +27,11 @@ import org.thp.thehive.connector.cortex.services.CortexActor.CheckJob import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.models._ import org.thp.thehive.services.{AttachmentSrv, ObservableSrv, ObservableSteps, ObservableTypeSrv, ReportTagSrv} +import play.api.libs.json.Json + +import scala.collection.JavaConverters._ +import scala.concurrent.{ExecutionContext, Future} +import scala.util.{Failure, Success, Try} @Singleton class JobSrv @Inject() ( @@ -44,7 +43,7 @@ class JobSrv @Inject() ( reportTagSrv: ReportTagSrv, serviceHelper: ServiceHelper, auditSrv: CortexAuditSrv, - implicit val db: Database, + @Named("with-thehive-schema") implicit val db: Database, implicit val ec: ExecutionContext, implicit val mat: Materializer ) extends VertexSrv[Job, JobSteps] { @@ -267,7 +266,7 @@ class JobSrv @Inject() ( } @EntitySteps[Job] -class JobSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Job](raw) { +class JobSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Job](raw) { /** * Checks if a Job is visible from a certain UserRole end diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ResponderSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ResponderSrv.scala index 39a70566f4..f4f6c1d133 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ResponderSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ResponderSrv.scala @@ -1,22 +1,22 @@ package org.thp.thehive.connector.cortex.services -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Failure, Success} - -import play.api.Logger -import play.api.libs.json.JsObject - +import com.google.inject.name.Named import javax.inject.{Inject, Singleton} import org.thp.cortex.dto.v0.OutputWorker import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.Database import org.thp.thehive.controllers.v0.Conversion.toObjectType import org.thp.thehive.models.Permissions +import play.api.Logger +import play.api.libs.json.JsObject + +import scala.concurrent.{ExecutionContext, Future} +import scala.util.{Failure, Success} @Singleton class ResponderSrv @Inject() ( connector: Connector, - db: Database, + @Named("with-thehive-cortex-schema") db: Database, entityHelper: EntityHelper, serviceHelper: ServiceHelper, implicit val ec: ExecutionContext diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ServiceHelper.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ServiceHelper.scala index 8729288e5a..9294749673 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ServiceHelper.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ServiceHelper.scala @@ -1,7 +1,6 @@ package org.thp.thehive.connector.cortex.services -import play.api.Logger - +import com.google.inject.name.Named import gremlin.scala.P import javax.inject.{Inject, Singleton} import org.thp.cortex.client.CortexClient @@ -9,10 +8,11 @@ import org.thp.cortex.dto.v0.OutputWorker import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ import org.thp.thehive.services._ +import play.api.Logger @Singleton class ServiceHelper @Inject() ( - db: Database, + @Named("with-thehive-cortex-schema") db: Database, organisationSrv: OrganisationSrv ) { diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerCtrlTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerCtrlTest.scala index e126de6332..f8bcb5c265 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerCtrlTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerCtrlTest.scala @@ -1,11 +1,17 @@ package org.thp.thehive.connector.cortex.controllers.v0 -import play.api.test.{FakeRequest, PlaySpecification} - -import org.thp.thehive.TestAppBuilder +import org.thp.scalligraph.AppBuilder +import org.thp.scalligraph.models.Database +import org.thp.thehive.{BasicDatabaseProvider, TestAppBuilder} import org.thp.thehive.connector.cortex.dto.v0.OutputWorker +import play.api.test.{FakeRequest, PlaySpecification} class AnalyzerCtrlTest extends PlaySpecification with TestAppBuilder { + override def appConfigure: AppBuilder = + super + .appConfigure + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-cortex-schema") + "analyzer controller" should { "list analyzers" in testApp { app => val request = FakeRequest("GET", s"/api/connector/cortex/analyzer?range=all").withHeaders("user" -> "certuser@thehive.local") diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrlTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrlTest.scala index 5ef704ecc7..7705b5d9aa 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrlTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/AnalyzerTemplateCtrlTest.scala @@ -1,17 +1,23 @@ package org.thp.thehive.connector.cortex.controllers.v0 -import scala.util.Random - +import org.thp.scalligraph.AppBuilder +import org.thp.scalligraph.controllers.FakeTemporaryFile +import org.thp.scalligraph.models.Database +import org.thp.thehive.{BasicDatabaseProvider, TestAppBuilder} +import org.thp.thehive.connector.cortex.dto.v0.OutputAnalyzerTemplate import play.api.libs.json.Json import play.api.mvc.MultipartFormData.FilePart import play.api.mvc.{AnyContentAsMultipartFormData, MultipartFormData} import play.api.test.{FakeRequest, PlaySpecification} -import org.thp.scalligraph.controllers.FakeTemporaryFile -import org.thp.thehive.TestAppBuilder -import org.thp.thehive.connector.cortex.dto.v0.OutputAnalyzerTemplate +import scala.util.Random class AnalyzerTemplateCtrlTest extends PlaySpecification with TestAppBuilder { + override def appConfigure: AppBuilder = + super + .appConfigure + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-cortex-schema") + "report controller" should { // "create, fetch, update and delete a template" in testApp {app => // diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrlTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrlTest.scala index bcfd10a543..2915c07e0b 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrlTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/controllers/v0/JobCtrlTest.scala @@ -1,16 +1,15 @@ package org.thp.thehive.connector.cortex.controllers.v0 -import play.api.libs.json.Json -import play.api.test.{FakeRequest, PlaySpecification} - import org.thp.cortex.client.{CortexClient, TestCortexClientProvider} import org.thp.scalligraph.AppBuilder import org.thp.scalligraph.models.{Database, Schema} import org.thp.scalligraph.steps.StepsOps._ -import org.thp.thehive.TestAppBuilder +import org.thp.thehive.{BasicDatabaseProvider, TestAppBuilder} import org.thp.thehive.connector.cortex.models.TheHiveCortexSchemaProvider import org.thp.thehive.connector.cortex.services.{Connector, CortexActor, TestConnector} import org.thp.thehive.services.ObservableSrv +import play.api.libs.json.Json +import play.api.test.{FakeRequest, PlaySpecification} class JobCtrlTest extends PlaySpecification with TestAppBuilder { override val databaseName: String = "thehiveCortex" @@ -23,6 +22,7 @@ class JobCtrlTest extends PlaySpecification with TestAppBuilder { .bindToProvider[CortexClient, TestCortexClientProvider] .bind[Connector, TestConnector] .bindToProvider[Schema, TheHiveCortexSchemaProvider] + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-cortex-schema") ) "job controller" should { diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ActionSrvTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ActionSrvTest.scala index abe9a6952d..db2dde50e2 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ActionSrvTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ActionSrvTest.scala @@ -1,21 +1,20 @@ package org.thp.thehive.connector.cortex.services -import scala.io.Source - -import play.api.libs.json._ -import play.api.test.PlaySpecification - import org.thp.cortex.client.{CortexClient, TestCortexClientProvider} import org.thp.cortex.dto.v0.OutputJob import org.thp.scalligraph.AppBuilder import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models._ import org.thp.scalligraph.steps.StepsOps._ -import org.thp.thehive.TestAppBuilder +import org.thp.thehive.{BasicDatabaseProvider, TestAppBuilder} import org.thp.thehive.connector.cortex.controllers.v0.ActionCtrl import org.thp.thehive.connector.cortex.models.{JobStatus, TheHiveCortexSchemaProvider} import org.thp.thehive.models._ import org.thp.thehive.services.{AlertSrv, LogSrv, TaskSrv} +import play.api.libs.json._ +import play.api.test.PlaySpecification + +import scala.io.Source class ActionSrvTest extends PlaySpecification with TestAppBuilder { implicit val authContext: AuthContext = @@ -32,6 +31,7 @@ class ActionSrvTest extends PlaySpecification with TestAppBuilder { .bind[Connector, TestConnector] .bindToProvider[Schema, TheHiveCortexSchemaProvider] ) + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-cortex-schema") def testAppBuilder[A](body: AppBuilder => A): A = testApp { app => body( diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/AnalyzerSrvTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/AnalyzerSrvTest.scala index 042d6e7148..df6110b615 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/AnalyzerSrvTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/AnalyzerSrvTest.scala @@ -1,15 +1,14 @@ package org.thp.thehive.connector.cortex.services -import play.api.test.PlaySpecification - import org.thp.cortex.client.{CortexClient, TestCortexClientProvider} import org.thp.cortex.dto.v0.OutputWorker import org.thp.scalligraph.AppBuilder import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models._ -import org.thp.thehive.TestAppBuilder +import org.thp.thehive.{BasicDatabaseProvider, TestAppBuilder} import org.thp.thehive.connector.cortex.models.TheHiveCortexSchemaProvider import org.thp.thehive.models.Permissions +import play.api.test.PlaySpecification class AnalyzerSrvTest extends PlaySpecification with TestAppBuilder { override val databaseName: String = "thehiveCortex" @@ -23,6 +22,7 @@ class AnalyzerSrvTest extends PlaySpecification with TestAppBuilder { .bind[Connector, TestConnector] .bindToProvider[Schema, TheHiveCortexSchemaProvider] ) + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-cortex-schema") implicit val authContext: AuthContext = DummyUserSrv(userId = "certuser@thehive.local", organisation = "cert", permissions = Permissions.all).authContext diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala index 4816ddead4..78c4ef318c 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala @@ -8,7 +8,7 @@ import org.thp.scalligraph.AppBuilder import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, DummyUserSrv, Schema} import org.thp.scalligraph.steps.StepsOps._ -import org.thp.thehive.TestAppBuilder +import org.thp.thehive.{BasicDatabaseProvider, TestAppBuilder} import org.thp.thehive.connector.cortex.models.{Job, JobStatus, TheHiveCortexSchemaProvider} import org.thp.thehive.models.Permissions import org.thp.thehive.services._ @@ -28,6 +28,7 @@ class JobSrvTest extends PlaySpecification with TestAppBuilder { .bindActor[CortexActor]("cortex-actor") .bindToProvider[CortexClient, TestCortexClientProvider] .bind[Connector, TestConnector] + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-cortex-schema") .`override`(_.bindToProvider[Schema, TheHiveCortexSchemaProvider]) "job service" should { diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ResponderSrvTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ResponderSrvTest.scala index 1a0c3ec026..e084a48e64 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ResponderSrvTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/ResponderSrvTest.scala @@ -1,18 +1,17 @@ package org.thp.thehive.connector.cortex.services +import org.thp.cortex.client.{CortexClient, TestCortexClientProvider} +import org.thp.scalligraph.AppBuilder import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models._ import org.thp.scalligraph.steps.StepsOps._ -import org.thp.thehive.TestAppBuilder +import org.thp.thehive.{BasicDatabaseProvider, TestAppBuilder} +import org.thp.thehive.connector.cortex.models.TheHiveCortexSchemaProvider import org.thp.thehive.models.Permissions import org.thp.thehive.services._ import play.api.libs.json.Json import play.api.test.PlaySpecification -import org.thp.cortex.client.{CortexClient, TestCortexClientProvider} -import org.thp.scalligraph.AppBuilder -import org.thp.thehive.connector.cortex.models.TheHiveCortexSchemaProvider - class ResponderSrvTest extends PlaySpecification with TestAppBuilder { implicit val authContext: AuthContext = DummyUserSrv(userId = "certuser@thehive.local", organisation = "cert", permissions = Permissions.all).authContext @@ -28,6 +27,7 @@ class ResponderSrvTest extends PlaySpecification with TestAppBuilder { .bind[Connector, TestConnector] .bindToProvider[Schema, TheHiveCortexSchemaProvider] ) + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-cortex-schema") "responder service" should { "fetch responders by type" in testApp { app => diff --git a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/controllers/v0/MispCtrl.scala b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/controllers/v0/MispCtrl.scala index 1f84c4f948..1912549453 100644 --- a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/controllers/v0/MispCtrl.scala +++ b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/controllers/v0/MispCtrl.scala @@ -19,7 +19,7 @@ class MispCtrl @Inject() ( mispExportSrv: MispExportSrv, alertSrv: AlertSrv, caseSrv: CaseSrv, - db: Database, + @Named("with-thehive-schema") db: Database, @Named("misp-actor") mispActor: ActorRef, implicit val ec: ExecutionContext ) { diff --git a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispExportSrv.scala b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispExportSrv.scala index 6053c56fe6..d33f486745 100644 --- a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispExportSrv.scala +++ b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispExportSrv.scala @@ -2,13 +2,8 @@ package org.thp.thehive.connector.misp.services import java.util.Date -import scala.concurrent.{ExecutionContext, Future} -import scala.util.Try - -import play.api.Logger - import gremlin.scala.Graph -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.misp.dto.{Attribute, Tag => MispTag} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} @@ -16,6 +11,10 @@ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.{BadRequestError, NotFoundError} import org.thp.thehive.models._ import org.thp.thehive.services.{AlertSrv, AttachmentSrv, CaseSrv, OrganisationSrv} +import play.api.Logger + +import scala.concurrent.{ExecutionContext, Future} +import scala.util.Try @Singleton class MispExportSrv @Inject() ( @@ -24,7 +23,7 @@ class MispExportSrv @Inject() ( attachmentSrv: AttachmentSrv, alertSrv: AlertSrv, organisationSrv: OrganisationSrv, - db: Database + @Named("with-thehive-schema") db: Database ) { lazy val logger: Logger = Logger(getClass) diff --git a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala index 70903a5755..4a5ff5a56d 100644 --- a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala +++ b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala @@ -3,17 +3,11 @@ package org.thp.thehive.connector.misp.services import java.nio.file.Files import java.util.Date -import scala.collection.JavaConverters._ -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Failure, Success, Try} - -import play.api.Logger - import akka.stream.Materializer import akka.stream.scaladsl.{FileIO, Sink, Source} import akka.util.ByteString import gremlin.scala.{__, By, Key, P, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.misp.dto.{Attribute, Event, Tag => MispTag} import org.thp.scalligraph.RichSeq import org.thp.scalligraph.auth.AuthContext @@ -23,6 +17,11 @@ import org.thp.scalligraph.services.RichVertexGremlinScala import org.thp.scalligraph.steps.StepsOps._ import org.thp.thehive.models._ import org.thp.thehive.services._ +import play.api.Logger + +import scala.collection.JavaConverters._ +import scala.concurrent.{ExecutionContext, Future} +import scala.util.{Failure, Success, Try} @Singleton class MispImportSrv @Inject() ( @@ -33,7 +32,7 @@ class MispImportSrv @Inject() ( observableTypeSrv: ObservableTypeSrv, attachmentSrv: AttachmentSrv, caseTemplateSrv: CaseTemplateSrv, - db: Database, + @Named("with-thehive-schema") db: Database, implicit val ec: ExecutionContext, implicit val mat: Materializer ) { diff --git a/thehive/app/org/thp/thehive/ClusterSetup.scala b/thehive/app/org/thp/thehive/ClusterSetup.scala index 0dedc3de53..76b4243f46 100644 --- a/thehive/app/org/thp/thehive/ClusterSetup.scala +++ b/thehive/app/org/thp/thehive/ClusterSetup.scala @@ -2,14 +2,30 @@ package org.thp.thehive import akka.actor.ActorSystem import akka.cluster.Cluster -import javax.inject.Inject +import com.google.inject.Injector +import javax.inject.{Inject, Singleton} +import org.thp.scalligraph.models.Database +import play.api.inject.ApplicationLifecycle import play.api.{Configuration, Logger} -class ClusterSetup @Inject() (configuration: Configuration, system: ActorSystem) { - lazy val logger: Logger = Logger(getClass) +import scala.concurrent.Future + +@Singleton +class ClusterSetup @Inject() ( + configuration: Configuration, + system: ActorSystem, + applicationLifeCycle: ApplicationLifecycle, + db: Database, + injector: Injector +) { + applicationLifeCycle + .addStopHook(() => Future.successful(db.close())) if (configuration.get[Seq[String]]("akka.cluster.seed-nodes").isEmpty) { - val cluster = Cluster(system) + val logger: Logger = Logger(getClass) logger.info("Initialising cluster") + val cluster = Cluster(system) cluster.join(cluster.system.provider.getDefaultAddress) + GuiceAkkaExtension(system).set(injector) + } } diff --git a/thehive/app/org/thp/thehive/GuiceAkkaExtension.scala b/thehive/app/org/thp/thehive/GuiceAkkaExtension.scala new file mode 100644 index 0000000000..ea1db17d01 --- /dev/null +++ b/thehive/app/org/thp/thehive/GuiceAkkaExtension.scala @@ -0,0 +1,27 @@ +package org.thp.thehive + +import akka.actor.{ActorSystem, ClassicActorSystemProvider, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider} +import com.google.inject.Injector + +import scala.concurrent.duration.DurationInt +import scala.concurrent.{Await, Promise} + +class GuiceAkkaExtension extends Extension { + private val injectorPromise = Promise[Injector] + def set(injector: Injector): Unit = { + injectorPromise.success(injector) + () + } + lazy val injector: Injector = Await.result(injectorPromise.future, 1.minute) +} + +object GuiceAkkaExtension extends ExtensionId[GuiceAkkaExtension] with ExtensionIdProvider { + override def lookup: ExtensionId[GuiceAkkaExtension] = GuiceAkkaExtension + override def createExtension(system: ExtendedActorSystem) = new GuiceAkkaExtension + + /** + * Java API: retrieve the Count extension for the given system. + */ + override def get(system: ActorSystem): GuiceAkkaExtension = super.get(system) + override def get(system: ClassicActorSystemProvider): GuiceAkkaExtension = super.get(system) +} diff --git a/thehive/app/org/thp/thehive/TheHiveModule.scala b/thehive/app/org/thp/thehive/TheHiveModule.scala index 3c1f6e5a4f..132bf38ddf 100644 --- a/thehive/app/org/thp/thehive/TheHiveModule.scala +++ b/thehive/app/org/thp/thehive/TheHiveModule.scala @@ -6,15 +6,29 @@ import net.codingwell.scalaguice.{ScalaModule, ScalaMultibinder} import org.thp.scalligraph.auth._ import org.thp.scalligraph.janus.JanusDatabase import org.thp.scalligraph.models.{Database, Schema} -import org.thp.scalligraph.services.{HadoopStorageSrv, S3StorageSrv} +import org.thp.scalligraph.services.{GenIntegrityCheckOps, HadoopStorageSrv, S3StorageSrv} +import org.thp.thehive.models.{DatabaseProvider, TheHiveSchemaDefinition} import org.thp.thehive.services.notification.notifiers._ import org.thp.thehive.services.notification.triggers._ -import org.thp.thehive.services.{CaseDedupActorProvider, DataDedupActorProvider, TOTPAuthSrvProvider, TagDedupActorProvider} +import org.thp.thehive.services.{ + CaseIntegrityCheckOps, + CaseTemplateIntegrityCheckOps, + CustomFieldIntegrityCheckOps, + DataIntegrityCheckOps, + ImpactStatusIntegrityCheckOps, + IntegrityCheckActorProvider, + ObservableTypeIntegrityCheckOps, + OrganisationIntegrityCheckOps, + ProfileIntegrityCheckOps, + ResolutionStatusIntegrityCheckOps, + TOTPAuthSrvProvider, + TagIntegrityCheckOps, + UserIntegrityCheckOps +} import play.api.libs.concurrent.AkkaGuiceSupport //import org.thp.scalligraph.orientdb.{OrientDatabase, OrientDatabaseStorageSrv} import org.thp.scalligraph.services.config.ConfigActor import org.thp.scalligraph.services.{DatabaseStorageSrv, LocalFileSystemStorageSrv, StorageSrv} -import org.thp.thehive.models.{SchemaUpdater, TheHiveSchema} import org.thp.thehive.services.notification.NotificationActor import org.thp.thehive.services.{Connector, LocalKeyAuthProvider, LocalPasswordAuthProvider, LocalUserSrv} //import org.thp.scalligraph.neo4j.Neo4jDatabase @@ -62,9 +76,10 @@ class TheHiveModule(environment: Environment, configuration: Configuration) exte notifierBindings.addBinding.to[WebhookProvider] configuration.get[String]("db.provider") match { - case "janusgraph" => bind(classOf[Database]).to(classOf[JanusDatabase]) + case "janusgraph" => bind[Database].to[JanusDatabase] case other => sys.error(s"Authentication provider [$other] is not recognized") } + bind[Database].annotatedWithName("with-thehive-schema").toProvider[DatabaseProvider] configuration.get[String]("storage.provider") match { case "localfs" => bind(classOf[StorageSrv]).to(classOf[LocalFileSystemStorageSrv]) @@ -81,16 +96,25 @@ class TheHiveModule(environment: Environment, configuration: Configuration) exte queryExecutorBindings.addBinding.to[TheHiveQueryExecutorV1] ScalaMultibinder.newSetBinder[Connector](binder) val schemaBindings = ScalaMultibinder.newSetBinder[Schema](binder) - schemaBindings.addBinding.to[TheHiveSchema] + schemaBindings.addBinding.to[TheHiveSchemaDefinition] bindActor[ConfigActor]("config-actor") bindActor[NotificationActor]("notification-actor") - bind[ActorRef].annotatedWithName("data-dedup-actor").toProvider[DataDedupActorProvider] - bind[ActorRef].annotatedWithName("case-dedup-actor").toProvider[CaseDedupActorProvider] - bind[ActorRef].annotatedWithName("tag-dedup-actor").toProvider[TagDedupActorProvider] + val integrityCheckOpsBindings = ScalaMultibinder.newSetBinder[GenIntegrityCheckOps](binder) + integrityCheckOpsBindings.addBinding.to[ProfileIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[OrganisationIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[TagIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[UserIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[ImpactStatusIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[ResolutionStatusIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[ObservableTypeIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[CustomFieldIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[CaseTemplateIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[DataIntegrityCheckOps] + integrityCheckOpsBindings.addBinding.to[CaseIntegrityCheckOps] + bind[ActorRef].annotatedWithName("integrity-check-actor").toProvider[IntegrityCheckActorProvider] - bind[SchemaUpdater].asEagerSingleton() bind[ClusterSetup].asEagerSingleton() () } diff --git a/thehive/app/org/thp/thehive/controllers/dav/Router.scala b/thehive/app/org/thp/thehive/controllers/dav/Router.scala index dd6cd17be3..dc1a4cf036 100644 --- a/thehive/app/org/thp/thehive/controllers/dav/Router.scala +++ b/thehive/app/org/thp/thehive/controllers/dav/Router.scala @@ -2,7 +2,7 @@ package org.thp.thehive.controllers.dav import akka.stream.scaladsl.StreamConverters import akka.util.ByteString -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.thehive.services.AttachmentSrv @@ -18,7 +18,8 @@ import scala.util.matching.Regex import scala.xml.{Node, NodeSeq} @Singleton -class Router @Inject() (entrypoint: Entrypoint, vfs: VFS, db: Database, attachmentSrv: AttachmentSrv) extends SimpleRouter { +class Router @Inject() (entrypoint: Entrypoint, vfs: VFS, @Named("with-thehive-schema") db: Database, attachmentSrv: AttachmentSrv) + extends SimpleRouter { lazy val logger: Logger = Logger(getClass) object PROPFIND { diff --git a/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala index 20269b22a8..1bdc26d529 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/AlertCtrl.scala @@ -4,7 +4,7 @@ import java.util.Base64 import gremlin.scala.{__, By, Graph, Key, StepLabel, Vertex} import io.scalaland.chimney.dsl._ -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.controllers.{Entrypoint, FString, FieldsParser} import org.thp.scalligraph.models.Database @@ -27,7 +27,7 @@ import scala.util.{Failure, Success, Try} @Singleton class AlertCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, alertSrv: AlertSrv, caseTemplateSrv: CaseTemplateSrv, @@ -98,60 +98,63 @@ class AlertCtrl @Inject() ( } yield Results.Created((richAlert -> richObservables).toJson) } - def alertSimilarityRenderer(implicit authContext: AuthContext, db: Database, graph: Graph): AlertSteps => Traversal[JsArray, JsArray] = { - alertSteps => - val observableLabel = StepLabel[Vertex]() - val caseLabel = StepLabel[Vertex]() - Traversal( - alertSteps - .observables - .similar - .as(observableLabel) - .`case` - .as(caseLabel) - .raw - .select(observableLabel.name, caseLabel.name) - .fold - .map { resultMapList => - val similarCases = resultMapList - .asScala - .map { m => - val cid = m.getValue(caseLabel).id() - val ioc = m.getValue(observableLabel).value[Boolean]("ioc") - cid -> ioc - } - .groupBy(_._1) - .map { - case (cid, cidIoc) => - val iocStats = cidIoc.groupBy(_._2).mapValues(_.size) - val (caseVertex, observableCount, resolutionStatus) = caseSrv - .getByIds(cid.toString) - .project( - _(By[Vertex]()) - .and(By(new CaseSteps(__[Vertex]).observables(authContext).groupCount(By(Key[Boolean]("ioc"))).raw)) - .and(By(__[Vertex].outTo[CaseResolutionStatus].values[String]("value").fold)) - ) - .head() - val case0 = caseVertex - .as[Case] - val similarCase = case0 - .asInstanceOf[Case] - .into[OutputSimilarCase] - .withFieldConst(_.artifactCount, observableCount.getOrDefault(false, 0L).toInt) - .withFieldConst(_.iocCount, observableCount.getOrDefault(true, 0L).toInt) - .withFieldConst(_.similarArtifactCount, iocStats.getOrElse(false, 0)) - .withFieldConst(_.similarIOCCount, iocStats.getOrElse(true, 0)) - .withFieldConst(_.resolutionStatus, atMostOneOf[String](resolutionStatus)) - .withFieldComputed(_.status, _.status.toString) - .withFieldConst(_.id, case0._id) - .withFieldConst(_._id, case0._id) - .withFieldRenamed(_.number, _.caseId) - .transform - Json.toJson(similarCase) - } - JsArray(similarCases.toSeq) - } - ) + def alertSimilarityRenderer( + implicit authContext: AuthContext, + @Named("with-thehive-schema") db: Database, + graph: Graph + ): AlertSteps => Traversal[JsArray, JsArray] = { alertSteps => + val observableLabel = StepLabel[Vertex]() + val caseLabel = StepLabel[Vertex]() + Traversal( + alertSteps + .observables + .similar + .as(observableLabel) + .`case` + .as(caseLabel) + .raw + .select(observableLabel.name, caseLabel.name) + .fold + .map { resultMapList => + val similarCases = resultMapList + .asScala + .map { m => + val cid = m.getValue(caseLabel).id() + val ioc = m.getValue(observableLabel).value[Boolean]("ioc") + cid -> ioc + } + .groupBy(_._1) + .map { + case (cid, cidIoc) => + val iocStats = cidIoc.groupBy(_._2).mapValues(_.size) + val (caseVertex, observableCount, resolutionStatus) = caseSrv + .getByIds(cid.toString) + .project( + _(By[Vertex]()) + .and(By(new CaseSteps(__[Vertex]).observables(authContext).groupCount(By(Key[Boolean]("ioc"))).raw)) + .and(By(__[Vertex].outTo[CaseResolutionStatus].values[String]("value").fold)) + ) + .head() + val case0 = caseVertex + .as[Case] + val similarCase = case0 + .asInstanceOf[Case] + .into[OutputSimilarCase] + .withFieldConst(_.artifactCount, observableCount.getOrDefault(false, 0L).toInt) + .withFieldConst(_.iocCount, observableCount.getOrDefault(true, 0L).toInt) + .withFieldConst(_.similarArtifactCount, iocStats.getOrElse(false, 0)) + .withFieldConst(_.similarIOCCount, iocStats.getOrElse(true, 0)) + .withFieldConst(_.resolutionStatus, atMostOneOf[String](resolutionStatus)) + .withFieldComputed(_.status, _.status.toString) + .withFieldConst(_.id, case0._id) + .withFieldConst(_._id, case0._id) + .withFieldRenamed(_.number, _.caseId) + .transform + Json.toJson(similarCase) + } + JsArray(similarCases.toSeq) + } + ) } def get(alertId: String): Action[AnyContent] = diff --git a/thehive/app/org/thp/thehive/controllers/v0/AttachmentCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/AttachmentCtrl.scala index a8269c652f..05c01f930a 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/AttachmentCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/AttachmentCtrl.scala @@ -3,7 +3,7 @@ package org.thp.thehive.controllers.v0 import java.nio.file.Files import akka.stream.scaladsl.FileIO -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import net.lingala.zip4j.ZipFile import net.lingala.zip4j.model.ZipParameters import net.lingala.zip4j.model.enums.{CompressionLevel, EncryptionMethod} @@ -20,7 +20,12 @@ import play.api.mvc._ import scala.util.{Failure, Success, Try} @Singleton -class AttachmentCtrl @Inject() (entrypoint: Entrypoint, appConfig: ApplicationConfig, attachmentSrv: AttachmentSrv, db: Database) { +class AttachmentCtrl @Inject() ( + entrypoint: Entrypoint, + appConfig: ApplicationConfig, + attachmentSrv: AttachmentSrv, + @Named("with-thehive-schema") db: Database +) { val forbiddenChar: Seq[Char] = Seq('/', '\n', '\r', '\t', '\u0000', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':', ';') val passwordConfig: ConfigItem[String, String] = appConfig.item[String]("datastore.attachment.password", "Password used to protect attachment ZIP") diff --git a/thehive/app/org/thp/thehive/controllers/v0/AuditCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/AuditCtrl.scala index 4d35e0d93a..87dc15f754 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/AuditCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/AuditCtrl.scala @@ -3,7 +3,7 @@ package org.thp.thehive.controllers.v0 import java.util.Date import gremlin.scala.{By, Key} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.apache.tinkerpop.gremlin.process.traversal.Order import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database @@ -26,7 +26,7 @@ class AuditCtrl @Inject() ( val caseSrv: CaseSrv, val taskSrv: TaskSrv, val userSrv: UserSrv, - implicit val db: Database + @Named("with-thehive-schema") implicit val db: Database ) extends QueryableCtrl with AuditRenderer { diff --git a/thehive/app/org/thp/thehive/controllers/v0/AuthenticationCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/AuthenticationCtrl.scala index 1cf2fbc09d..84fb90d5bd 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/AuthenticationCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/AuthenticationCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.AuthorizationError import org.thp.scalligraph.auth.{AuthSrv, RequestOrganisation} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} @@ -19,7 +19,7 @@ class AuthenticationCtrl @Inject() ( authSrv: AuthSrv, requestOrganisation: RequestOrganisation, userSrv: UserSrv, - db: Database, + @Named("with-thehive-schema") db: Database, implicit val ec: ExecutionContext ) { diff --git a/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala index 5054fc4327..0310629688 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala @@ -20,7 +20,7 @@ import scala.util.Success @Singleton class CaseCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, caseSrv: CaseSrv, caseTemplateSrv: CaseTemplateSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v0/CaseRenderer.scala b/thehive/app/org/thp/thehive/controllers/v0/CaseRenderer.scala index 3c6ac1a1c1..2e08004bc5 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/CaseRenderer.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/CaseRenderer.scala @@ -2,11 +2,8 @@ package org.thp.thehive.controllers.v0 import java.lang.{Long => JLong} -import scala.collection.JavaConverters._ - -import play.api.libs.json._ - import gremlin.scala.{__, By, Graph, GremlinScala, Key, Vertex} +import javax.inject.Named import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.Database import org.thp.scalligraph.services._ @@ -14,17 +11,22 @@ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.Traversal import org.thp.thehive.models._ import org.thp.thehive.services.{CaseSteps, ShareSteps} +import play.api.libs.json._ + +import scala.collection.JavaConverters._ trait CaseRenderer { - def observableStats(shareTraversal: GremlinScala[Vertex])(implicit db: Database, graph: Graph): GremlinScala[JsObject] = + def observableStats( + shareTraversal: GremlinScala[Vertex] + )(implicit @Named("with-thehive-schema") db: Database, graph: Graph): GremlinScala[JsObject] = new ShareSteps(shareTraversal) .observables .count .map(count => Json.obj("count" -> count)) .raw - def taskStats(shareTraversal: GremlinScala[Vertex])(implicit db: Database, graph: Graph): GremlinScala[JsObject] = + def taskStats(shareTraversal: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph): GremlinScala[JsObject] = new ShareSteps(shareTraversal) .tasks .active @@ -56,19 +58,27 @@ trait CaseRenderer { def mergeIntoStats(caseTraversal: GremlinScala[Vertex]): GremlinScala[Seq[JsObject]] = caseTraversal.constant(Nil) - def sharedWithStats(caseTraversal: GremlinScala[Vertex])(implicit db: Database, graph: Graph): GremlinScala[Seq[String]] = + def sharedWithStats( + caseTraversal: GremlinScala[Vertex] + )(implicit @Named("with-thehive-schema") db: Database, graph: Graph): GremlinScala[Seq[String]] = new CaseSteps(caseTraversal).organisations.name.fold.map(_.asScala.toSeq).raw - def originStats(caseTraversal: GremlinScala[Vertex])(implicit db: Database, graph: Graph): GremlinScala[String] = + def originStats(caseTraversal: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph): GremlinScala[String] = new CaseSteps(caseTraversal).origin.name.raw - def shareCountStats(caseTraversal: GremlinScala[Vertex])(implicit db: Database, graph: Graph): GremlinScala[JLong] = + def shareCountStats(caseTraversal: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph): GremlinScala[JLong] = new CaseSteps(caseTraversal).organisations.count.raw - def isOwnerStats(caseTraversal: GremlinScala[Vertex])(implicit db: Database, graph: Graph, authContext: AuthContext): GremlinScala[Boolean] = + def isOwnerStats( + caseTraversal: GremlinScala[Vertex] + )(implicit @Named("with-thehive-schema") db: Database, graph: Graph, authContext: AuthContext): GremlinScala[Boolean] = new CaseSteps(caseTraversal).origin.name.map(_ == authContext.organisation).raw - def caseStatsRenderer(implicit authContext: AuthContext, db: Database, graph: Graph): CaseSteps => Traversal[JsObject, JsObject] = + def caseStatsRenderer( + implicit authContext: AuthContext, + @Named("with-thehive-schema") db: Database, + graph: Graph + ): CaseSteps => Traversal[JsObject, JsObject] = _.project( _.apply( By( diff --git a/thehive/app/org/thp/thehive/controllers/v0/CaseTemplateCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/CaseTemplateCtrl.scala index d7f7d24d46..0d02c0e2c4 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/CaseTemplateCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/CaseTemplateCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.RichSeq import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database @@ -17,7 +17,7 @@ import play.api.mvc.{Action, AnyContent, Results} @Singleton class CaseTemplateCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, caseTemplateSrv: CaseTemplateSrv, organisationSrv: OrganisationSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v0/CustomFieldCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/CustomFieldCtrl.scala index 807af1615b..f895be9342 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/CustomFieldCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/CustomFieldCtrl.scala @@ -1,11 +1,6 @@ package org.thp.thehive.controllers.v0 -import scala.util.Success - -import play.api.libs.json.{JsNumber, JsObject} -import play.api.mvc.{Action, AnyContent, Results} - -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.query.PropertyUpdater @@ -14,9 +9,18 @@ import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.dto.v0.InputCustomField import org.thp.thehive.models.Permissions import org.thp.thehive.services.CustomFieldSrv +import play.api.libs.json.{JsNumber, JsObject} +import play.api.mvc.{Action, AnyContent, Results} + +import scala.util.Success @Singleton -class CustomFieldCtrl @Inject() (entrypoint: Entrypoint, db: Database, properties: Properties, customFieldSrv: CustomFieldSrv) extends AuditRenderer { +class CustomFieldCtrl @Inject() ( + entrypoint: Entrypoint, + @Named("with-thehive-schema") db: Database, + properties: Properties, + customFieldSrv: CustomFieldSrv +) extends AuditRenderer { def create: Action[AnyContent] = entrypoint("create custom field") diff --git a/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala index 318123ba40..d8eead51aa 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/DashboardCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -15,7 +15,7 @@ import play.api.mvc.{Action, AnyContent, Results} @Singleton class DashboardCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, dashboardSrv: DashboardSrv, organisationSrv: OrganisationSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala index 979645a405..94b717e06f 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/DescribeCtrl.scala @@ -3,7 +3,7 @@ package org.thp.thehive.controllers.v0 import java.lang.{Boolean => JBoolean} import java.util.Date -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.NotFoundError import org.thp.scalligraph.controllers.Entrypoint import org.thp.scalligraph.models.Database @@ -35,7 +35,7 @@ class DescribeCtrl @Inject() ( auditCtrl: AuditCtrl, customFieldSrv: CustomFieldSrv, injector: Injector, - db: Database, + @Named("with-thehive-schema") db: Database, applicationConfig: ApplicationConfig ) { diff --git a/thehive/app/org/thp/thehive/controllers/v0/ListCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ListCtrl.scala index d41c156a83..aaa40847a3 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ListCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ListCtrl.scala @@ -1,21 +1,26 @@ package org.thp.thehive.controllers.v0 -import scala.util.{Failure, Success} - -import play.api.libs.json.{JsObject, JsString, Json} -import play.api.mvc.{Action, AnyContent, Results} - -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.utils.Hasher import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.dto.v0.InputCustomField +import org.thp.thehive.models.ObservableType import org.thp.thehive.services.{CustomFieldSrv, ObservableTypeSrv} +import play.api.libs.json.{JsObject, JsString, Json} +import play.api.mvc.{Action, AnyContent, Results} + +import scala.util.{Failure, Success} @Singleton -class ListCtrl @Inject() (entrypoint: Entrypoint, db: Database, customFieldSrv: CustomFieldSrv, observableTypeSrv: ObservableTypeSrv) { +class ListCtrl @Inject() ( + entrypoint: Entrypoint, + @Named("with-thehive-schema") db: Database, + customFieldSrv: CustomFieldSrv, + observableTypeSrv: ObservableTypeSrv +) { def list: Action[AnyContent] = entrypoint("list") @@ -28,7 +33,7 @@ class ListCtrl @Inject() (entrypoint: Entrypoint, db: Database, customFieldSrv: .auth { _ => val result = listName match { case "list_artifactDataType" => - val objectTypes = observableTypeSrv.initialValues.toList.map { ot => + val objectTypes = ObservableType.initialValues.toList.map { ot => val id = Hasher("MD5").fromString(ot.name).head.toString // this Traversable.head can't fail id -> JsString(ot.name) } diff --git a/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala index d50f6b315b..490b75883e 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/LogCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -16,7 +16,7 @@ import play.api.mvc.{Action, AnyContent, Results} @Singleton class LogCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, logSrv: LogSrv, taskSrv: TaskSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala index e97ff7e285..bc74a166b5 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph._ import org.thp.scalligraph.controllers._ import org.thp.scalligraph.models.Database @@ -20,7 +20,7 @@ import scala.util.Success @Singleton class ObservableCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, observableSrv: ObservableSrv, observableTypeSrv: ObservableTypeSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableTypeCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableTypeCtrl.scala index 39fc595a77..15bd0a9709 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ObservableTypeCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableTypeCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.query.{ParamQuery, PublicProperty, Query} @@ -15,7 +15,7 @@ import play.api.mvc.{Action, AnyContent, Results} @Singleton class ObservableTypeCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, observableTypeSrv: ObservableTypeSrv ) extends QueryableCtrl { diff --git a/thehive/app/org/thp/thehive/controllers/v0/OrganisationCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/OrganisationCtrl.scala index d0fce9e5b3..3445df760d 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/OrganisationCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/OrganisationCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.NotFoundError import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} @@ -18,7 +18,7 @@ import scala.util.{Failure, Success} @Singleton class OrganisationCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, organisationSrv: OrganisationSrv, userSrv: UserSrv @@ -51,7 +51,7 @@ class OrganisationCtrl @Inject() ( .authTransaction(db) { implicit request => implicit graph => val inputOrganisation: InputOrganisation = request.body("organisation") for { - _ <- userSrv.current.organisations(Permissions.manageOrganisation).get(OrganisationSrv.administration.name).existsOrFail() + _ <- userSrv.current.organisations(Permissions.manageOrganisation).get(Organisation.administration.name).existsOrFail() org <- organisationSrv.create(inputOrganisation.toOrganisation) } yield Results.Created(org.toJson) @@ -128,7 +128,7 @@ class OrganisationCtrl @Inject() ( def listLinks(organisationId: String): Action[AnyContent] = entrypoint("list organisation links") .authRoTransaction(db) { implicit request => implicit graph => - val isInDefaultOrganisation = userSrv.current.organisations.get(OrganisationSrv.administration.name).exists() + val isInDefaultOrganisation = userSrv.current.organisations.get(Organisation.administration.name).exists() val organisation = if (isInDefaultOrganisation) organisationSrv.get(organisationId) diff --git a/thehive/app/org/thp/thehive/controllers/v0/PageCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/PageCtrl.scala index 4a7d9f9fb6..39bd932da4 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/PageCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/PageCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -13,8 +13,13 @@ import org.thp.thehive.services.{OrganisationSrv, PageSrv, PageSteps} import play.api.mvc._ @Singleton -class PageCtrl @Inject() (entrypoint: Entrypoint, pageSrv: PageSrv, db: Database, properties: Properties, organisationSrv: OrganisationSrv) - extends QueryableCtrl { +class PageCtrl @Inject() ( + entrypoint: Entrypoint, + pageSrv: PageSrv, + @Named("with-thehive-schema") db: Database, + properties: Properties, + organisationSrv: OrganisationSrv +) extends QueryableCtrl { override val entityName: String = "page" override val publicProperties: List[PublicProperty[_, _]] = properties.page ::: metaProperties[PageSteps] diff --git a/thehive/app/org/thp/thehive/controllers/v0/ProfileCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ProfileCtrl.scala index 8da52c9af9..82f509a32a 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ProfileCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ProfileCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.AuthorizationError import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} @@ -16,7 +16,8 @@ import play.api.mvc.{Action, AnyContent, Results} import scala.util.Failure @Singleton -class ProfileCtrl @Inject() (entrypoint: Entrypoint, db: Database, properties: Properties, profileSrv: ProfileSrv) extends QueryableCtrl { +class ProfileCtrl @Inject() (entrypoint: Entrypoint, @Named("with-thehive-schema") db: Database, properties: Properties, profileSrv: ProfileSrv) + extends QueryableCtrl { override val getQuery: ParamQuery[IdOrName] = Query.initWithParam[IdOrName, ProfileSteps]( "getProfile", diff --git a/thehive/app/org/thp/thehive/controllers/v0/QueryCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/QueryCtrl.scala index 472a1afdd3..5d4cbf16dd 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/QueryCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/QueryCtrl.scala @@ -1,7 +1,7 @@ package org.thp.thehive.controllers.v0 import gremlin.scala.Graph -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.apache.tinkerpop.gremlin.process.traversal.Order import org.scalactic.Accumulation._ import org.scalactic.Good @@ -36,7 +36,7 @@ trait QueryableCtrl { .build } -class QueryCtrl(entrypoint: Entrypoint, db: Database, ctrl: QueryableCtrl, queryExecutor: QueryExecutor) { +class QueryCtrl(entrypoint: Entrypoint, @Named("with-thehive-schema") db: Database, ctrl: QueryableCtrl, queryExecutor: QueryExecutor) { lazy val logger: Logger = Logger(getClass) val publicProperties: List[PublicProperty[_, _]] = queryExecutor.publicProperties @@ -140,7 +140,7 @@ class QueryCtrl(entrypoint: Entrypoint, db: Database, ctrl: QueryableCtrl, query } @Singleton -class QueryCtrlBuilder @Inject() (entrypoint: Entrypoint, db: Database) { +class QueryCtrlBuilder @Inject() (entrypoint: Entrypoint, @Named("with-thehive-schema") db: Database) { def apply(ctrl: QueryableCtrl, queryExecutor: QueryExecutor): QueryCtrl = new QueryCtrl(entrypoint, db, ctrl, queryExecutor) diff --git a/thehive/app/org/thp/thehive/controllers/v0/ShareCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ShareCtrl.scala index 8b5c41a401..3a49a8141e 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ShareCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ShareCtrl.scala @@ -1,11 +1,7 @@ package org.thp.thehive.controllers.v0 -import scala.util.{Failure, Success, Try} - -import play.api.mvc.{Action, AnyContent, Results} - import gremlin.scala.Graph -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database @@ -15,11 +11,14 @@ import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.dto.v0.{InputShare, ObservablesFilter, TasksFilter} import org.thp.thehive.models.Permissions import org.thp.thehive.services._ +import play.api.mvc.{Action, AnyContent, Results} + +import scala.util.{Failure, Success, Try} @Singleton class ShareCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, shareSrv: ShareSrv, organisationSrv: OrganisationSrv, caseSrv: CaseSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v0/StatsCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/StatsCtrl.scala index e0ecba10ff..6be624c919 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/StatsCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/StatsCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.scalactic.Accumulation._ import org.thp.scalligraph.AttributeCheckingError import org.thp.scalligraph.controllers.{Entrypoint, Field, FieldsParser} @@ -13,7 +13,7 @@ import play.api.mvc.{Action, AnyContent, Results} class StatsCtrl @Inject() ( entrypoint: Entrypoint, queryExecutor: TheHiveQueryExecutor, - db: Database + @Named("with-thehive-schema") db: Database ) { lazy val logger: Logger = Logger(getClass) diff --git a/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala index 65e2d5ed2b..c15664976e 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/StatusCtrl.scala @@ -1,20 +1,19 @@ package org.thp.thehive.controllers.v0 -import scala.collection.immutable -import scala.util.Success - -import play.api.libs.json.{JsObject, JsString, Json} -import play.api.mvc.{AbstractController, Action, AnyContent, Results} - -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.ScalligraphApplicationLoader import org.thp.scalligraph.auth.{AuthCapability, AuthSrv, MultiAuthSrv} import org.thp.scalligraph.controllers.Entrypoint import org.thp.scalligraph.models.Database import org.thp.scalligraph.services.config.{ApplicationConfig, ConfigItem} import org.thp.thehive.TheHiveModule -import org.thp.thehive.models.HealthStatus +import org.thp.thehive.models.{HealthStatus, User} import org.thp.thehive.services.{Connector, UserSrv} +import play.api.libs.json.{JsObject, JsString, Json} +import play.api.mvc.{AbstractController, Action, AnyContent, Results} + +import scala.collection.immutable +import scala.util.Success @Singleton class StatusCtrl @Inject() ( @@ -23,7 +22,7 @@ class StatusCtrl @Inject() ( authSrv: AuthSrv, userSrv: UserSrv, connectors: immutable.Set[Connector], - db: Database + @Named("with-thehive-schema") db: Database ) { val passwordConfig: ConfigItem[String, String] = appConfig.item[String]("datastore.attachment.password", "Password used to protect attachment ZIP") @@ -60,7 +59,7 @@ class StatusCtrl @Inject() ( def health: Action[AnyContent] = entrypoint("health") { _ => val dbStatus = db - .roTransaction(graph => userSrv.getOrFail(UserSrv.system.login)(graph)) + .roTransaction(graph => userSrv.getOrFail(User.system.login)(graph)) .fold(_ => HealthStatus.Error, _ => HealthStatus.Ok) val connectorStatus = connectors.map(c => c.health) val distinctStatus = connectorStatus + dbStatus diff --git a/thehive/app/org/thp/thehive/controllers/v0/StreamCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/StreamCtrl.scala index 5271fb6e38..4252905ebe 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/StreamCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/StreamCtrl.scala @@ -1,18 +1,17 @@ package org.thp.thehive.controllers.v0 -import scala.concurrent.ExecutionContext -import scala.util.Success - -import play.api.libs.json.{JsArray, JsObject, Json} -import play.api.mvc.{Action, AnyContent, Results} - -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.apache.tinkerpop.gremlin.process.traversal.Order import org.thp.scalligraph.controllers.Entrypoint import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.services._ +import play.api.libs.json.{JsArray, JsObject, Json} +import play.api.mvc.{Action, AnyContent, Results} + +import scala.concurrent.ExecutionContext +import scala.util.Success @Singleton class StreamCtrl @Inject() ( @@ -22,7 +21,7 @@ class StreamCtrl @Inject() ( val caseSrv: CaseSrv, val taskSrv: TaskSrv, val userSrv: UserSrv, - implicit val db: Database, + @Named("with-thehive-schema") implicit val db: Database, implicit val ec: ExecutionContext ) extends AuditRenderer { diff --git a/thehive/app/org/thp/thehive/controllers/v0/TagCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/TagCtrl.scala index b4e77d1bcb..ab1b463ba1 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/TagCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/TagCtrl.scala @@ -2,7 +2,7 @@ package org.thp.thehive.controllers.v0 import java.nio.file.Files -import javax.inject.Inject +import javax.inject.{Inject, Named} import org.thp.scalligraph.RichSeq import org.thp.scalligraph.controllers.{Entrypoint, FFile, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} @@ -19,7 +19,7 @@ import scala.util.Try class TagCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, tagSrv: TagSrv ) extends QueryableCtrl { diff --git a/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala index 8048dc1f1c..109101553b 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/TaskCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.RichOptionTry import org.thp.scalligraph.controllers._ import org.thp.scalligraph.models.Database @@ -17,7 +17,7 @@ import play.api.mvc.{Action, AnyContent, Results} @Singleton class TaskCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, taskSrv: TaskSrv, caseSrv: CaseSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala b/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala index 794211165e..e84d0dd953 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/TheHiveQueryExecutor.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.scalactic.Good import org.thp.scalligraph.BadRequestError import org.thp.scalligraph.auth.AuthContext @@ -29,7 +29,7 @@ object OutputParam { @Singleton class TheHiveQueryExecutor @Inject() ( - override val db: Database, + @Named("with-thehive-schema") override val db: Database, caseCtrl: CaseCtrl, taskCtrl: TaskCtrl, logCtrl: LogCtrl, diff --git a/thehive/app/org/thp/thehive/controllers/v0/UserCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/UserCtrl.scala index aab3b0d546..dc8eb06ab4 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/UserCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/UserCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v0 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthSrv import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database @@ -22,7 +22,7 @@ import scala.util.{Failure, Success} @Singleton class UserCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, userSrv: UserSrv, profileSrv: ProfileSrv, @@ -64,7 +64,7 @@ class UserCtrl @Inject() ( .orElse( userSrv .get(request.userId) - .richUser(OrganisationSrv.administration.name) + .richUser(Organisation.administration.name) .getOrFail("User") ) .map(user => Results.Ok(user.toJson).withHeaders("X-Organisation" -> request.organisation)) @@ -80,10 +80,10 @@ class UserCtrl @Inject() ( for { _ <- userSrv.current.organisations(Permissions.manageUser).get(organisationName).existsOrFail() organisation <- organisationSrv.getOrFail(organisationName) - profile <- if (inputUser.roles.contains("admin")) profileSrv.getOrFail(ProfileSrv.admin.name) - else if (inputUser.roles.contains("write")) profileSrv.getOrFail(ProfileSrv.analyst.name) - else if (inputUser.roles.contains("read")) profileSrv.getOrFail(ProfileSrv.readonly.name) - else profileSrv.getOrFail(ProfileSrv.readonly.name) + profile <- if (inputUser.roles.contains("admin")) profileSrv.getOrFail(Profile.admin.name) + else if (inputUser.roles.contains("write")) profileSrv.getOrFail(Profile.analyst.name) + else if (inputUser.roles.contains("read")) profileSrv.getOrFail(Profile.readonly.name) + else profileSrv.getOrFail(Profile.readonly.name) user <- userSrv.addOrCreateUser(inputUser.toUser, inputUser.avatar, organisation, profile) } yield user -> userSrv.canSetPassword(user.user) } diff --git a/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala index 9f28339b7e..d3feb525f3 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v1 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -15,7 +15,7 @@ import play.api.mvc.{Action, AnyContent, Results} @Singleton class AlertCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, alertSrv: AlertSrv, caseTemplateSrv: CaseTemplateSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v1/AuthenticationCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/AuthenticationCtrl.scala index 8be477dc6a..4549b499b3 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/AuthenticationCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/AuthenticationCtrl.scala @@ -1,19 +1,20 @@ package org.thp.thehive.controllers.v1 -import scala.collection.JavaConverters._ -import scala.concurrent.ExecutionContext -import scala.util.{Failure, Success, Try} -import play.api.mvc.{Action, AnyContent, Results} -import javax.inject.{Inject, Singleton} -import org.thp.scalligraph.{AuthenticationError, AuthorizationError, BadRequestError, MultiFactorCodeRequired} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.{AuthSrv, RequestOrganisation} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database +import org.thp.scalligraph.steps.StepsOps._ +import org.thp.scalligraph.{AuthenticationError, AuthorizationError, BadRequestError, MultiFactorCodeRequired} +import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models.Permissions import org.thp.thehive.services.{TOTPAuthSrv, UserSrv} -import org.thp.thehive.controllers.v1.Conversion._ -import org.thp.scalligraph.steps.StepsOps._ import play.api.libs.json.Json +import play.api.mvc.{Action, AnyContent, Results} + +import scala.collection.JavaConverters._ +import scala.concurrent.ExecutionContext +import scala.util.{Failure, Success, Try} @Singleton class AuthenticationCtrl @Inject() ( @@ -21,7 +22,7 @@ class AuthenticationCtrl @Inject() ( authSrv: AuthSrv, requestOrganisation: RequestOrganisation, userSrv: UserSrv, - db: Database, + @Named("with-thehive-schema") db: Database, implicit val ec: ExecutionContext ) { diff --git a/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala index 082a5df918..9fac53e3bb 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v1 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -18,7 +18,7 @@ import scala.util.{Success, Try} @Singleton class CaseCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, caseSrv: CaseSrv, caseTemplateSrv: CaseTemplateSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v1/CaseTemplateCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/CaseTemplateCtrl.scala index 5a9b846cf0..a8f45e36c6 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/CaseTemplateCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/CaseTemplateCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v1 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -17,7 +17,7 @@ import scala.util.Success @Singleton class CaseTemplateCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, caseTemplateSrv: CaseTemplateSrv, organisationSrv: OrganisationSrv diff --git a/thehive/app/org/thp/thehive/controllers/v1/CustomFieldCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/CustomFieldCtrl.scala index 4e9989f35c..055bbf6350 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/CustomFieldCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/CustomFieldCtrl.scala @@ -1,19 +1,18 @@ package org.thp.thehive.controllers.v1 -import scala.util.Success - -import play.api.mvc.{Action, AnyContent, Results} - -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ import org.thp.thehive.services.CustomFieldSrv +import play.api.mvc.{Action, AnyContent, Results} + +import scala.util.Success @Singleton -class CustomFieldCtrl @Inject() (entrypoint: Entrypoint, db: Database, customFieldSrv: CustomFieldSrv) { +class CustomFieldCtrl @Inject() (entrypoint: Entrypoint, @Named("with-thehive-schema") db: Database, customFieldSrv: CustomFieldSrv) { def create: Action[AnyContent] = entrypoint("create custom field") diff --git a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala index 02433e3e9e..45891ab3ab 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v1 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph._ import org.thp.scalligraph.controllers._ import org.thp.scalligraph.models.Database @@ -20,7 +20,7 @@ import scala.util.Success @Singleton class ObservableCtrl @Inject() ( entryPoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, observableSrv: ObservableSrv, observableTypeSrv: ObservableTypeSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v1/ObservableRenderer.scala b/thehive/app/org/thp/thehive/controllers/v1/ObservableRenderer.scala index fe11cc0093..743216499c 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/ObservableRenderer.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/ObservableRenderer.scala @@ -1,20 +1,24 @@ package org.thp.thehive.controllers.v1 -import scala.collection.JavaConverters._ - -import play.api.libs.json.{JsObject, Json} - import gremlin.scala.{__, By, Graph, Key, Vertex} +import javax.inject.Named import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.Traversal import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.services.ObservableSteps +import play.api.libs.json.{JsObject, Json} + +import scala.collection.JavaConverters._ trait ObservableRenderer { - def observableStatsRenderer(implicit authContext: AuthContext, db: Database, graph: Graph): ObservableSteps => Traversal[JsObject, JsObject] = + def observableStatsRenderer( + implicit authContext: AuthContext, + @Named("with-thehive-schema") db: Database, + graph: Graph + ): ObservableSteps => Traversal[JsObject, JsObject] = _.project( _.apply(By(new ObservableSteps(__[Vertex]).shares.organisation.name.fold.raw)) .and( diff --git a/thehive/app/org/thp/thehive/controllers/v1/OrganisationCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/OrganisationCtrl.scala index d9efaab4bb..ee52e84d3d 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/OrganisationCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/OrganisationCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v1 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -15,7 +15,7 @@ import play.api.mvc.{Action, AnyContent, Results} @Singleton class OrganisationCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, organisationSrv: OrganisationSrv, userSrv: UserSrv @@ -39,7 +39,8 @@ class OrganisationCtrl @Inject() ( override val extraQueries: Seq[ParamQuery[_]] = Seq( Query[OrganisationSteps, OrganisationSteps]("visible", (organisationSteps, _) => organisationSteps.visibleOrganisationsFrom), Query[OrganisationSteps, UserSteps]("users", (organisationSteps, _) => organisationSteps.users), - Query[OrganisationSteps, CaseTemplateSteps]("caseTemplates", (organisationSteps, _) => organisationSteps.caseTemplates) + Query[OrganisationSteps, CaseTemplateSteps]("caseTemplates", (organisationSteps, _) => organisationSteps.caseTemplates), + Query[OrganisationSteps, AlertSteps]("alerts", (organisationSteps, _) => organisationSteps.alerts) ) def create: Action[AnyContent] = diff --git a/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala index ff85496532..fc12837d7f 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/TaskCtrl.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v1 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperty, Query} @@ -17,7 +17,7 @@ import scala.util.Success @Singleton class TaskCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, taskSrv: TaskSrv, caseSrv: CaseSrv, diff --git a/thehive/app/org/thp/thehive/controllers/v1/TheHiveQueryExecutor.scala b/thehive/app/org/thp/thehive/controllers/v1/TheHiveQueryExecutor.scala index a8a0937a6e..ad38625b10 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/TheHiveQueryExecutor.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/TheHiveQueryExecutor.scala @@ -1,6 +1,6 @@ package org.thp.thehive.controllers.v1 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.controllers.{FObject, FieldsParser} import org.thp.scalligraph.models.Database import org.thp.scalligraph.query._ @@ -30,7 +30,7 @@ class TheHiveQueryExecutor @Inject() ( // dashboardCtrl: DashboardCtrl, organisationCtrl: OrganisationCtrl, auditCtrl: AuditCtrl, - implicit val db: Database + @Named("with-thehive-schema") implicit val db: Database ) extends QueryExecutor { lazy val controllers: List[QueryableCtrl] = diff --git a/thehive/app/org/thp/thehive/controllers/v1/UserCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/UserCtrl.scala index 5b0efc69be..fdcf12d7f1 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/UserCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/UserCtrl.scala @@ -2,7 +2,7 @@ package org.thp.thehive.controllers.v1 import java.util.Base64 -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthSrv import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} import org.thp.scalligraph.models.Database @@ -25,7 +25,7 @@ import scala.util.{Failure, Success, Try} @Singleton class UserCtrl @Inject() ( entrypoint: Entrypoint, - db: Database, + @Named("with-thehive-schema") db: Database, properties: Properties, userSrv: UserSrv, authSrv: AuthSrv, diff --git a/thehive/app/org/thp/thehive/models/CustomField.scala b/thehive/app/org/thp/thehive/models/CustomField.scala index 73de17d7ca..fe4152c1c1 100644 --- a/thehive/app/org/thp/thehive/models/CustomField.scala +++ b/thehive/app/org/thp/thehive/models/CustomField.scala @@ -2,11 +2,13 @@ package org.thp.thehive.models import java.util.Date -import scala.util.{Failure, Success, Try} -import play.api.libs.json._ import gremlin.scala.Edge +import javax.inject.Named import org.thp.scalligraph._ -import org.thp.scalligraph.models.{Database, DefineIndex, Entity, IndexType, Model, UniMapping} +import org.thp.scalligraph.models._ +import play.api.libs.json._ + +import scala.util.{Failure, Success, Try} trait CustomFieldValue[C] extends Product { def order: Option[Int] @@ -23,7 +25,7 @@ trait CustomFieldValue[C] extends Product { def dateValue_=(value: Option[Date]): C } -class CustomFieldValueEdge(db: Database, edge: Edge) extends CustomFieldValue[CustomFieldValueEdge] with Entity { +class CustomFieldValueEdge(@Named("with-thehive-schema") db: Database, edge: Edge) extends CustomFieldValue[CustomFieldValueEdge] with Entity { override def order: Option[Int] = db.getOptionProperty(edge, "order", UniMapping.int.optional) override def stringValue: Option[String] = db.getOptionProperty(edge, "stringValue", UniMapping.string.optional) override def booleanValue: Option[Boolean] = db.getOptionProperty(edge, "booleanValue", UniMapping.boolean.optional) diff --git a/thehive/app/org/thp/thehive/models/SchemaUpdater.scala b/thehive/app/org/thp/thehive/models/SchemaUpdater.scala deleted file mode 100644 index 287b916467..0000000000 --- a/thehive/app/org/thp/thehive/models/SchemaUpdater.scala +++ /dev/null @@ -1,15 +0,0 @@ -package org.thp.thehive.models - -import javax.inject.{Inject, Singleton} -import org.thp.scalligraph.auth.UserSrv -import org.thp.scalligraph.models.Database -import play.api.inject.ApplicationLifecycle - -import scala.concurrent.Future - -@Singleton -class SchemaUpdater @Inject() (theHiveSchema: TheHiveSchema, db: Database, userSrv: UserSrv, applicationLifeCycle: ApplicationLifecycle) { - applicationLifeCycle - .addStopHook(() => Future.successful(db.close())) - theHiveSchema.operations.execute(db, theHiveSchema)(userSrv.getSystemAuthContext).get -} diff --git a/thehive/app/org/thp/thehive/models/SchemaUpdaterActor.scala b/thehive/app/org/thp/thehive/models/SchemaUpdaterActor.scala new file mode 100644 index 0000000000..762bc9a57e --- /dev/null +++ b/thehive/app/org/thp/thehive/models/SchemaUpdaterActor.scala @@ -0,0 +1,86 @@ +package org.thp.thehive.models + +import akka.actor.{Actor, ActorRef, ActorSystem, PoisonPill, Props} +import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings, ClusterSingletonProxy, ClusterSingletonProxySettings} +import akka.pattern.ask +import akka.util.Timeout +import javax.inject.{Inject, Provider, Singleton} +import org.thp.scalligraph.models.Database +import org.thp.thehive.services.LocalUserSrv +import play.api.Logger + +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import scala.util.Try + +@Singleton +class DatabaseProvider @Inject() ( + database: Database, + theHiveSchema: TheHiveSchemaDefinition, + actorSystem: ActorSystem +) extends Provider[Database] { + import SchemaUpdaterActor._ + lazy val schemaUpdaterActor: ActorRef = { + val singletonManager = + actorSystem.actorOf( + ClusterSingletonManager.props( + singletonProps = Props(classOf[SchemaUpdaterActor], theHiveSchema, database), + terminationMessage = PoisonPill, + settings = ClusterSingletonManagerSettings(actorSystem) + ), + name = "theHiveSchemaUpdaterSingletonManager" + ) + + actorSystem.actorOf( + ClusterSingletonProxy.props( + singletonManagerPath = singletonManager.path.toStringWithoutAddress, + settings = ClusterSingletonProxySettings(actorSystem) + ), + name = "theHiveSchemaUpdaterSingletonProxy" + ) + } + + override def get(): Database = { + implicit val timeout: Timeout = Timeout(5.minutes) + Await.result(schemaUpdaterActor ? RequestDBStatus, timeout.duration) match { + case DBStatus(status) => + status.get + database.asInstanceOf[Database] + } + } +} + +object SchemaUpdaterActor { + case object RequestDBStatus + case class DBStatus(status: Try[Unit]) +} + +class SchemaUpdaterActor @Inject() (theHiveSchema: TheHiveSchemaDefinition, database: Database) extends Actor { + import SchemaUpdaterActor._ + lazy val logger: Logger = Logger(getClass) + + def update(): Try[Unit] = { + theHiveSchema + .update(database)(LocalUserSrv.getSystemAuthContext) + .recover { + case error => logger.error(s"Database with TheHiveSchema schema update failure", error) + } + database.addSchemaIndexes(theHiveSchema) + } + + override def receive: Receive = { + case RequestDBStatus => + val status = update() + sender ! DBStatus(status) + context.become(receive(status)) + } + + def receive(status: Try[Unit]): Receive = { + case RequestDBStatus => + status.fold({ _ => + val newStatus = update() + sender ! DBStatus(newStatus) + context.become(receive(newStatus)) + }, _ => sender ! DBStatus(status)) + } +} diff --git a/thehive/app/org/thp/thehive/models/TheHiveSchema.scala b/thehive/app/org/thp/thehive/models/TheHiveSchema.scala index ae91e305c2..c968e18ee9 100644 --- a/thehive/app/org/thp/thehive/models/TheHiveSchema.scala +++ b/thehive/app/org/thp/thehive/models/TheHiveSchema.scala @@ -2,11 +2,6 @@ package org.thp.thehive.models import java.lang.reflect.Modifier -import scala.collection.JavaConverters._ -import scala.reflect.runtime.{universe => ru} -import scala.util.{Success, Try} -import play.api.Logger -import play.api.inject.Injector import gremlin.scala.{Graph, Key} import javax.inject.{Inject, Singleton} import org.janusgraph.core.schema.ConsistencyModifier @@ -15,13 +10,17 @@ import org.reflections.scanners.SubTypesScanner import org.reflections.util.ConfigurationBuilder import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.janus.JanusDatabase -import org.thp.scalligraph.models.{HasModel, IndexType, InitialValue, Model, Operations, Schema, UpdatableSchema} -import org.thp.scalligraph.services.VertexSrv -import org.thp.thehive.services.{OrganisationSrv, ProfileSrv, RoleSrv, UserSrv} +import org.thp.scalligraph.models._ import org.thp.scalligraph.steps.StepsOps._ +import play.api.Logger +import play.api.inject.Injector + +import scala.collection.JavaConverters._ +import scala.reflect.runtime.{universe => ru} +import scala.util.{Success, Try} @Singleton -class TheHiveSchema @Inject() (injector: Injector) extends Schema with UpdatableSchema { +class TheHiveSchemaDefinition @Inject() (injector: Injector) extends Schema with UpdatableSchema { lazy val logger: Logger = Logger(getClass) val name: String = "thehive" @@ -65,7 +64,7 @@ class TheHiveSchema @Inject() (injector: Injector) extends Schema with Updatable removePropertyLock("data") } .addIndex("Tag", IndexType.tryUnique, "namespace", "predicate", "value") - .dbOperation[JanusDatabase]("Enable indexes")(_.enableIndexes()) + .addIndex("Audit", IndexType.basic, "requestId", "mainAction") val reflectionClasses = new Reflections( new ConfigurationBuilder() @@ -89,22 +88,9 @@ class TheHiveSchema @Inject() (injector: Injector) extends Schema with Updatable .toSeq } - override lazy val initialValues: Seq[InitialValue[_]] = - reflectionClasses - .getSubTypesOf(classOf[VertexSrv[_, _]]) - .asScala - .filterNot(c => Modifier.isAbstract(c.getModifiers)) - .toSeq - .map { vertexSrvClass => - injector.instanceOf(vertexSrvClass).getInitialValues - } - .flatten + override lazy val initialValues: Seq[InitialValue[_]] = modelList.collect { + case vertexModel: VertexModel => vertexModel.getInitialValues + }.flatten - override def init(implicit graph: Graph, authContext: AuthContext): Try[Unit] = - for { - adminUser <- injector.instanceOf[UserSrv].getOrFail(UserSrv.init.login) - adminProfile <- injector.instanceOf[ProfileSrv].getOrFail(ProfileSrv.admin.name) - adminOrganisation <- injector.instanceOf[OrganisationSrv].getOrFail(OrganisationSrv.administration.name) - _ <- injector.instanceOf[RoleSrv].create(adminUser, adminOrganisation, adminProfile) - } yield () + override def init(db: Database)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = Success(()) } diff --git a/thehive/app/org/thp/thehive/services/AlertSrv.scala b/thehive/app/org/thp/thehive/services/AlertSrv.scala index 62487b80ca..9226e9c1b0 100644 --- a/thehive/app/org/thp/thehive/services/AlertSrv.scala +++ b/thehive/app/org/thp/thehive/services/AlertSrv.scala @@ -2,13 +2,8 @@ package org.thp.thehive.services import java.util.{Date, List => JList} -import scala.collection.JavaConverters._ -import scala.util.{Failure, Try} - -import play.api.libs.json.{JsObject, Json} - import gremlin.scala._ -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.apache.tinkerpop.gremlin.process.traversal.Path import org.thp.scalligraph.auth.{AuthContext, Permission} import org.thp.scalligraph.models._ @@ -19,6 +14,10 @@ import org.thp.scalligraph.steps.{Traversal, TraversalLike, VertexSteps} import org.thp.scalligraph.{CreateError, EntitySteps, InternalError, RichJMap, RichOptionTry, RichSeq} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ +import play.api.libs.json.{JsObject, Json} + +import scala.collection.JavaConverters._ +import scala.util.{Failure, Try} @Singleton class AlertSrv @Inject() ( @@ -30,7 +29,7 @@ class AlertSrv @Inject() ( observableSrv: ObservableSrv, auditSrv: AuditSrv )( - implicit db: Database + implicit @Named("with-thehive-schema") db: Database ) extends VertexSrv[Alert, AlertSteps] { val alertTagSrv = new EdgeSrv[AlertTag, Alert, Tag] @@ -286,7 +285,7 @@ class AlertSrv @Inject() ( } @EntitySteps[Alert] -class AlertSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Alert](raw) { +class AlertSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Alert](raw) { override def newInstance(newRaw: GremlinScala[Vertex] = raw): AlertSteps = new AlertSteps(newRaw) def get(idOrSource: String): AlertSteps = idOrSource.split(';') match { diff --git a/thehive/app/org/thp/thehive/services/AttachmentSrv.scala b/thehive/app/org/thp/thehive/services/AttachmentSrv.scala index 6232e3265a..b54669fee9 100644 --- a/thehive/app/org/thp/thehive/services/AttachmentSrv.scala +++ b/thehive/app/org/thp/thehive/services/AttachmentSrv.scala @@ -3,17 +3,12 @@ package org.thp.thehive.services import java.io.InputStream import java.nio.file.Files -import scala.concurrent.Future -import scala.util.Try - -import play.api.Configuration - import akka.NotUsed -import akka.stream.{IOResult, Materializer} import akka.stream.scaladsl.{Source, StreamConverters} +import akka.stream.{IOResult, Materializer} import akka.util.ByteString import gremlin.scala.{Graph, GremlinScala, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.EntitySteps import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.controllers.FFile @@ -23,10 +18,16 @@ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.VertexSteps import org.thp.scalligraph.utils.Hasher import org.thp.thehive.models.Attachment +import play.api.Configuration + +import scala.concurrent.Future +import scala.util.Try @Singleton -class AttachmentSrv @Inject() (configuration: Configuration, storageSrv: StorageSrv)(implicit db: Database, mat: Materializer) - extends VertexSrv[Attachment, AttachmentSteps] { +class AttachmentSrv @Inject() (configuration: Configuration, storageSrv: StorageSrv)( + implicit @Named("with-thehive-schema") db: Database, + mat: Materializer +) extends VertexSrv[Attachment, AttachmentSteps] { val hashers: Hasher = Hasher(configuration.get[Seq[String]]("attachment.hash"): _*) @@ -80,7 +81,8 @@ class AttachmentSrv @Inject() (configuration: Configuration, storageSrv: Storage } @EntitySteps[Attachment] -class AttachmentSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Attachment](raw) { +class AttachmentSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) + extends VertexSteps[Attachment](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): AttachmentSteps = new AttachmentSteps(newRaw) override def newInstance(): AttachmentSteps = new AttachmentSteps(raw.clone()) diff --git a/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala b/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala index 9e8f74bab2..c8e0385787 100644 --- a/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala +++ b/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala @@ -25,8 +25,9 @@ class CaseTemplateSrv @Inject() ( organisationSrv: OrganisationSrv, tagSrv: TagSrv, taskSrv: TaskSrv, - auditSrv: AuditSrv -)(implicit db: Database) + auditSrv: AuditSrv, + @Named("integrity-check-actor") integrityCheckActor: ActorRef +)(implicit @Named("with-thehive-schema") db: Database) extends VertexSrv[CaseTemplate, CaseTemplateSteps] { val caseTemplateTagSrv = new EdgeSrv[CaseTemplateTag, CaseTemplate, Tag] diff --git a/thehive/app/org/thp/thehive/services/ConfigContext.scala b/thehive/app/org/thp/thehive/services/ConfigContext.scala index 2ca78046e9..16b5a9c29d 100644 --- a/thehive/app/org/thp/thehive/services/ConfigContext.scala +++ b/thehive/app/org/thp/thehive/services/ConfigContext.scala @@ -1,16 +1,15 @@ package org.thp.thehive.services -import scala.util.Try - -import play.api.libs.json.JsValue - -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.Database import org.thp.scalligraph.services.config.ConfigContext +import play.api.libs.json.JsValue + +import scala.util.Try @Singleton -class UserConfigContext @Inject() (db: Database, configSrv: ConfigSrv) extends ConfigContext[AuthContext] { +class UserConfigContext @Inject() (@Named("with-thehive-schema") db: Database, configSrv: ConfigSrv) extends ConfigContext[AuthContext] { override def defaultPath(path: String): String = s"user.defaults.$path" override def getValue(context: AuthContext, path: String): Option[JsValue] = @@ -36,7 +35,7 @@ class UserConfigContext @Inject() (db: Database, configSrv: ConfigSrv) extends C } @Singleton -class OrganisationConfigContext @Inject() (db: Database, configSrv: ConfigSrv) extends ConfigContext[AuthContext] { +class OrganisationConfigContext @Inject() (@Named("with-thehive-schema") db: Database, configSrv: ConfigSrv) extends ConfigContext[AuthContext] { override def defaultPath(path: String): String = s"organisation.defaults.$path" override def getValue(context: AuthContext, path: String): Option[JsValue] = diff --git a/thehive/app/org/thp/thehive/services/ConfigSrv.scala b/thehive/app/org/thp/thehive/services/ConfigSrv.scala index 5ba50bbd28..5932cd1068 100644 --- a/thehive/app/org/thp/thehive/services/ConfigSrv.scala +++ b/thehive/app/org/thp/thehive/services/ConfigSrv.scala @@ -1,12 +1,7 @@ package org.thp.thehive.services -import scala.collection.JavaConverters._ -import scala.util.Try - -import play.api.libs.json.{JsValue, Reads} - import gremlin.scala.{Graph, GremlinScala, Key, P, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.EntitySteps import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} @@ -16,13 +11,17 @@ import org.thp.scalligraph.steps.{Traversal, VertexSteps} import org.thp.thehive.models._ import org.thp.thehive.services.notification.NotificationSrv import org.thp.thehive.services.notification.triggers.Trigger +import play.api.libs.json.{JsValue, Reads} import shapeless.HNil +import scala.collection.JavaConverters._ +import scala.util.Try + @Singleton class ConfigSrv @Inject() ( organisationSrv: OrganisationSrv, userSrv: UserSrv -)(implicit val db: Database) +)(@Named("with-thehive-schema") implicit val db: Database) extends VertexSrv[Config, ConfigSteps] { val organisationConfigSrv = new EdgeSrv[OrganisationConfig, Organisation, Config] val userConfigSrv = new EdgeSrv[UserConfig, User, Config] @@ -76,7 +75,7 @@ class ConfigSrv @Inject() ( } @EntitySteps[Config] -class ConfigSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Config](raw) { +class ConfigSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Config](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): ConfigSteps = new ConfigSteps(newRaw) override def newInstance(): ConfigSteps = new ConfigSteps(raw.clone()) diff --git a/thehive/app/org/thp/thehive/services/CustomFieldSrv.scala b/thehive/app/org/thp/thehive/services/CustomFieldSrv.scala index 8156ba806d..5ced75f2d6 100644 --- a/thehive/app/org/thp/thehive/services/CustomFieldSrv.scala +++ b/thehive/app/org/thp/thehive/services/CustomFieldSrv.scala @@ -2,15 +2,16 @@ package org.thp.thehive.services import java.util.{Map => JMap} +import akka.actor.ActorRef import gremlin.scala._ -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.apache.tinkerpop.gremlin.structure.T import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.query.PropertyUpdater -import org.thp.scalligraph.services.{RichElement, VertexSrv} +import org.thp.scalligraph.services.{IntegrityCheckOps, RichElement, VertexSrv} import org.thp.scalligraph.steps.StepsOps._ -import org.thp.scalligraph.steps.{EdgeSteps, SelectMap, Traversal, ValueMap, VertexSteps} +import org.thp.scalligraph.steps._ import org.thp.scalligraph.{EntitySteps, RichSeq} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ @@ -18,7 +19,7 @@ import play.api.libs.json.{JsNull, JsObject, JsValue} import shapeless.HNil import scala.collection.JavaConverters._ -import scala.util.Try +import scala.util.{Success, Try} @Singleton class CustomFieldSrv @Inject() (auditSrv: AuditSrv, @Named("integrity-check-actor") integrityCheckActor: ActorRef)( @@ -36,6 +37,8 @@ class CustomFieldSrv @Inject() (auditSrv: AuditSrv, @Named("integrity-check-acto _ <- auditSrv.customField.create(created, created.toJson) } yield created + override def exists(e: CustomField)(implicit graph: Graph): Boolean = initSteps.getByName(e.name).exists() + def delete(c: CustomField with Entity, force: Boolean)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { get(c).remove() // TODO use force auditSrv.customField.delete(c) @@ -68,7 +71,8 @@ class CustomFieldSrv @Inject() (auditSrv: AuditSrv, @Named("integrity-check-acto } @EntitySteps[CustomField] -class CustomFieldSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[CustomField](raw) { +class CustomFieldSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) + extends VertexSteps[CustomField](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): CustomFieldSteps = new CustomFieldSteps(newRaw) override def newInstance(): CustomFieldSteps = new CustomFieldSteps(raw.clone()) @@ -80,7 +84,7 @@ class CustomFieldSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: } -class CustomFieldValueSteps(raw: GremlinScala[Edge])(implicit db: Database, graph: Graph) +class CustomFieldValueSteps(raw: GremlinScala[Edge])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends EdgeSteps[CustomFieldValue[_], Product, CustomField](raw) { override def newInstance(): CustomFieldValueSteps = new CustomFieldValueSteps(raw.clone()) @@ -178,3 +182,14 @@ class CustomFieldValueSteps(raw: GremlinScala[Edge])(implicit db: Database, grap // def remove() = raw.drop().i } + +class CustomFieldIntegrityCheckOps @Inject() (@Named("with-thehive-schema") val db: Database, val service: CustomFieldSrv) + extends IntegrityCheckOps[CustomField] { + override def resolve(entities: List[CustomField with Entity])(implicit graph: Graph): Try[Unit] = entities match { + case head :: tail => + tail.foreach(copyEdge(_, head)) + tail.foreach(service.get(_).remove()) + Success(()) + case _ => Success(()) + } +} diff --git a/thehive/app/org/thp/thehive/services/DashboardSrv.scala b/thehive/app/org/thp/thehive/services/DashboardSrv.scala index e185ec7fae..961335a2f5 100644 --- a/thehive/app/org/thp/thehive/services/DashboardSrv.scala +++ b/thehive/app/org/thp/thehive/services/DashboardSrv.scala @@ -2,13 +2,8 @@ package org.thp.thehive.services import java.util.{List => JList} -import scala.collection.JavaConverters._ -import scala.util.{Success, Try} - -import play.api.libs.json.{JsObject, Json} - import gremlin.scala.{__, By, Element, Graph, GremlinScala, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.apache.tinkerpop.gremlin.process.traversal.Path import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} @@ -19,10 +14,15 @@ import org.thp.scalligraph.steps.{Traversal, VertexSteps} import org.thp.scalligraph.{EntitySteps, InternalError} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ +import play.api.libs.json.{JsObject, Json} + +import scala.collection.JavaConverters._ +import scala.util.{Success, Try} @Singleton -class DashboardSrv @Inject() (organisationSrv: OrganisationSrv, userSrv: UserSrv, auditSrv: AuditSrv)(implicit db: Database) - extends VertexSrv[Dashboard, DashboardSteps] { +class DashboardSrv @Inject() (organisationSrv: OrganisationSrv, userSrv: UserSrv, auditSrv: AuditSrv)( + implicit @Named("with-thehive-schema") db: Database +) extends VertexSrv[Dashboard, DashboardSteps] { val organisationDashboardSrv = new EdgeSrv[OrganisationDashboard, Organisation, Dashboard] val dashboardUserSrv = new EdgeSrv[DashboardUser, Dashboard, User] @@ -78,7 +78,8 @@ class DashboardSrv @Inject() (organisationSrv: OrganisationSrv, userSrv: UserSrv } @EntitySteps[Dashboard] -class DashboardSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Dashboard](raw) { +class DashboardSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) + extends VertexSteps[Dashboard](raw) { override def newInstance(newRaw: GremlinScala[Vertex] = raw): DashboardSteps = new DashboardSteps(newRaw) def visible(implicit authContext: AuthContext): DashboardSteps = diff --git a/thehive/app/org/thp/thehive/services/KeyValueSrv.scala b/thehive/app/org/thp/thehive/services/KeyValueSrv.scala index 16870503d6..8b94bb432c 100644 --- a/thehive/app/org/thp/thehive/services/KeyValueSrv.scala +++ b/thehive/app/org/thp/thehive/services/KeyValueSrv.scala @@ -1,22 +1,22 @@ package org.thp.thehive.services -import scala.util.Try - import gremlin.scala.{Graph, GremlinScala, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.services.VertexSrv import org.thp.scalligraph.steps.VertexSteps import org.thp.thehive.models.KeyValue +import scala.util.Try + @Singleton -class KeyValueSrv @Inject() ()(implicit db: Database) extends VertexSrv[KeyValue, KeyValueSteps] { +class KeyValueSrv @Inject() ()(implicit @Named("with-thehive-schema") db: Database) extends VertexSrv[KeyValue, KeyValueSteps] { def create(e: KeyValue)(implicit graph: Graph, authContext: AuthContext): Try[KeyValue with Entity] = createEntity(e) override def steps(raw: GremlinScala[Vertex])(implicit graph: Graph): KeyValueSteps = new KeyValueSteps(raw) } -class KeyValueSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[KeyValue](raw) { +class KeyValueSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[KeyValue](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): KeyValueSteps = new KeyValueSteps(newRaw) override def newInstance(): KeyValueSteps = new KeyValueSteps(raw.clone()) } diff --git a/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala b/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala index fd5c77e228..a5aed7f2eb 100644 --- a/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala +++ b/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala @@ -2,20 +2,19 @@ package org.thp.thehive.services import java.util.Base64 -import scala.concurrent.ExecutionContext -import scala.util.{Failure, Random, Success, Try} - -import play.api.Configuration -import play.api.mvc.RequestHeader - -import javax.inject.{Inject, Provider, Singleton} +import javax.inject.{Inject, Named, Provider, Singleton} import org.thp.scalligraph.NotFoundError import org.thp.scalligraph.auth._ import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ +import play.api.Configuration +import play.api.mvc.RequestHeader + +import scala.concurrent.ExecutionContext +import scala.util.{Failure, Random, Success, Try} class LocalKeyAuthSrv( - db: Database, + @Named("with-thehive-schema") db: Database, userSrv: UserSrv, localUserSrv: LocalUserSrv, authSrv: AuthSrv, @@ -67,7 +66,7 @@ class LocalKeyAuthSrv( @Singleton class LocalKeyAuthProvider @Inject() ( - db: Database, + @Named("with-thehive-schema") db: Database, userSrv: UserSrv, localUserSrv: LocalUserSrv, authSrvProvider: Provider[AuthSrv], diff --git a/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala b/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala index 08aeb8b6e4..6ae9933010 100644 --- a/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala +++ b/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala @@ -1,7 +1,7 @@ package org.thp.thehive.services import io.github.nremond.SecureHash -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.{AuthCapability, AuthContext, AuthSrv, AuthSrvProvider} import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ @@ -19,7 +19,7 @@ object LocalPasswordAuthSrv { SecureHash.createHash(password) } -class LocalPasswordAuthSrv(db: Database, userSrv: UserSrv, localUserSrv: LocalUserSrv) extends AuthSrv { +class LocalPasswordAuthSrv(@Named("with-thehive-schema") db: Database, userSrv: UserSrv, localUserSrv: LocalUserSrv) extends AuthSrv { val name = "local" override val capabilities: Set[AuthCapability.Value] = Set(AuthCapability.changePassword, AuthCapability.setPassword) lazy val logger: Logger = Logger(getClass) @@ -69,7 +69,8 @@ class LocalPasswordAuthSrv(db: Database, userSrv: UserSrv, localUserSrv: LocalUs } @Singleton -class LocalPasswordAuthProvider @Inject() (db: Database, userSrv: UserSrv, localUserSrv: LocalUserSrv) extends AuthSrvProvider { +class LocalPasswordAuthProvider @Inject() (@Named("with-thehive-schema") db: Database, userSrv: UserSrv, localUserSrv: LocalUserSrv) + extends AuthSrvProvider { override val name: String = "local" override def apply(config: Configuration): Try[AuthSrv] = Success(new LocalPasswordAuthSrv(db, userSrv, localUserSrv)) } diff --git a/thehive/app/org/thp/thehive/services/LocalUserSrv.scala b/thehive/app/org/thp/thehive/services/LocalUserSrv.scala index 9b03bb173c..890b1ac244 100644 --- a/thehive/app/org/thp/thehive/services/LocalUserSrv.scala +++ b/thehive/app/org/thp/thehive/services/LocalUserSrv.scala @@ -1,12 +1,12 @@ package org.thp.thehive.services -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.{AuthContext, AuthContextImpl, User => ScalligraphUser, UserSrv => ScalligraphUserSrv} import org.thp.scalligraph.models.Database import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.utils.Instance import org.thp.scalligraph.{AuthenticationError, CreateError, NotFoundError} -import org.thp.thehive.models.{Permissions, User} +import org.thp.thehive.models.{Organisation, Permissions, Profile, User} import play.api.Configuration import play.api.libs.json.JsObject import play.api.mvc.RequestHeader @@ -14,8 +14,13 @@ import play.api.mvc.RequestHeader import scala.util.{Failure, Success, Try} @Singleton -class LocalUserSrv @Inject() (db: Database, userSrv: UserSrv, organisationSrv: OrganisationSrv, profileSrv: ProfileSrv, configuration: Configuration) - extends ScalligraphUserSrv { +class LocalUserSrv @Inject() ( + @Named("with-thehive-schema") db: Database, + userSrv: UserSrv, + organisationSrv: OrganisationSrv, + profileSrv: ProfileSrv, + configuration: Configuration +) extends ScalligraphUserSrv { override def getAuthContext(request: RequestHeader, userId: String, organisationName: Option[String]): Try[AuthContext] = db.roTransaction { implicit graph => @@ -30,7 +35,7 @@ class LocalUserSrv @Inject() (db: Database, userSrv: UserSrv, organisationSrv: O .orElse { organisationName.flatMap { org => userSteps - .getAuthContext(requestId, OrganisationSrv.administration.name) + .getAuthContext(requestId, Organisation.administration.name) .headOption() .map(_.changeOrganisation(org)) } @@ -55,7 +60,7 @@ class LocalUserSrv @Inject() (db: Database, userSrv: UserSrv, organisationSrv: O profileStr <- readData(userInfo, profileFieldName, defaultProfile) profile <- profileSrv.getOrFail(profileStr) orgaStr <- readData(userInfo, organisationFieldName, defaultOrg) - if orgaStr != OrganisationSrv.administration.name || profile.name == ProfileSrv.admin.name + if orgaStr != Organisation.administration.name || profile.name == Profile.admin.name organisation <- organisationSrv.getOrFail(orgaStr) richUser <- userSrv.addOrCreateUser( User(userId, userId, None, locked = false, None, None), @@ -68,11 +73,15 @@ class LocalUserSrv @Inject() (db: Database, userSrv: UserSrv, organisationSrv: O } else Failure(CreateError(s"Autocreate on single sign on is $autocreate")) } - override def getSystemAuthContext: AuthContext = + override def getSystemAuthContext: AuthContext = LocalUserSrv.getSystemAuthContext +} + +object LocalUserSrv { + def getSystemAuthContext: AuthContext = AuthContextImpl( - UserSrv.system.login, - UserSrv.system.name, - OrganisationSrv.administration.name, + User.system.login, + User.system.name, + Organisation.administration.name, Instance.getInternalId, Permissions.all ) diff --git a/thehive/app/org/thp/thehive/services/LogSrv.scala b/thehive/app/org/thp/thehive/services/LogSrv.scala index 36765a8f2d..5667fee8a6 100644 --- a/thehive/app/org/thp/thehive/services/LogSrv.scala +++ b/thehive/app/org/thp/thehive/services/LogSrv.scala @@ -1,12 +1,7 @@ package org.thp.thehive.services -import scala.collection.JavaConverters._ -import scala.util.Try - -import play.api.libs.json.{JsObject, Json} - import gremlin.scala._ -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.EntitySteps import org.thp.scalligraph.auth.{AuthContext, Permission} import org.thp.scalligraph.controllers.FFile @@ -17,9 +12,14 @@ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.{Traversal, VertexSteps} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ +import play.api.libs.json.{JsObject, Json} + +import scala.collection.JavaConverters._ +import scala.util.Try @Singleton -class LogSrv @Inject() (attachmentSrv: AttachmentSrv, auditSrv: AuditSrv)(implicit db: Database) extends VertexSrv[Log, LogSteps] { +class LogSrv @Inject() (attachmentSrv: AttachmentSrv, auditSrv: AuditSrv)(implicit @Named("with-thehive-schema") db: Database) + extends VertexSrv[Log, LogSteps] { val taskLogSrv = new EdgeSrv[TaskLog, Task, Log] val logAttachmentSrv = new EdgeSrv[LogAttachment, Log, Attachment] override def steps(raw: GremlinScala[Vertex])(implicit graph: Graph): LogSteps = new LogSteps(raw) @@ -72,7 +72,7 @@ class LogSrv @Inject() (attachmentSrv: AttachmentSrv, auditSrv: AuditSrv)(implic } @EntitySteps[Log] -class LogSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Log](raw) { +class LogSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Log](raw) { def task = new TaskSteps(raw.in("TaskLog")) diff --git a/thehive/app/org/thp/thehive/services/ObservableSrv.scala b/thehive/app/org/thp/thehive/services/ObservableSrv.scala index 0279b15277..094fbfa373 100644 --- a/thehive/app/org/thp/thehive/services/ObservableSrv.scala +++ b/thehive/app/org/thp/thehive/services/ObservableSrv.scala @@ -3,13 +3,8 @@ package org.thp.thehive.services import java.lang.{Long => JLong} import java.util.{Set => JSet} -import scala.collection.JavaConverters._ -import scala.util.Try - -import play.api.libs.json.JsObject - import gremlin.scala.{KeyValue => _, _} -import javax.inject.{Inject, Provider, Singleton} +import javax.inject.{Inject, Named, Provider, Singleton} import org.apache.tinkerpop.gremlin.process.traversal.{P => JP} import org.thp.scalligraph.auth.{AuthContext, Permission} import org.thp.scalligraph.controllers.FFile @@ -20,6 +15,10 @@ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.{Traversal, TraversalLike, VertexSteps} import org.thp.scalligraph.{EntitySteps, RichSeq} import org.thp.thehive.models._ +import play.api.libs.json.JsObject + +import scala.collection.JavaConverters._ +import scala.util.Try @Singleton class ObservableSrv @Inject() ( @@ -30,7 +29,7 @@ class ObservableSrv @Inject() ( caseSrvProvider: Provider[CaseSrv], auditSrv: AuditSrv )( - implicit db: Database + implicit @Named("with-thehive-schema") db: Database ) extends VertexSrv[Observable, ObservableSteps] { lazy val caseSrv: CaseSrv = caseSrvProvider.get val observableKeyValueSrv = new EdgeSrv[ObservableKeyValue, Observable, KeyValue] @@ -186,7 +185,8 @@ class ObservableSrv @Inject() ( } @EntitySteps[Observable] -class ObservableSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Observable](raw) { +class ObservableSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) + extends VertexSteps[Observable](raw) { def filterOnType(`type`: String): ObservableSteps = this.filter(_.outTo[ObservableObservableType].has("name", `type`)) diff --git a/thehive/app/org/thp/thehive/services/ObservableTypeSrv.scala b/thehive/app/org/thp/thehive/services/ObservableTypeSrv.scala index f7645e3306..0fa3d6dc15 100644 --- a/thehive/app/org/thp/thehive/services/ObservableTypeSrv.scala +++ b/thehive/app/org/thp/thehive/services/ObservableTypeSrv.scala @@ -1,37 +1,22 @@ package org.thp.thehive.services +import akka.actor.ActorRef import gremlin.scala._ -import javax.inject.{Inject, Singleton} -import org.thp.scalligraph.{BadRequestError, EntitySteps} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.services._ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.VertexSteps +import org.thp.scalligraph.{BadRequestError, EntitySteps} import org.thp.thehive.models._ + import scala.util.{Failure, Success, Try} @Singleton -class ObservableTypeSrv @Inject() (implicit db: Database) extends VertexSrv[ObservableType, ObservableTypeSteps] { - - override val initialValues: Seq[ObservableType] = Seq( - ObservableType("url", isAttachment = false), - ObservableType("other", isAttachment = false), - ObservableType("user-agent", isAttachment = false), - ObservableType("regexp", isAttachment = false), - ObservableType("mail-subject", isAttachment = false), - ObservableType("registry", isAttachment = false), - ObservableType("mail", isAttachment = false), - ObservableType("autonomous-system", isAttachment = false), - ObservableType("domain", isAttachment = false), - ObservableType("ip", isAttachment = false), - ObservableType("uri_path", isAttachment = false), - ObservableType("filename", isAttachment = false), - ObservableType("hash", isAttachment = false), - ObservableType("file", isAttachment = true), - ObservableType("fqdn", isAttachment = false), - ObservableType("hostname", isAttachment = false) - ) +class ObservableTypeSrv @Inject() (@Named("integrity-check-actor") integrityCheckActor: ActorRef)( + implicit @Named("with-thehive-schema") db: Database +) extends VertexSrv[ObservableType, ObservableTypeSteps] { val observableObservableTypeSrv = new EdgeSrv[ObservableObservableType, Observable, ObservableType] override def steps(raw: GremlinScala[Vertex])(implicit graph: Graph): ObservableTypeSteps = new ObservableTypeSteps(raw) @@ -40,6 +25,13 @@ class ObservableTypeSrv @Inject() (implicit db: Database) extends VertexSrv[Obse if (db.isValidId(idOrName)) getByIds(idOrName) else initSteps.getByName(idOrName) + override def exists(e: ObservableType)(implicit graph: Graph): Boolean = initSteps.getByName(e.name).exists() + + override def createEntity(e: ObservableType)(implicit graph: Graph, authContext: AuthContext): Try[ObservableType with Entity] = { + integrityCheckActor ! IntegrityCheckActor.EntityAdded("ObservableType") + super.createEntity(e) + } + def create(observableType: ObservableType)(implicit graph: Graph, authContext: AuthContext): Try[ObservableType with Entity] = createEntity(observableType) @@ -52,7 +44,8 @@ class ObservableTypeSrv @Inject() (implicit db: Database) extends VertexSrv[Obse } @EntitySteps[ObservableType] -class ObservableTypeSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[ObservableType](raw) { +class ObservableTypeSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) + extends VertexSteps[ObservableType](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): ObservableTypeSteps = new ObservableTypeSteps(newRaw) override def newInstance(): ObservableTypeSteps = new ObservableTypeSteps(raw.clone()) @@ -63,3 +56,14 @@ class ObservableTypeSteps(raw: GremlinScala[Vertex])(implicit db: Database, grap def getByName(name: String): ObservableTypeSteps = new ObservableTypeSteps(raw.has(Key("name") of name)) } + +class ObservableTypeIntegrityCheckOps @Inject() (@Named("with-thehive-schema") val db: Database, val service: ObservableTypeSrv) + extends IntegrityCheckOps[ObservableType] { + override def resolve(entities: List[ObservableType with Entity])(implicit graph: Graph): Try[Unit] = entities match { + case head :: tail => + tail.foreach(copyEdge(_, head)) + tail.foreach(service.get(_).remove()) + Success(()) + case _ => Success(()) + } +} diff --git a/thehive/app/org/thp/thehive/services/OrganisationSrv.scala b/thehive/app/org/thp/thehive/services/OrganisationSrv.scala index a4dfdd7a8a..f1a7049153 100644 --- a/thehive/app/org/thp/thehive/services/OrganisationSrv.scala +++ b/thehive/app/org/thp/thehive/services/OrganisationSrv.scala @@ -1,36 +1,42 @@ package org.thp.thehive.services +import akka.actor.ActorRef import gremlin.scala._ - -import scala.collection.JavaConverters._ -import javax.inject.{Inject, Singleton} -import org.thp.scalligraph.{BadRequestError, EntitySteps, RichSeq} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.{AuthContext, Permission} import org.thp.scalligraph.models._ import org.thp.scalligraph.query.PropertyUpdater import org.thp.scalligraph.services._ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.{Traversal, VertexSteps} +import org.thp.scalligraph.{BadRequestError, EntitySteps, RichSeq} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ import play.api.libs.json.JsObject +import scala.collection.JavaConverters._ import scala.util.{Failure, Success, Try} -object OrganisationSrv { - val administration: Organisation = Organisation("admin", "organisation for administration") -} - @Singleton -class OrganisationSrv @Inject() (roleSrv: RoleSrv, profileSrv: ProfileSrv, auditSrv: AuditSrv)(implicit db: Database) - extends VertexSrv[Organisation, OrganisationSteps] { - - override val initialValues: Seq[Organisation] = Seq(OrganisationSrv.administration) - val organisationOrganisationSrv = new EdgeSrv[OrganisationOrganisation, Organisation, Organisation] - val organisationShareSrv = new EdgeSrv[OrganisationShare, Organisation, Share] +class OrganisationSrv @Inject() ( + roleSrv: RoleSrv, + profileSrv: ProfileSrv, + auditSrv: AuditSrv, + @Named("integrity-check-actor") integrityCheckActor: ActorRef +)( + implicit @Named("with-thehive-schema") db: Database +) extends VertexSrv[Organisation, OrganisationSteps] { + + val organisationOrganisationSrv = new EdgeSrv[OrganisationOrganisation, Organisation, Organisation] + val organisationShareSrv = new EdgeSrv[OrganisationShare, Organisation, Share] override def steps(raw: GremlinScala[Vertex])(implicit graph: Graph): OrganisationSteps = new OrganisationSteps(raw) + override def createEntity(e: Organisation)(implicit graph: Graph, authContext: AuthContext): Try[Organisation with Entity] = { + integrityCheckActor ! IntegrityCheckActor.EntityAdded("Organisation") + super.createEntity(e) + } + def create(organisation: Organisation, user: User with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Organisation with Entity] = for { createdOrganisation <- create(organisation) @@ -49,11 +55,13 @@ class OrganisationSrv @Inject() (roleSrv: RoleSrv, profileSrv: ProfileSrv, audit if (db.isValidId(idOrName)) getByIds(idOrName) else initSteps.getByName(idOrName) + override def exists(e: Organisation)(implicit graph: Graph): Boolean = initSteps.getByName(e.name).exists() + override def update( steps: OrganisationSteps, propertyUpdaters: Seq[PropertyUpdater] )(implicit graph: Graph, authContext: AuthContext): Try[(OrganisationSteps, JsObject)] = - if (steps.newInstance().has("name", OrganisationSrv.administration.name).exists()) + if (steps.newInstance().has("name", Organisation.administration.name).exists()) Failure(BadRequestError("Admin organisation is unmodifiable")) else { auditSrv.mergeAudits(super.update(steps, propertyUpdaters)) { @@ -108,7 +116,8 @@ class OrganisationSrv @Inject() (roleSrv: RoleSrv, profileSrv: ProfileSrv, audit } @EntitySteps[Organisation] -class OrganisationSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Organisation](raw) { +class OrganisationSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) + extends VertexSteps[Organisation](raw) { def links: OrganisationSteps = newInstance(raw.outTo[OrganisationOrganisation]) @@ -174,3 +183,14 @@ class OrganisationSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: override def newInstance(): OrganisationSteps = new OrganisationSteps(raw.clone()) } + +class OrganisationIntegrityCheckOps @Inject() (@Named("with-thehive-schema") val db: Database, val service: OrganisationSrv) + extends IntegrityCheckOps[Organisation] { + override def resolve(entities: List[Organisation with Entity])(implicit graph: Graph): Try[Unit] = entities match { + case head :: tail => + tail.foreach(copyEdge(_, head)) + tail.foreach(service.get(_).remove()) + Success(()) + case _ => Success(()) + } +} diff --git a/thehive/app/org/thp/thehive/services/PageSrv.scala b/thehive/app/org/thp/thehive/services/PageSrv.scala index a9a0d753cd..d177e8c600 100644 --- a/thehive/app/org/thp/thehive/services/PageSrv.scala +++ b/thehive/app/org/thp/thehive/services/PageSrv.scala @@ -1,7 +1,7 @@ package org.thp.thehive.services import gremlin.scala.{Graph, GremlinScala, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.EntitySteps import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} @@ -15,7 +15,8 @@ import play.api.libs.json.Json import scala.util.Try @Singleton -class PageSrv @Inject() (implicit db: Database, organisationSrv: OrganisationSrv, auditSrv: AuditSrv) extends VertexSrv[Page, PageSteps] { +class PageSrv @Inject() (implicit @Named("with-thehive-schema") db: Database, organisationSrv: OrganisationSrv, auditSrv: AuditSrv) + extends VertexSrv[Page, PageSteps] { val organisationPageSrv = new EdgeSrv[OrganisationPage, Organisation, Page] @@ -47,7 +48,7 @@ class PageSrv @Inject() (implicit db: Database, organisationSrv: OrganisationSrv } @EntitySteps[Page] -class PageSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Page](raw) { +class PageSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Page](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): PageSteps = new PageSteps(newRaw) override def newInstance(): PageSteps = new PageSteps(raw.clone()) diff --git a/thehive/app/org/thp/thehive/services/ProfileSrv.scala b/thehive/app/org/thp/thehive/services/ProfileSrv.scala index 3594bab5a7..ad84e05829 100644 --- a/thehive/app/org/thp/thehive/services/ProfileSrv.scala +++ b/thehive/app/org/thp/thehive/services/ProfileSrv.scala @@ -1,53 +1,32 @@ package org.thp.thehive.services -import scala.util.{Failure, Try} - -import play.api.libs.json.JsObject - +import akka.actor.ActorRef import gremlin.scala._ -import javax.inject.{Inject, Singleton} -import org.thp.scalligraph.{BadRequestError, EntitySteps} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.{AuthContext, Permission} import org.thp.scalligraph.models._ import org.thp.scalligraph.query.PropertyUpdater import org.thp.scalligraph.services._ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.VertexSteps +import org.thp.scalligraph.{BadRequestError, EntitySteps} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ +import play.api.libs.json.JsObject -object ProfileSrv { - lazy val admin: Profile = Profile("admin", Permissions.adminPermissions) - - lazy val analyst: Profile = Profile( - "analyst", - Set( - Permissions.manageCase, - Permissions.manageObservable, - Permissions.manageAlert, - Permissions.manageTask, - Permissions.manageAction, - Permissions.manageShare, - Permissions.manageAnalyse, - Permissions.managePage - ) - ) - lazy val readonly: Profile = Profile("read-only", Set.empty) - lazy val orgAdmin: Profile = Profile("org-admin", Permissions.forScope("organisation")) - - def isEditable(profile: Profile): Boolean = profile.name != admin.name && profile.name != orgAdmin.name -} +import scala.util.{Failure, Success, Try} @Singleton -class ProfileSrv @Inject() (auditSrv: AuditSrv)(implicit val db: Database) extends VertexSrv[Profile, ProfileSteps] { +class ProfileSrv @Inject() (auditSrv: AuditSrv, @Named("integrity-check-actor") integrityCheckActor: ActorRef)( + implicit @Named("with-thehive-schema") val db: Database +) extends VertexSrv[Profile, ProfileSteps] { + + lazy val orgAdmin: Profile with Entity = db.roTransaction(graph => getOrFail(Profile.orgAdmin.name)(graph)).get - lazy val orgAdmin: Profile with Entity = db.roTransaction(graph => getOrFail(ProfileSrv.orgAdmin.name)(graph)).get - override val initialValues: Seq[Profile] = Seq( - ProfileSrv.admin, - ProfileSrv.orgAdmin, - ProfileSrv.analyst, - ProfileSrv.readonly - ) + override def createEntity(e: Profile)(implicit graph: Graph, authContext: AuthContext): Try[Profile with Entity] = { + integrityCheckActor ! IntegrityCheckActor.EntityAdded("Profile") + super.createEntity(e) + } def create(profile: Profile)(implicit graph: Graph, authContext: AuthContext): Try[Profile with Entity] = for { @@ -61,8 +40,10 @@ class ProfileSrv @Inject() (auditSrv: AuditSrv)(implicit val db: Database) exten if (db.isValidId(idOrName)) getByIds(idOrName) else initSteps.getByName(idOrName) + override def exists(e: Profile)(implicit graph: Graph): Boolean = initSteps.getByName(e.name).exists() + def remove(profile: Profile with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = - if (!ProfileSrv.isEditable(profile)) + if (!profile.isEditable) Failure(BadRequestError(s"Profile ${profile.name} cannot be removed")) else if (get(profile).filter(_.or(_.roles, _.shares)).exists()) Failure(BadRequestError(s"Profile ${profile.name} is used")) @@ -75,13 +56,13 @@ class ProfileSrv @Inject() (auditSrv: AuditSrv)(implicit val db: Database) exten steps: ProfileSteps, propertyUpdaters: Seq[PropertyUpdater] )(implicit graph: Graph, authContext: AuthContext): Try[(ProfileSteps, JsObject)] = - if (steps.newInstance().toIterator.exists(!ProfileSrv.isEditable(_))) + if (steps.newInstance().toIterator.exists(!_.isEditable)) Failure(BadRequestError(s"Profile is not editable")) else super.update(steps, propertyUpdaters) } @EntitySteps[Profile] -class ProfileSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Profile](raw) { +class ProfileSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Profile](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): ProfileSteps = new ProfileSteps(newRaw) override def newInstance(): ProfileSteps = new ProfileSteps(raw.clone()) @@ -98,3 +79,14 @@ class ProfileSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Grap def contains(permission: Permission): ProfileSteps = this.has("permissions", permission) } + +class ProfileIntegrityCheckOps @Inject() (@Named("with-thehive-schema") val db: Database, val service: ProfileSrv) + extends IntegrityCheckOps[Profile] { + override def resolve(entities: List[Profile with Entity])(implicit graph: Graph): Try[Unit] = entities match { + case head :: tail => + tail.foreach(copyEdge(_, head)) + tail.foreach(service.get(_).remove()) + Success(()) + case _ => Success(()) + } +} diff --git a/thehive/app/org/thp/thehive/services/ReportTagSrv.scala b/thehive/app/org/thp/thehive/services/ReportTagSrv.scala index 3dd67a72aa..2bea03f41d 100644 --- a/thehive/app/org/thp/thehive/services/ReportTagSrv.scala +++ b/thehive/app/org/thp/thehive/services/ReportTagSrv.scala @@ -1,21 +1,20 @@ package org.thp.thehive.services -import scala.util.Try - import gremlin.scala.{Graph, GremlinScala, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.RichSeq import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} -import org.thp.scalligraph.services.{EdgeSrv, VertexSrv} -import org.thp.scalligraph.services.RichVertexGremlinScala +import org.thp.scalligraph.services.{EdgeSrv, RichVertexGremlinScala, VertexSrv} import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.VertexSteps - import org.thp.thehive.models.{Observable, ObservableReportTag, ReportTag} +import scala.util.Try + @Singleton -class ReportTagSrv @Inject() (observableSrv: ObservableSrv)(implicit db: Database) extends VertexSrv[ReportTag, ReportTagSteps] { +class ReportTagSrv @Inject() (observableSrv: ObservableSrv)(implicit @Named("with-thehive-schema") db: Database) + extends VertexSrv[ReportTag, ReportTagSteps] { val observableReportTagSrv = new EdgeSrv[ObservableReportTag, Observable, ReportTag] override def steps(raw: GremlinScala[Vertex])(implicit graph: Graph): ReportTagSteps = new ReportTagSteps(raw) @@ -33,7 +32,8 @@ class ReportTagSrv @Inject() (observableSrv: ObservableSrv)(implicit db: Databas } } -class ReportTagSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[ReportTag](raw) { +class ReportTagSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) + extends VertexSteps[ReportTag](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): VertexSteps[ReportTag] = new ReportTagSteps(raw) def observable: ObservableSteps = new ObservableSteps(raw.inTo[ObservableReportTag]) diff --git a/thehive/app/org/thp/thehive/services/RoleSrv.scala b/thehive/app/org/thp/thehive/services/RoleSrv.scala index 445db4462d..7a439dc485 100644 --- a/thehive/app/org/thp/thehive/services/RoleSrv.scala +++ b/thehive/app/org/thp/thehive/services/RoleSrv.scala @@ -1,9 +1,7 @@ package org.thp.thehive.services -import scala.util.Try - import gremlin.scala._ -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.EntitySteps import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models._ @@ -11,8 +9,10 @@ import org.thp.scalligraph.services._ import org.thp.scalligraph.steps.VertexSteps import org.thp.thehive.models._ +import scala.util.Try + @Singleton -class RoleSrv @Inject() (implicit val db: Database) extends VertexSrv[Role, RoleSteps] { +class RoleSrv @Inject() (@Named("with-thehive-schema") implicit val db: Database) extends VertexSrv[Role, RoleSteps] { val roleOrganisationSrv = new EdgeSrv[RoleOrganisation, Role, Organisation] val userRoleSrv = new EdgeSrv[UserRole, User, Role] @@ -42,7 +42,7 @@ class RoleSrv @Inject() (implicit val db: Database) extends VertexSrv[Role, Role } @EntitySteps[Role] -class RoleSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Role](raw) { +class RoleSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Role](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): RoleSteps = new RoleSteps(newRaw) override def newInstance(): RoleSteps = new RoleSteps(raw.clone()) def organisation: OrganisationSteps = new OrganisationSteps(raw.outTo[RoleOrganisation]) diff --git a/thehive/app/org/thp/thehive/services/ShareSrv.scala b/thehive/app/org/thp/thehive/services/ShareSrv.scala index 9d8140a3aa..1dd89e9dde 100644 --- a/thehive/app/org/thp/thehive/services/ShareSrv.scala +++ b/thehive/app/org/thp/thehive/services/ShareSrv.scala @@ -1,21 +1,21 @@ package org.thp.thehive.services -import scala.util.{Failure, Try} - import gremlin.scala._ -import javax.inject.{Inject, Provider, Singleton} -import org.thp.scalligraph.{CreateError, EntitySteps} +import javax.inject.{Inject, Named, Provider, Singleton} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models._ import org.thp.scalligraph.services._ import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.{Traversal, VertexSteps} +import org.thp.scalligraph.{CreateError, EntitySteps} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.models._ +import scala.util.{Failure, Try} + @Singleton class ShareSrv @Inject() ( - implicit val db: Database, + @Named("with-thehive-schema") implicit val db: Database, auditSrv: AuditSrv, caseSrvProvider: Provider[CaseSrv], taskSrv: TaskSrv, @@ -289,7 +289,7 @@ class ShareSrv @Inject() ( } @EntitySteps[Share] -class ShareSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Share](raw) { +class ShareSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Share](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): ShareSteps = new ShareSteps(newRaw) override def newInstance(): ShareSteps = new ShareSteps(raw.clone()) diff --git a/thehive/app/org/thp/thehive/services/StreamSrv.scala b/thehive/app/org/thp/thehive/services/StreamSrv.scala index 7e5c00ae17..cb6fc196d7 100644 --- a/thehive/app/org/thp/thehive/services/StreamSrv.scala +++ b/thehive/app/org/thp/thehive/services/StreamSrv.scala @@ -2,19 +2,11 @@ package org.thp.thehive.services import java.io.NotSerializableException -import scala.collection.immutable -import scala.concurrent.duration.{DurationInt, FiniteDuration} -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Random, Try} - -import play.api.Logger -import play.api.libs.json.Json - import akka.actor.{actorRef2Scala, Actor, ActorIdentity, ActorRef, ActorSystem, Cancellable, Identify, PoisonPill, Props} import akka.pattern.{ask, AskTimeoutException} import akka.serialization.Serializer import akka.util.Timeout -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.NotFoundError import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.Database @@ -22,6 +14,13 @@ import org.thp.scalligraph.services.EventSrv import org.thp.scalligraph.services.config.ApplicationConfig.finiteDurationFormat import org.thp.scalligraph.services.config.{ApplicationConfig, ConfigItem} import org.thp.scalligraph.steps.StepsOps._ +import play.api.Logger +import play.api.libs.json.Json + +import scala.collection.immutable +import scala.concurrent.duration.{DurationInt, FiniteDuration} +import scala.concurrent.{ExecutionContext, Future} +import scala.util.{Random, Try} sealed trait StreamMessage extends Serializable @@ -45,7 +44,7 @@ class StreamActor( graceDuration: FiniteDuration, keepAlive: FiniteDuration, auditSrv: AuditSrv, - db: Database + @Named("with-thehive-schema") db: Database ) extends Actor { import context.dispatcher @@ -138,7 +137,7 @@ class StreamSrv @Inject() ( appConfig: ApplicationConfig, eventSrv: EventSrv, auditSrv: AuditSrv, - db: Database, + @Named("with-thehive-schema") db: Database, system: ActorSystem, implicit val ec: ExecutionContext ) { diff --git a/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala b/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala index d25eb5b192..a57a595440 100644 --- a/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala +++ b/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala @@ -6,13 +6,13 @@ import java.util.concurrent.TimeUnit import gremlin.scala.Graph import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec -import javax.inject.{Inject, Provider, Singleton} +import javax.inject.{Inject, Named, Provider, Singleton} import org.apache.commons.codec.binary.Base32 -import org.thp.scalligraph.{AuthenticationError, MultiFactorCodeRequired} -import org.thp.scalligraph.auth.{AuthCapability, AuthContext, AuthSrv, AuthSrvProvider, MultiAuthSrv} +import org.thp.scalligraph.auth._ import org.thp.scalligraph.models.Database import org.thp.scalligraph.services.config.{ApplicationConfig, ConfigItem} import org.thp.scalligraph.steps.StepsOps._ +import org.thp.scalligraph.{AuthenticationError, MultiFactorCodeRequired} import play.api.Configuration import play.api.mvc.RequestHeader @@ -24,7 +24,7 @@ class TOTPAuthSrv( appConfig: ApplicationConfig, availableAuthProviders: immutable.Set[AuthSrvProvider], userSrv: UserSrv, - db: Database + @Named("with-thehive-schema") db: Database ) extends MultiAuthSrv(configuration, appConfig, availableAuthProviders) { override val name: String = "totp" @@ -102,7 +102,7 @@ class TOTPAuthSrvProvider @Inject() ( appConfig: ApplicationConfig, authProviders: immutable.Set[AuthSrvProvider], userSrv: UserSrv, - db: Database + @Named("with-thehive-schema") db: Database ) extends Provider[AuthSrv] { override def get(): AuthSrv = new TOTPAuthSrv(configuration, appConfig, authProviders, userSrv, db) } diff --git a/thehive/app/org/thp/thehive/services/TagSrv.scala b/thehive/app/org/thp/thehive/services/TagSrv.scala index c44f72eeb0..5fa8174ffc 100644 --- a/thehive/app/org/thp/thehive/services/TagSrv.scala +++ b/thehive/app/org/thp/thehive/services/TagSrv.scala @@ -1,20 +1,22 @@ package org.thp.thehive.services +import akka.actor.ActorRef import gremlin.scala.{Graph, GremlinScala, Key, Vertex} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.services.config.{ApplicationConfig, ConfigItem} -import org.thp.scalligraph.services.{DedupActor, DedupActorProvider, DedupOps, VertexSrv} +import org.thp.scalligraph.services.{IntegrityCheckOps, VertexSrv} import org.thp.scalligraph.steps.StepsOps._ import org.thp.scalligraph.steps.{Traversal, VertexSteps} import org.thp.thehive.models.Tag -import scala.concurrent.duration.{DurationInt, FiniteDuration} import scala.util.{Success, Try} @Singleton -class TagSrv @Inject() (appConfig: ApplicationConfig)(implicit db: Database) extends VertexSrv[Tag, TagSteps] { +class TagSrv @Inject() (appConfig: ApplicationConfig, @Named("integrity-check-actor") integrityCheckActor: ActorRef)( + implicit @Named("with-thehive-schema") db: Database +) extends VertexSrv[Tag, TagSteps] { val autoCreateConfig: ConfigItem[Boolean, Boolean] = appConfig.item[Boolean]("tags.autocreate", "If true, create automatically tag if it doesn't exist") @@ -53,10 +55,17 @@ class TagSrv @Inject() (appConfig: ApplicationConfig)(implicit db: Database) ext } } + override def createEntity(e: Tag)(implicit graph: Graph, authContext: AuthContext): Try[Tag with Entity] = { + integrityCheckActor ! IntegrityCheckActor.EntityAdded("Tag") + super.createEntity(e) + } + def create(tag: Tag)(implicit graph: Graph, authContext: AuthContext): Try[Tag with Entity] = createEntity(tag) + + override def exists(e: Tag)(implicit graph: Graph): Boolean = initSteps.getByName(e.namespace, e.predicate, e.value).exists() } -class TagSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Tag](raw) { +class TagSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Tag](raw) { override def newInstance(newRaw: GremlinScala[Vertex]): TagSteps = new TagSteps(newRaw) override def newInstance(): TagSteps = new TagSteps(raw.clone()) @@ -74,7 +83,7 @@ class TagSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) e def displayName: Traversal[String, String] = this.map(_.toString) } -class TagDedupOps(val db: Database, val service: TagSrv) extends DedupOps[Tag] { +class TagIntegrityCheckOps @Inject() (@Named("with-thehive-schema") val db: Database, val service: TagSrv) extends IntegrityCheckOps[Tag] { override def resolve(entities: List[Tag with Entity])(implicit graph: Graph): Try[Unit] = entities match { case head :: tail => tail.foreach(copyEdge(_, head)) @@ -83,11 +92,3 @@ class TagDedupOps(val db: Database, val service: TagSrv) extends DedupOps[Tag] { case _ => Success(()) } } - -class TagDedupActor @Inject() (db: Database, tagSrv: TagSrv) extends TagDedupOps(db, tagSrv) with DedupActor { - override val min: FiniteDuration = 10.seconds - override val max: FiniteDuration = 1.minute -} - -@Singleton -class TagDedupActorProvider extends DedupActorProvider[TagDedupActor]("Tag") diff --git a/thehive/app/org/thp/thehive/services/TaskSrv.scala b/thehive/app/org/thp/thehive/services/TaskSrv.scala index 8bcf032e4c..d92eaeb884 100644 --- a/thehive/app/org/thp/thehive/services/TaskSrv.scala +++ b/thehive/app/org/thp/thehive/services/TaskSrv.scala @@ -3,7 +3,7 @@ package org.thp.thehive.services import java.util.Date import gremlin.scala._ -import javax.inject.{Inject, Provider, Singleton} +import javax.inject.{Inject, Named, Provider, Singleton} import org.thp.scalligraph.EntitySteps import org.thp.scalligraph.auth.{AuthContext, Permission} import org.thp.scalligraph.models.{Database, Entity} @@ -17,7 +17,7 @@ import play.api.libs.json.{JsNull, JsObject, Json} import scala.util.{Failure, Success, Try} @Singleton -class TaskSrv @Inject() (caseSrvProvider: Provider[CaseSrv], auditSrv: AuditSrv, logSrv: LogSrv)(implicit db: Database) +class TaskSrv @Inject() (caseSrvProvider: Provider[CaseSrv], auditSrv: AuditSrv, logSrv: LogSrv)(implicit @Named("with-thehive-schema") db: Database) extends VertexSrv[Task, TaskSteps] { lazy val caseSrv: CaseSrv = caseSrvProvider.get @@ -101,7 +101,7 @@ class TaskSrv @Inject() (caseSrvProvider: Provider[CaseSrv], auditSrv: AuditSrv, } @EntitySteps[Task] -class TaskSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[Task](raw) { +class TaskSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[Task](raw) { def visible(implicit authContext: AuthContext): TaskSteps = newInstance( raw.filter( diff --git a/thehive/app/org/thp/thehive/services/UserSrv.scala b/thehive/app/org/thp/thehive/services/UserSrv.scala index 7b119b21d9..33780365ab 100644 --- a/thehive/app/org/thp/thehive/services/UserSrv.scala +++ b/thehive/app/org/thp/thehive/services/UserSrv.scala @@ -1,10 +1,11 @@ package org.thp.thehive.services -import java.util.{List => JList} import java.util.regex.Pattern +import java.util.{List => JList} +import akka.actor.ActorRef import gremlin.scala._ -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.apache.tinkerpop.gremlin.process.traversal.Order import org.thp.scalligraph.auth.{AuthContext, AuthContextImpl, Permission} import org.thp.scalligraph.controllers.FFile @@ -22,33 +23,26 @@ import play.api.libs.json.{JsObject, Json} import scala.collection.JavaConverters._ import scala.util.{Failure, Success, Try} -object UserSrv { - val initPassword: String = "secret" - - val init: User = User( - login = "admin@thehive.local", - name = "Default admin user", - apikey = None, - locked = false, - password = Some(LocalPasswordAuthSrv.hashPassword(UserSrv.initPassword)), - totpSecret = None - ) - - val system: User = - User(login = "system@thehive.local", name = "TheHive system user", apikey = None, locked = false, password = None, totpSecret = None) -} - @Singleton -class UserSrv @Inject() (configuration: Configuration, roleSrv: RoleSrv, auditSrv: AuditSrv, attachmentSrv: AttachmentSrv, implicit val db: Database) - extends VertexSrv[User, UserSteps] { - - override val initialValues: Seq[User] = Seq(UserSrv.init, UserSrv.system) +class UserSrv @Inject() ( + configuration: Configuration, + roleSrv: RoleSrv, + auditSrv: AuditSrv, + attachmentSrv: AttachmentSrv, + @Named("integrity-check-actor") integrityCheckActor: ActorRef, + @Named("with-thehive-schema") implicit val db: Database +) extends VertexSrv[User, UserSteps] { val defaultUserDomain: Option[String] = configuration.getOptional[String]("auth.defaultUserDomain") val fullUserNameRegex: Pattern = "[\\p{Graph}&&[^@.]](?:[\\p{Graph}&&[^@]]*)*@\\p{Alnum}+(?:[\\p{Alnum}-.])*".r.pattern val userAttachmentSrv = new EdgeSrv[UserAttachment, User, Attachment] override def steps(raw: GremlinScala[Vertex])(implicit graph: Graph): UserSteps = new UserSteps(raw) + override def createEntity(e: User)(implicit graph: Graph, authContext: AuthContext): Try[User with Entity] = { + integrityCheckActor ! IntegrityCheckActor.EntityAdded("User") + super.createEntity(e) + } + def checkUser(user: User): Try[User] = { val login = if (!user.login.contains('@') && defaultUserDomain.isDefined) s"${user.login}@${defaultUserDomain.get}".toLowerCase @@ -58,6 +52,7 @@ class UserSrv @Inject() (configuration: Configuration, roleSrv: RoleSrv, auditSr else Failure(BadRequestError(s"User login is invalid, it must be an email address (found: ${user.login})")) } + // TODO return Try[Unit] def addUserToOrganisation(user: User with Entity, organisation: Organisation with Entity, profile: Profile with Entity)( implicit graph: Graph, authContext: AuthContext @@ -90,7 +85,7 @@ class UserSrv @Inject() (configuration: Configuration, roleSrv: RoleSrv, auditSr def canSetPassword(user: User with Entity)(implicit graph: Graph, authContext: AuthContext): Boolean = { val userOrganisations = get(user).organisations.name.toList.toSet val operatorOrganisations = current.organisations(Permissions.manageUser).name.toList - operatorOrganisations.contains(OrganisationSrv.administration.name) || (userOrganisations -- operatorOrganisations).isEmpty + operatorOrganisations.contains(Organisation.administration.name) || (userOrganisations -- operatorOrganisations).isEmpty } def delete(user: User with Entity, organisation: Organisation with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { @@ -103,6 +98,8 @@ class UserSrv @Inject() (configuration: Configuration, roleSrv: RoleSrv, auditSr auditSrv.user.delete(user, organisation) } + override def exists(e: User)(implicit graph: Graph): Boolean = initSteps.getByName(e.login).exists() + def lock(user: User with Entity)(implicit graph: Graph, authContext: AuthContext): Try[User with Entity] = for { updatedUser <- get(user).updateOne("locked" -> true) @@ -154,7 +151,7 @@ class UserSrv @Inject() (configuration: Configuration, roleSrv: RoleSrv, auditSr } @EntitySteps[User] -class UserSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) extends VertexSteps[User](raw) { +class UserSteps(raw: GremlinScala[Vertex])(implicit @Named("with-thehive-schema") db: Database, graph: Graph) extends VertexSteps[User](raw) { def current(authContext: AuthContext): UserSteps = get(authContext.userId) def get(idOrName: String): UserSteps = @@ -187,8 +184,8 @@ class UserSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) ) def organisations(requiredPermission: String): OrganisationSteps = { - val isInAdminOrganisation = newInstance().organisations0(requiredPermission).get(OrganisationSrv.administration.name).exists() - if (isInAdminOrganisation) new OrganisationSteps(db.labelFilter(Model.vertex[Organisation])(graph.V)) + val isInAdminOrganisation = newInstance().organisations0(requiredPermission).get(Organisation.administration.name).exists() + if (isInAdminOrganisation) new OrganisationSteps(db.labelFilter(db.getVertexModel[Organisation])(graph.V)) else organisations0(requiredPermission) } @@ -214,7 +211,7 @@ class UserSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) .value[String](Key("name")) .headOption() ) - .getOrElse(OrganisationSrv.administration.name) + .getOrElse(Organisation.administration.name) getAuthContext(requestId, organisationName) } @@ -232,7 +229,7 @@ class UserSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) .map { case (userId, userName, profile) => val scope = - if (organisationName == OrganisationSrv.administration.name) "admin" + if (organisationName == Organisation.administration.name) "admin" else "organisation" val permissions = Permissions.forScope(scope) & profile.as[Profile].permissions AuthContextImpl(userId, userName, organisationName, requestId, permissions) @@ -294,7 +291,58 @@ class UserSteps(raw: GremlinScala[Vertex])(implicit db: Database, graph: Graph) def avatar: AttachmentSteps = new AttachmentSteps(raw.outTo[UserAttachment]) - def systemUser: UserSteps = this.has("login", UserSrv.system.login) + def systemUser: UserSteps = this.has("login", User.system.login) def dashboards: DashboardSteps = new DashboardSteps(raw.inTo[DashboardUser]) } + +@Singleton +class UserIntegrityCheckOps @Inject() ( + @Named("with-thehive-schema") val db: Database, + val service: UserSrv, + profileSrv: ProfileSrv, + organisationSrv: OrganisationSrv, + roleSrv: RoleSrv +) extends IntegrityCheckOps[User] { + + override def initialCheck()(implicit graph: Graph, authContext: AuthContext): Unit = { + super.initialCheck() + for { + adminUser <- service.getOrFail(User.init.login) + adminProfile <- profileSrv.getOrFail(Profile.admin.name) + adminOrganisation <- organisationSrv.getOrFail(Organisation.administration.name) + _ <- roleSrv.create(adminUser, adminOrganisation, adminProfile) + } yield () + () + } + + override def check(): Unit = { + duplicateEntities + .foreach { entities => + db.tryTransaction { implicit graph => + resolve(entities) + } + } + db.tryTransaction { implicit graph => + duplicateInEdges[TaskUser](service.initSteps.raw).flatMap(firstCreatedElement(_)).foreach(e => removeEdges(e._2)) + duplicateInEdges[CaseUser](service.initSteps.raw).flatMap(firstCreatedElement(_)).foreach(e => removeEdges(e._2)) + duplicateLinks[Vertex, Vertex]( + service.initSteps.raw, + (_.out("UserRole"), _.in("UserRole")), + (_.out("RoleOrganisation"), _.in("RoleOrganisation")) + ).flatMap(firstCreatedElement(_)).foreach(e => removeVertices(e._2)) + Success(()) + } + () + } + + override def resolve(entities: List[User with Entity])(implicit graph: Graph): Try[Unit] = { + firstCreatedEntity(entities).foreach { + case (firstUser, otherUsers) => + otherUsers.foreach(copyEdge(_, firstUser)) + otherUsers.foreach(service.get(_).remove()) + } + Success(()) + } + +} diff --git a/thehive/app/org/thp/thehive/services/notification/NotificationActor.scala b/thehive/app/org/thp/thehive/services/notification/NotificationActor.scala index 870d6bd0ef..2ac517d587 100644 --- a/thehive/app/org/thp/thehive/services/notification/NotificationActor.scala +++ b/thehive/app/org/thp/thehive/services/notification/NotificationActor.scala @@ -3,7 +3,7 @@ package org.thp.thehive.services.notification import akka.actor.{Actor, ActorIdentity, Identify} import akka.util.Timeout import gremlin.scala.{__, By, Graph, Key, P, Vertex} -import javax.inject.Inject +import javax.inject.{Inject, Named} import org.thp.scalligraph.BadConfigurationError import org.thp.scalligraph.models.{Database, Entity} import org.thp.scalligraph.services.{EventSrv, RichElement, RichVertexGremlinScala} @@ -80,7 +80,7 @@ class NotificationActor @Inject() ( userSrv: UserSrv, notificationSrv: NotificationSrv, cache: SyncCacheApi, - implicit val db: Database + @Named("with-thehive-schema") implicit val db: Database ) extends Actor { import context.dispatcher lazy val logger: Logger = Logger(getClass) diff --git a/thehive/app/org/thp/thehive/services/package.scala b/thehive/app/org/thp/thehive/services/package.scala index f3eff687ed..103b8d85d3 100644 --- a/thehive/app/org/thp/thehive/services/package.scala +++ b/thehive/app/org/thp/thehive/services/package.scala @@ -5,10 +5,25 @@ import org.thp.scalligraph.steps.VertexSteps package object services { implicit class EntityStepsOps[E <: Product](steps: VertexSteps[E]) { - def asCase = new CaseSteps(steps.raw)(steps.db, steps.graph) - def asTask = new TaskSteps(steps.raw)(steps.db, steps.graph) - def asLog = new LogSteps(steps.raw)(steps.db, steps.graph) - def asObservable = new ObservableSteps(steps.raw)(steps.db, steps.graph) - def asAlert = new AlertSteps(steps.raw)(steps.db, steps.graph) + def asCase: CaseSteps = steps match { + case caseSteps: CaseSteps => caseSteps + case _ => new CaseSteps(steps.raw)(steps.db, steps.graph) + } + def asTask: TaskSteps = steps match { + case taskSteps: TaskSteps => taskSteps + case _ => new TaskSteps(steps.raw)(steps.db, steps.graph) + } + def asLog: LogSteps = steps match { + case logSteps: LogSteps => logSteps + case _ => new LogSteps(steps.raw)(steps.db, steps.graph) + } + def asObservable: ObservableSteps = steps match { + case observableSteps: ObservableSteps => observableSteps + case _ => new ObservableSteps(steps.raw)(steps.db, steps.graph) + } + def asAlert: AlertSteps = steps match { + case alertSteps: AlertSteps => alertSteps + case _ => new AlertSteps(steps.raw)(steps.db, steps.graph) + } } } diff --git a/thehive/test/org/thp/thehive/DatabaseBuilder.scala b/thehive/test/org/thp/thehive/DatabaseBuilder.scala index ed8d06348f..8ee69af240 100644 --- a/thehive/test/org/thp/thehive/DatabaseBuilder.scala +++ b/thehive/test/org/thp/thehive/DatabaseBuilder.scala @@ -2,15 +2,8 @@ package org.thp.thehive import java.io.File -import scala.io.Source -import scala.reflect.runtime.{universe => ru} -import scala.util.{Failure, Success, Try} - -import play.api.Logger -import play.api.libs.json.{JsArray, JsObject, JsValue, Json} - import gremlin.scala.{KeyValue => _, _} -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.scalactic.Or import org.thp.scalligraph.RichOption import org.thp.scalligraph.auth.AuthContext @@ -19,6 +12,12 @@ import org.thp.scalligraph.models.{Database, Entity, Schema} import org.thp.scalligraph.services.{EdgeSrv, VertexSrv} import org.thp.thehive.models._ import org.thp.thehive.services._ +import play.api.Logger +import play.api.libs.json.{JsArray, JsObject, JsValue, Json} + +import scala.io.Source +import scala.reflect.runtime.{universe => ru} +import scala.util.{Failure, Success, Try} @Singleton class DatabaseBuilder @Inject() ( @@ -48,7 +47,7 @@ class DatabaseBuilder @Inject() ( lazy val logger: Logger = Logger(getClass) - def build()(implicit db: Database, authContext: AuthContext): Try[Unit] = { + def build()(implicit @Named("with-thehive-schema") db: Database, authContext: AuthContext): Try[Unit] = { lazy val logger: Logger = Logger(getClass) logger.info("Initialize database schema") diff --git a/thehive/test/org/thp/thehive/TestAppBuilder.scala b/thehive/test/org/thp/thehive/TestAppBuilder.scala index ef4569c5b5..16a33d0ce9 100644 --- a/thehive/test/org/thp/thehive/TestAppBuilder.scala +++ b/thehive/test/org/thp/thehive/TestAppBuilder.scala @@ -3,13 +3,13 @@ package org.thp.thehive import java.io.File import java.nio.file.{Files, Paths} +import javax.inject.{Inject, Provider, Singleton} import org.apache.commons.io.FileUtils -import org.thp.scalligraph.AppBuilder import org.thp.scalligraph.auth._ -import org.thp.scalligraph.janus.JanusDatabase import org.thp.scalligraph.models.{Database, Schema} import org.thp.scalligraph.services.{LocalFileSystemStorageSrv, StorageSrv} -import org.thp.thehive.models.TheHiveSchema +import org.thp.scalligraph.{janus, AppBuilder} +import org.thp.thehive.models.TheHiveSchemaDefinition import org.thp.thehive.services.notification.notifiers.{AppendToFileProvider, EmailerProvider, NotifierProvider} import org.thp.thehive.services.notification.triggers._ import org.thp.thehive.services.{LocalKeyAuthProvider, LocalPasswordAuthProvider, LocalUserSrv} @@ -24,7 +24,7 @@ trait TestAppBuilder { (new AppBuilder) .bind[UserSrv, LocalUserSrv] .bind[StorageSrv, LocalFileSystemStorageSrv] - .bind[Schema, TheHiveSchema] + .bind[Schema, TheHiveSchemaDefinition] .multiBind[AuthSrvProvider](classOf[LocalPasswordAuthProvider], classOf[LocalKeyAuthProvider], classOf[HeaderAuthProvider]) .multiBind[NotifierProvider](classOf[AppendToFileProvider]) .multiBind[NotifierProvider](classOf[EmailerProvider]) @@ -35,8 +35,7 @@ trait TestAppBuilder { .bindToProvider[AuthSrv, MultiAuthSrvProvider] .bindActor[DummyActor]("config-actor") .bindActor[DummyActor]("notification-actor") - .bindActor[DummyActor]("case-dedup-actor") - .bindActor[DummyActor]("data-dedup-actor") + .bindActor[DummyActor]("integrity-check-actor") .addConfiguration("auth.providers = [{name:local},{name:key},{name:header, userHeader:user}]") .addConfiguration("play.modules.disabled = [org.thp.scalligraph.ScalligraphModule, org.thp.thehive.TheHiveModule]") .addConfiguration("play.mailer.mock = yes") @@ -58,15 +57,17 @@ trait TestAppBuilder { |} |akka.cluster.jmx.multi-mbeans-in-same-jvm: on |""".stripMargin) - .bind[Database, JanusDatabase] + .bind[Database, janus.JanusDatabase] + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-schema") - app[DatabaseBuilder].build()(app[Database], app[UserSrv].getSystemAuthContext) + app[DatabaseBuilder].build()(app.apply[Database], app[UserSrv].getSystemAuthContext) } } val storageDirectory = Files.createTempDirectory(Paths.get("target"), "janusgraph-test-database").toFile FileUtils.copyDirectory(new File(s"target/janusgraph-test-database-$databaseName"), storageDirectory) val app = appConfigure - .bind[Database, JanusDatabase] + .bind[Database, janus.JanusDatabase] + .bindNamedToProvider[Database, BasicDatabaseProvider]("with-thehive-schema") .addConfiguration(s""" |db { | provider: janusgraph @@ -85,3 +86,8 @@ trait TestAppBuilder { } } } + +@Singleton +class BasicDatabaseProvider @Inject() (database: Database) extends Provider[Database] { + override def get(): Database = database +}