All Downloads are FREE. Search and download functionalities are using the official Maven repository.

events.audit.scala Maven / Gradle / Ivy

package otoroshi.events

import akka.util.ByteString
import otoroshi.env.Env
import otoroshi.models._
import org.joda.time.DateTime
import otoroshi.script.{Job, JobContext}
import play.api.libs.json._
import otoroshi.ssl.Cert
import otoroshi.utils.syntax.implicits.BetterJsValue

import scala.concurrent.{ExecutionContext, Future}

trait AuditEvent extends AnalyticEvent {
  override def `@type`: String = "AuditEvent"
}

object AuditEvent {
  def generic(audit: String, `@service`: String = "Otoroshi", `@serviceId`: String = "")(
      additionalPayload: JsObject
  )(implicit env: Env): GenericAudit = {
    GenericAudit(audit, env, `@service`, `@serviceId`)(additionalPayload)
  }
}

case class GenericAudit(audit: String, env: Env, `@service`: String = "Otoroshi", `@serviceId`: String = "")(
    additionalPayload: JsObject
) extends AuditEvent {

  val `@id`: String                 = env.snowflakeGenerator.nextIdStr()
  val `@timestamp`: DateTime        = DateTime.now()
  val fromOrigin: Option[String]    = None
  val fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue = {
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> env.env,
      "audit"      -> audit
    ) ++ additionalPayload
  }
}

// TODO: include UA
case class BackOfficeEvent(
    `@id`: String,
    `@env`: String,
    user: BackOfficeUser,
    action: String,
    message: String,
    from: String,
    ua: String,
    metadata: JsObject = Json.obj(),
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = Some(from)
  override def fromUserAgent: Option[String] = Some(ua)

  override def toJson(implicit env: Env): JsValue =
    Json.obj(
      "@id"          -> `@id`,
      "@timestamp"   -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"        -> `@type`,
      "@product"     -> env.eventsName,
      "@serviceId"   -> `@serviceId`,
      "@service"     -> `@service`,
      "@env"         -> `@env`,
      "audit"        -> "BackOfficeEvent",
      "userName"     -> user.name,
      "userEmail"    -> user.email,
      "user"         -> user.profile,
      "userRandomId" -> user.randomId,
      "action"       -> action,
      "from"         -> from,
      "message"      -> message,
      "metadata"     -> metadata
    )
}

case class AdminApiEvent(
    `@id`: String,
    `@env`: String,
    apiKey: Option[ApiKey],
    user: Option[JsValue],
    action: String,
    message: String,
    from: String,
    ua: String,
    metadata: JsValue = Json.obj(),
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = Some(from)
  override def fromUserAgent: Option[String] = Some(ua)

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "AdminApiEvent",
      "user"       -> user.getOrElse(JsNull).as[JsValue],
      "apiKey"     -> apiKey.map(ak => ak.toJson).getOrElse(JsNull).as[JsValue],
      "action"     -> action,
      "from"       -> from,
      "message"    -> message,
      "metadata"   -> metadata
    )
}

case class SnowMonkeyOutageRegisteredEvent(
    `@id`: String,
    `@env`: String,
    action: String,
    message: String,
    config: SnowMonkeyConfig,
    desc: ServiceDescriptor,
    dryRun: Boolean,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "SnowMonkeyOutageRegisteredEvent",
      "user"       -> "--",
      "apiKey"     -> "--",
      "action"     -> action,
      "from"       -> "--",
      "message"    -> message,
      "config"     -> config.asJson,
      "service"    -> desc.toJson,
      "dryRun"     -> dryRun
    )
}

case class CircuitBreakerOpenedEvent(
    `@id`: String,
    `@env`: String,
    target: Target,
    service: JsValue,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = service.select("id").asOpt[String].getOrElse("--")

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "CircuitBreakerOpenedEvent",
      "target"     -> target.toJson,
      "service"    -> service
    )
}

case class CircuitBreakerClosedEvent(
    `@id`: String,
    `@env`: String,
    target: Target,
    service: JsValue,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = service.select("id").asOpt[String].getOrElse("--")

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "CircuitBreakerClosedEvent",
      "target"     -> target.toJson,
      "service"    -> service
    )
}

case class MaxConcurrentRequestReachedEvent(
    `@id`: String,
    `@env`: String,
    limit: Long,
    current: Long,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "MaxConcurrentRequestReachedEvent",
      "limit"      -> limit,
      "current"    -> current
    )
}

case class JobStartedEvent(
    `@id`: String,
    `@env`: String,
    job: Job,
    ctx: JobContext,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "JobStartedEvent",
      "job"        -> job.auditJson(ctx)(_env)
    )
}

case class JobStoppedEvent(
    `@id`: String,
    `@env`: String,
    job: Job,
    ctx: JobContext,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "JobStoppedEvent",
      "job"        -> job.auditJson(ctx)(_env)
    )
}

case class JobRunEvent(
    `@id`: String,
    `@env`: String,
    job: Job,
    ctx: JobContext,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "JobRunEvent",
      "job"        -> job.auditJson(ctx)(_env)
    )
}

case class JobErrorEvent(
    `@id`: String,
    `@env`: String,
    job: Job,
    ctx: JobContext,
    err: Throwable,
    `@timestamp`: DateTime = DateTime.now()
) extends AuditEvent {

  override def `@service`: String   = "Otoroshi"
  override def `@serviceId`: String = "--"

  override def fromOrigin: Option[String]    = None
  override def fromUserAgent: Option[String] = None

  override def toJson(implicit _env: Env): JsValue =
    Json.obj(
      "@id"        -> `@id`,
      "@timestamp" -> play.api.libs.json.JodaWrites.JodaDateTimeNumberWrites.writes(`@timestamp`),
      "@type"      -> `@type`,
      "@product"   -> _env.eventsName,
      "@serviceId" -> `@serviceId`,
      "@service"   -> `@service`,
      "@env"       -> `@env`,
      "audit"      -> "JobErrorEvent",
      "job"        -> job.auditJson(ctx)(_env),
      "err"        -> err.getMessage
    )
}

object Audit {
  def send[A <: AuditEvent](audit: A)(implicit env: Env): Unit = {
    implicit val ec = env.analyticsExecutionContext
    audit.toAnalytics()
    audit.toEnrichedJson.map(e => env.datastores.auditDataStore.push(e))
  }
}

trait AuditDataStore {
  def count()(implicit ec: ExecutionContext, env: Env): Future[Long]
  def findAllRaw(from: Long = 0, to: Long = 1000)(implicit ec: ExecutionContext, env: Env): Future[Seq[ByteString]]
  def push(event: JsValue)(implicit ec: ExecutionContext, env: Env): Future[Long]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy