Skip to content

Commit b820df8

Browse files
committed
Merge branch 'release/3.1.7'
2 parents 619a70f + 671643d commit b820df8

File tree

20 files changed

+240
-99
lines changed

20 files changed

+240
-99
lines changed
Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,71 @@
11
package org.thp.cortex.controllers
22

3-
import scala.concurrent.ExecutionContext
4-
3+
import scala.concurrent.{ExecutionContext, Future}
54
import play.api.Configuration
65
import play.api.http.Status
76
import play.api.libs.json.Json.toJsFieldJsValueWrapper
8-
import play.api.libs.json.{JsBoolean, JsString, Json}
7+
import play.api.libs.json.{JsBoolean, JsNull, JsString, Json}
98
import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
10-
119
import com.sksamuel.elastic4s.ElasticDsl
10+
import org.elastic4play.controllers.Authenticated
11+
1212
import javax.inject.{Inject, Singleton}
1313
import org.elasticsearch.client.Node
14-
import org.thp.cortex.models.Worker
15-
14+
import org.thp.cortex.models.{Roles, Worker, WorkerType}
1615
import org.elastic4play.services.AuthSrv
1716
import org.elastic4play.services.auth.MultiAuthSrv
17+
import org.thp.cortex.services.WorkerSrv
1818

1919
@Singleton
2020
class StatusCtrl @Inject() (
2121
configuration: Configuration,
2222
authSrv: AuthSrv,
23+
workerSrv: WorkerSrv,
2324
components: ControllerComponents,
25+
authenticated: Authenticated,
2426
implicit val ec: ExecutionContext
2527
) extends AbstractController(components)
2628
with Status {
2729

2830
private[controllers] def getVersion(c: Class[_]) = Option(c.getPackage.getImplementationVersion).getOrElse("SNAPSHOT")
2931

30-
def get: Action[AnyContent] = Action {
31-
Ok(
32-
Json.obj(
33-
"versions" -> Json.obj(
34-
"Cortex" -> getVersion(classOf[Worker]),
35-
"Elastic4Play" -> getVersion(classOf[AuthSrv]),
36-
"Play" -> getVersion(classOf[AbstractController]),
37-
"Elastic4s" -> getVersion(classOf[ElasticDsl]),
38-
"ElasticSearch client" -> getVersion(classOf[Node])
39-
),
40-
"config" -> Json.obj(
41-
"protectDownloadsWith" -> configuration.get[String]("datastore.attachment.password"),
42-
"authType" -> (authSrv match {
43-
case multiAuthSrv: MultiAuthSrv =>
44-
multiAuthSrv.authProviders.map { a =>
45-
JsString(a.name)
46-
}
47-
case _ => JsString(authSrv.name)
48-
}),
49-
"capabilities" -> authSrv.capabilities.map(c => JsString(c.toString)),
50-
"ssoAutoLogin" -> JsBoolean(configuration.getOptional[Boolean]("auth.sso.autologin").getOrElse(false))
32+
def get: Action[AnyContent] =
33+
Action {
34+
Ok(
35+
Json.obj(
36+
"versions" -> Json.obj(
37+
"Cortex" -> getVersion(classOf[Worker]),
38+
"Elastic4Play" -> getVersion(classOf[AuthSrv]),
39+
"Play" -> getVersion(classOf[AbstractController]),
40+
"Elastic4s" -> getVersion(classOf[ElasticDsl]),
41+
"ElasticSearch client" -> getVersion(classOf[Node])
42+
),
43+
"config" -> Json.obj(
44+
"protectDownloadsWith" -> configuration.get[String]("datastore.attachment.password"),
45+
"authType" -> (authSrv match {
46+
case multiAuthSrv: MultiAuthSrv =>
47+
multiAuthSrv.authProviders.map { a =>
48+
JsString(a.name)
49+
}
50+
case _ => JsString(authSrv.name)
51+
}),
52+
"capabilities" -> authSrv.capabilities.map(c => JsString(c.toString)),
53+
"ssoAutoLogin" -> JsBoolean(configuration.getOptional[Boolean]("auth.sso.autologin").getOrElse(false))
54+
)
5155
)
5256
)
53-
)
54-
}
57+
}
58+
59+
def getAlerts: Action[AnyContent] =
60+
authenticated(Roles.read).async { implicit request =>
61+
workerSrv.obsoleteWorkersForUser(request.userId).map { obsoleteWorkers =>
62+
val (obsoleteAnalyzers, obsoleteResponders) = obsoleteWorkers.partition(_.tpe() == WorkerType.analyzer)
63+
val alerts =
64+
(if (obsoleteAnalyzers.nonEmpty) List("ObsoleteAnalyzers") else Nil) :::
65+
(if (obsoleteResponders.nonEmpty) List("ObsoleteResponders") else Nil)
66+
Ok(Json.toJson(alerts))
67+
}
68+
}
5569

5670
def health: Action[AnyContent] = TODO
5771
}

app/org/thp/cortex/models/WorkerDefinition.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,23 @@ object WorkerDefinition {
110110

111111
def reads(workerType: WorkerType.Type): Reads[List[WorkerDefinition]] = {
112112
val reads = singleReads(workerType)
113-
reads.map(List(_)) orElse Reads.list(reads)
113+
implicitly[Reads[JsArray]]
114+
reads.map(List(_)) orElse JsArrayReads.map(array =>
115+
array
116+
.value
117+
.toList
118+
.flatMap { js =>
119+
reads
120+
.reads(js)
121+
.fold(
122+
invalid => {
123+
logger.warn(s"The catalog contains an invalid entry\n entry:$js\n $invalid")
124+
Seq.empty
125+
},
126+
Seq(_)
127+
)
128+
}
129+
)
114130
}
115131

116132
implicit val writes: Writes[WorkerDefinition] = Writes[WorkerDefinition] { workerDefinition =>

app/org/thp/cortex/services/DockerJobRunnerSrv.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class DockerJobRunnerSrv(
6262
ec: ExecutionContext
6363
): Try[Unit] = {
6464
import scala.collection.JavaConverters._
65-
if (autoUpdate) client.pull(dockerImage)
65+
if (autoUpdate) Try(client.pull(dockerImage))
6666
// ContainerConfig.builder().addVolume()
6767
val hostConfigBuilder = HostConfig.builder()
6868
config.getOptional[Seq[String]]("docker.container.capAdd").map(_.asJava).foreach(hostConfigBuilder.capAdd)

app/org/thp/cortex/services/JobRunnerSrv.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class JobRunnerSrv @Inject() (
129129
.deepMerge(worker.config)
130130
.deepMerge(proxy_http)
131131
.deepMerge(proxy_https)
132-
(worker.config \ "cacerts").asOpt[String].foreach { cacerts =>
132+
(worker.config \ "cacerts").asOpt[String].filterNot(_.trim.isEmpty).foreach { cacerts =>
133133
val cacertsFile = jobFolder.resolve("input").resolve("cacerts")
134134
Files.write(cacertsFile, cacerts.getBytes)
135135
}

app/org/thp/cortex/services/WorkerSrv.scala

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
11
package org.thp.cortex.services
22

3-
import java.net.URL
4-
import java.nio.file.{Files, Path, Paths}
5-
6-
import scala.collection.JavaConverters._
7-
import scala.concurrent.{ExecutionContext, Future}
8-
import scala.io.Codec
9-
import scala.util.{Failure, Success, Try}
10-
11-
import play.api.libs.json.{JsArray, JsObject, JsString, Json}
12-
import play.api.{Configuration, Logger}
13-
143
import akka.NotUsed
154
import akka.stream.Materializer
165
import akka.stream.scaladsl.{Sink, Source}
17-
import javax.inject.{Inject, Provider, Singleton}
18-
import org.scalactic.Accumulation._
19-
import org.scalactic._
20-
import org.thp.cortex.models._
21-
226
import org.elastic4play._
237
import org.elastic4play.controllers.{Fields, StringInputValue}
248
import org.elastic4play.database.ModifyConfig
9+
import org.elastic4play.services.QueryDSL.any
2510
import org.elastic4play.services._
11+
import org.scalactic.Accumulation._
12+
import org.scalactic._
13+
import org.thp.cortex.models._
14+
import play.api.libs.json.{JsObject, JsString, Json}
15+
import play.api.{Configuration, Logger}
16+
17+
import java.net.URL
18+
import java.nio.file.{Files, Path, Paths}
19+
import javax.inject.{Inject, Provider, Singleton}
20+
import scala.collection.JavaConverters._
21+
import scala.concurrent.{ExecutionContext, Future}
22+
import scala.io.Codec
23+
import scala.util.{Failure, Success, Try}
2624

2725
@Singleton
2826
class WorkerSrv @Inject() (
@@ -128,21 +126,23 @@ class WorkerSrv @Inject() (
128126
private def find(queryDef: QueryDef, range: Option[String], sortBy: Seq[String]): (Source[Worker, NotUsed], Future[Long]) =
129127
findSrv[WorkerModel, Worker](workerModel, queryDef, range, sortBy)
130128

131-
def rescan(): Unit = {
132-
import org.elastic4play.services.QueryDSL._
129+
def rescan(): Unit =
133130
scan(
134131
analyzersURLs.map(_ -> WorkerType.analyzer) ++
135132
respondersURLs.map(_ -> WorkerType.responder)
136-
).onComplete { _ =>
137-
userSrv.inInitAuthContext { implicit authContext =>
138-
find(any, Some("all"), Nil)._1.runForeach { worker =>
139-
workerMap.get(worker.workerDefinitionId()) match {
140-
case Some(wd) => update(worker, Fields.empty.set("dataTypeList", Json.toJson(wd.dataTypeList)))
141-
case None => update(worker, Fields.empty.set("dataTypeList", JsArray.empty))
142-
}
143-
}
144-
}
133+
)
134+
135+
def obsoleteWorkersForUser(userId: String): Future[Seq[Worker]] =
136+
userSrv.get(userId).flatMap { user =>
137+
obsoleteWorkersForOrganization(user.organization())
145138
}
139+
140+
def obsoleteWorkersForOrganization(organizationId: String): Future[Seq[Worker]] = {
141+
import org.elastic4play.services.QueryDSL._
142+
find(withParent("organization", organizationId), Some("all"), Nil)
143+
._1
144+
.filterNot(worker => workerMap.contains(worker.workerDefinitionId()))
145+
.runWith(Sink.seq)
146146
}
147147

148148
def scan(workerUrls: Seq[(String, WorkerType.Type)]): Future[Unit] = {
@@ -250,7 +250,7 @@ class WorkerSrv @Inject() (
250250
.set("command", workerDefinition.command.map(p => JsString(p.toString)))
251251
.set("url", workerDefinition.url)
252252
.set("license", workerDefinition.license)
253-
.set("baseConfig", workerDefinition.baseConfiguration.map(JsString.apply))
253+
.set("baseConfig", workerDefinition.baseConfiguration.fold(JsString(workerDefinition.name))(JsString.apply))
254254
.set("configuration", cfg.toString)
255255
.set("type", workerDefinition.tpe.toString)
256256
.addIfAbsent("dataTypeList", StringInputValue(workerDefinition.dataTypeList))

conf/routes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ POST /api/ssoLogin org.thp.cort
1313
###################
1414
# API used by TheHive
1515
GET /api/status org.thp.cortex.controllers.StatusCtrl.get
16+
GET /api/alert org.thp.cortex.controllers.StatusCtrl.getAlerts
1617
GET /api/analyzer org.thp.cortex.controllers.AnalyzerCtrl.find
1718
POST /api/analyzer/_search org.thp.cortex.controllers.AnalyzerCtrl.find
1819
GET /api/analyzer/:id org.thp.cortex.controllers.AnalyzerCtrl.get(id)

version.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ThisBuild / version := "3.1.6-1"
1+
ThisBuild / version := "3.1.7-1"

www/.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v12.18

www/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cortex",
3-
"version": "3.1.6",
3+
"version": "3.1.7",
44
"description": "A powerfull observable analysis engine",
55
"license": "AGPL-3.0-or-later",
66
"homepage": "https://github.com/TheHive-Project/Cortex",

www/src/app/components/header/header.component.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,24 @@ class HeaderController {
1313
constructor(
1414
$state,
1515
$log,
16-
$q,
1716
$uibModal,
17+
$scope,
1818
AuthService,
1919
AnalyzerService,
20-
NotificationService
20+
NotificationService,
21+
AlertService
2122
) {
2223
'ngInject';
2324

2425
this.$state = $state;
2526
this.$log = $log;
2627
this.$uibModal = $uibModal;
27-
this.$q = $q;
28+
this.$scope = $scope;
2829

2930
this.AuthService = AuthService;
3031
this.AnalyzerService = AnalyzerService;
3132
this.NotificationService = NotificationService;
33+
this.AlertService = AlertService;
3234
}
3335

3436
logout() {
@@ -44,6 +46,11 @@ class HeaderController {
4446
$onInit() {
4547
this.isOrgAdmin = this.AuthService.isOrgAdmin(this.main.currentUser);
4648
this.isSuperAdmin = this.AuthService.isSuperAdmin(this.main.currentUser);
49+
50+
this.AlertService.startUpdate();
51+
this.$scope.$on('$destroy', () => {
52+
this.AlertService.stopUpdate();
53+
});
4754
}
4855

4956
newAnalysis() {
@@ -67,8 +74,8 @@ class HeaderController {
6774
if (!_.isString(err)) {
6875
this.NotificationService.error(
6976
err.data.message ||
70-
`An error occurred: ${err.statusText}` ||
71-
'An unexpected error occurred'
77+
`An error occurred: ${err.statusText}` ||
78+
'An unexpected error occurred'
7279
);
7380
}
7481
});

0 commit comments

Comments
 (0)