Skip to content

Commit

Permalink
#2317 Fix notification execution
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Jan 13, 2022
1 parent b94983d commit f121b92
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 63 deletions.
2 changes: 1 addition & 1 deletion thehive/app/org/thp/thehive/services/AuditSrv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ object AuditOps {
_.by
.by(_.context.entityMap.option)
.by(_.`object`.entityMap.option)
.by(_.organisation.fold)
.by(_.organisation.dedup.fold)
)

def richAudit: Traversal[RichAudit, JMap[String, Any], Converter[RichAudit, JMap[String, Any]]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import javax.inject.Inject
import scala.collection.immutable
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.util.Try
import scala.util.{Success, Try}

object NotificationTopic {
def apply(role: String = ""): String = if (role.isEmpty) "notification" else s"notification-$role"
Expand Down Expand Up @@ -123,17 +123,23 @@ class NotificationActor @Inject() (
notificationConfigs
.foreach {
case notificationConfig if notificationConfig.roleRestriction.isEmpty || (notificationConfig.roleRestriction & roles).nonEmpty =>
val result = for {
trigger <- notificationSrv.getTrigger(notificationConfig.triggerConfig)
if trigger.filter(audit, context, organisation, user)
notifier <- notificationSrv.getNotifier(notificationConfig.notifierConfig)
_ = logger.info(s"Execution of notifier ${notifier.name} for user $user")
} yield notifier.execute(audit, context, `object`, organisation, user).failed.foreach { error =>
logger.error(s"Execution of notifier ${notifier.name} has failed for user $user", error)
}
result.failed.foreach { error =>
logger.error(s"Execution of notification $notificationConfig has failed for user $user / ${organisation.name}", error)
}
notificationSrv
.getTrigger(notificationConfig.triggerConfig)
.flatMap { trigger =>
logger.debug(s"Checking trigger $trigger against $audit, $context, $organisation, $user")
if (trigger.filter(audit, context, organisation, user)) notificationSrv.getNotifier(notificationConfig.notifierConfig).map(Some(_))
else Success(None)
}
.map(_.foreach { notififer =>
logger.info(s"Execution of notifier $notififer for user $user")
notififer.execute(audit, context, `object`, organisation, user).failed.foreach { error =>
logger.error(s"Execution of notifier $notififer has failed for user $user", error)
}
})
.failed
.foreach { error =>
logger.error(s"Execution of notification $notificationConfig has failed for user $user / ${organisation.name}", error)
}
case notificationConfig =>
logger.debug(s"Notification has role restriction($notificationConfig.roleRestriction) and it is not applicable here ($roles)")
Future
Expand All @@ -159,47 +165,51 @@ class NotificationActor @Inject() (
case (audit, context, obj, organisations) =>
logger.debug(s"Notification is related to $audit, $context, ${organisations.map(_.name).mkString(",")}")
organisations.foreach { organisation =>
triggerMap
lazy val organisationNotificationConfigs = organisationSrv
.get(organisation)
.config
.has(_.name, "notification")
.value(_.value)
.headOption
.toSeq
.flatMap(_.asOpt[Seq[NotificationConfig]].getOrElse(Nil))
val orgNotifs = triggerMap
.getOrElse(organisation._id, Map.empty)
val mustNotifyOrganisation = orgNotifs
.exists {
case (trigger, (true, _)) => trigger.preFilter(audit, context, organisation)
case _ => false
}
if (mustNotifyOrganisation)
executeNotification(None, organisationNotificationConfigs.filterNot(_.delegate), audit, context, obj, organisation)
val mustNotifyOrgUsers = orgNotifs.exists {
case (trigger, (false, _)) => trigger.preFilter(audit, context, organisation)
case _ => false
}
if (mustNotifyOrgUsers) {
val userConfig = organisationNotificationConfigs.filter(_.delegate)
organisationSrv
.get(organisation)
.users
.filter(_.config.hasNot(_.name, "notification"))
.toIterator
.foreach { user =>
executeNotification(Some(user), userConfig, audit, context, obj, organisation)
}
}
val usersToNotify = orgNotifs.flatMap {
case (trigger, (_, userIds)) if userIds.nonEmpty && trigger.preFilter(audit, context, organisation) => userIds
case _ => Nil
}.toSeq
userSrv
.getByIds(usersToNotify: _*)
.project(_.by.by(_.config.has(_.name, "notification").value(_.value).option))
.foreach {
case (trigger, (inOrg, userIds)) if trigger.preFilter(audit, context, organisation) =>
logger.debug(s"Notification trigger ${trigger.name} is applicable for $audit")
if (userIds.nonEmpty)
userSrv
.getByIds(userIds: _*)
.project(
_.by
.by(_.config("notification").value(_.value).fold)
)
.toIterator
.foreach {
case (user, notificationConfig) =>
val config = notificationConfig.flatMap(_.asOpt[NotificationConfig])
executeNotification(Some(user), config, audit, context, obj, organisation)
}
if (inOrg)
organisationSrv
.get(organisation)
.config
.has(_.name, "notification")
.value(_.value)
.toIterator
.foreach { notificationConfig: JsValue =>
val (userConfig, orgConfig) = notificationConfig
.asOpt[Seq[NotificationConfig]]
.getOrElse(Nil)
.partition(_.delegate)
organisationSrv
.get(organisation)
.users
.filter(_.config.hasNot(_.name, "notification"))
.toIterator
.foreach { user =>
executeNotification(Some(user), userConfig, audit, context, obj, organisation)
}
executeNotification(None, orgConfig, audit, context, obj, organisation)
}
case (trigger, _) => logger.debug(s"Notification trigger ${trigger.name} is NOT applicable for $audit")
case (user, Some(config)) =>
config.asOpt[Seq[NotificationConfig]].foreach { userConfig =>
executeNotification(Some(user), userConfig, audit, context, obj, organisation)
}
case _ =>
}
}
case _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import scala.util.{Success, Try}
@Singleton
class AlertCreatedProvider @Inject() extends TriggerProvider {
override val name: String = "AlertCreated"
override def apply(config: Configuration): Try[Trigger] = Success(new AlertCreated())
override def apply(config: Configuration): Try[Trigger] = Success(AlertCreated)
}

class AlertCreated extends GlobalTrigger {
object AlertCreated extends GlobalTrigger {
override val name: String = "AlertCreated"
override val auditAction: String = Audit.create
override val entityName: String = "Alert"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import scala.util.{Success, Try}
@Singleton
class CaseCreatedProvider @Inject() extends TriggerProvider {
override val name: String = "CaseCreated"
override def apply(config: Configuration): Try[Trigger] = Success(new CaseCreated())
override def apply(config: Configuration): Try[Trigger] = Success(CaseCreated)
}

class CaseCreated() extends Trigger {
object CaseCreated extends Trigger {
override val name: String = "CaseCreated"

override def preFilter(audit: Audit with Entity, context: Option[Entity], organisation: Organisation with Entity): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import scala.util.{Success, Try}
@Singleton
class CaseShareProvider @Inject() extends TriggerProvider {
override val name: String = "CaseShared"
override def apply(config: Configuration): Try[Trigger] = Success(new CaseShared())
override def apply(config: Configuration): Try[Trigger] = Success(CaseShared)
}

class CaseShared() extends Trigger {
object CaseShared extends Trigger {
override val name: String = "CaseShared"

override def preFilter(audit: Audit with Entity, context: Option[Entity], organisation: Organisation with Entity): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ class FilteredEventProvider @Inject() extends TriggerProvider {
override val name: String = "FilteredEvent"
override def apply(config: Configuration): Try[Trigger] = {
val filter = Json.parse(config.underlying.getValue("filter").render(ConfigRenderOptions.concise())).as[EventFilter]
Success(new FilteredEvent(filter))
Success(FilteredEvent(filter))
}
}

class FilteredEvent(eventFilter: EventFilter) extends Trigger {
case class FilteredEvent(eventFilter: EventFilter) extends Trigger {
override val name: String = "FilteredEvent"

override def preFilter(audit: Audit with Entity, context: Option[Entity], organisation: Organisation with Entity): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import scala.util.{Success, Try}
@Singleton
class JobFinishedProvider @Inject() extends TriggerProvider {
override val name: String = "JobFinished"
override def apply(config: Configuration): Try[Trigger] = Success(new JobFinished())
override def apply(config: Configuration): Try[Trigger] = Success(JobFinished)
}

class JobFinished extends GlobalTrigger {
object JobFinished extends GlobalTrigger {
override val name: String = "JobFinished"
override val auditAction: String = Audit.update
override val entityName: String = "Job"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ class LogInMyTask(logSrv: LogSrv) extends Trigger {

def taskAssignee(logId: EntityId)(implicit graph: Graph): Option[String] =
logSrv.getByIds(logId).task.assignee.value(_.login).headOption

override def toString: String = "LogInMyTask"
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ class TaskAssigned(taskSrv: TaskSrv) extends Trigger {

def taskAssignee(taskId: EntityId, userId: EntityId)(implicit graph: Graph): Option[User with Entity] =
taskSrv.getByIds(taskId).assignee.get(userId).headOption

override def toString: String = "TaskAssigned"
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class AlertCreatedTest extends PlaySpecification with TestAppBuilder {
user2 must beSuccessfulTry
user1 must beSuccessfulTry

val alertCreated = new AlertCreated()
val alertCreated = AlertCreated

alertCreated.filter(audit.get, Some(alert.get), organisation.get, user1.toOption) must beFalse
alertCreated.filter(audit.get, Some(alert.get), organisation.get, user2.toOption) must beTrue
Expand Down

0 comments on commit f121b92

Please sign in to comment.