Add ability to serialize audit events into json
SshAuditEvent, HttpAuditEvent and ExtendedHttpAudit events can now be
serialized into json.
Audit events will be json serialized to then be persisted into elasticsearch
Feature: Issue 9866
Change-Id: I2b1ed9c80fc4269c40c664cd5edb2d11731492bb
diff --git a/auditlog/src/main/scala/com/gerritforge/analytics/auditlog/model/AuditEvent.scala b/auditlog/src/main/scala/com/gerritforge/analytics/auditlog/model/AuditEvent.scala
index c577e78..0f4a848 100644
--- a/auditlog/src/main/scala/com/gerritforge/analytics/auditlog/model/AuditEvent.scala
+++ b/auditlog/src/main/scala/com/gerritforge/analytics/auditlog/model/AuditEvent.scala
@@ -1,4 +1,5 @@
package com.gerritforge.analytics.auditlog.model
+import org.json4s.jackson.Serialization.write
import org.json4s.native.JsonMethods._
import org.json4s.{DefaultFormats, _}
@@ -6,7 +7,7 @@
object AuditEvent {
- implicit private val formats = DefaultFormats
+ implicit private val formats = DefaultFormats + AuditUUID.serializer + AccountId.serializer
def fromJsonString(json: String): Try[AuditEvent] = {
Try(parse(json)).flatMap { jsValueEvent =>
@@ -21,6 +22,10 @@
}
}
}
+
+ def toJsonString[T <: AuditEvent](auditEvent: T): String = {
+ compact(render(parse(write[T](auditEvent)).snakizeKeys))
+ }
}
sealed trait AuditEvent {
@@ -74,4 +79,22 @@
)
final case class AccountId(id: Int)
-final case class AuditUUID(uuid: String)
\ No newline at end of file
+object AccountId {
+ val serializer = new CustomSerializer[AccountId]( _ =>
+ (
+ { case JObject(JField("id", JInt(id)) :: Nil) => AccountId(id.intValue()) },
+ { case a: AccountId => JInt(a.id) }
+ )
+ )
+}
+
+final case class AuditUUID(uuid: String)
+
+object AuditUUID {
+ val serializer = new CustomSerializer[AuditUUID]( _ =>
+ (
+ { case JObject(JField("uuid", JString(uuid)) :: Nil) => AuditUUID(uuid) },
+ { case a: AuditUUID => JString(a.uuid) }
+ )
+ )
+}
\ No newline at end of file
diff --git a/auditlog/src/test/scala/com/gerritforge/analytics/auditlog/model/AuditEventSpec.scala b/auditlog/src/test/scala/com/gerritforge/analytics/auditlog/model/AuditEventSpec.scala
index a6ec23d..82c9988 100644
--- a/auditlog/src/test/scala/com/gerritforge/analytics/auditlog/model/AuditEventSpec.scala
+++ b/auditlog/src/test/scala/com/gerritforge/analytics/auditlog/model/AuditEventSpec.scala
@@ -1,11 +1,16 @@
package com.gerritforge.analytics.auditlog.model
+import org.json4s.JsonAST.JValue
+import org.json4s.JsonDSL._
import org.json4s.MappingException
import org.json4s.ParserUtil.ParseException
-import org.scalatest.{FlatSpec, Inside, Matchers}
+import org.json4s.native.JsonMethods._
import org.scalatest.TryValues._
+import org.scalatest.{FlatSpec, Inside, Matchers}
class AuditEventSpec extends FlatSpec with Matchers with Inside {
+ behavior of "fromJsonString"
+
"parsing a string that is not json" should "result in a ParseException failure" in {
val someJson = """this_is_not_a_valid_json"""
AuditEvent.fromJsonString(someJson).failure.exception shouldBe a[ParseException]
@@ -209,4 +214,89 @@
gotUUID.uuid shouldBe auditUUID
}
}
+
+ behavior of "toJsonString"
+
+ "an HttpAuditEvent" should "be serializable into json" in {
+
+ val httpMethod = "GET"
+ val httpStatus = 200
+ val sessionId = "someSessionId"
+ val who = CurrentUser(accessPath = "UNKNOWN", accountId = None)
+ val timeAtStart = 1000L
+ val what="https://review.gerrithub.io/Mirantis/tcp-qa/git-upload-pack"
+ val elapsed = 22
+ val uuid = AuditUUID("audit:5f10fea5-35d1-4252-b86f-99db7a9b549b")
+
+ val event = HttpAuditEvent(httpMethod, httpStatus, sessionId, who, timeAtStart, what, elapsed, uuid)
+
+ val expectedJson: JValue =
+ ("http_method" -> httpMethod) ~
+ ("http_status" -> httpStatus) ~
+ ("session_id" -> sessionId) ~
+ ("who" ->
+ ("access_path" -> who.accessPath)
+ ) ~
+ ("time_at_start" -> timeAtStart) ~
+ ("what" -> what) ~
+ ("elapsed" -> elapsed) ~
+ ("uuid" -> uuid.uuid)
+
+ parse(AuditEvent.toJsonString(event)) shouldBe expectedJson
+ }
+
+ "an ExtendedHttpAuditEvent" should "be serializable into json" in {
+
+ val httpMethod = "GET"
+ val httpStatus = 200
+ val sessionId = "someSessionId"
+ val accountId = 123
+ val who = CurrentUser(accessPath = "/config/server/info", accountId = Some(AccountId(accountId)))
+ val timeAtStart = 1000L
+ val what="/config/server/info"
+ val elapsed = 22
+ val uuid = AuditUUID("audit:5f10fea5-35d1-4252-b86f-99db7a9b549b")
+
+ val event = ExtendedHttpAuditEvent(httpMethod, httpStatus, sessionId, who, timeAtStart, what, elapsed, uuid)
+
+ val expectedJson: JValue =
+ ("http_method" -> httpMethod) ~
+ ("http_status" -> httpStatus) ~
+ ("session_id" -> sessionId) ~
+ ("who" ->
+ ("access_path" -> who.accessPath) ~
+ ("account_id" -> accountId)
+ ) ~
+ ("time_at_start" -> timeAtStart) ~
+ ("what" -> what) ~
+ ("elapsed" -> elapsed) ~
+ ("uuid" -> uuid.uuid)
+
+ parse(AuditEvent.toJsonString(event)) shouldBe expectedJson
+ }
+
+ "an SshAuditEvent" should "be serializable into json" in {
+ val sessionId = "2adc5bef"
+ val accountId = 1009124
+ val accessPath = "SSH_COMMAND"
+ val timeAtStart = 1542240322369L
+ val what = "gerrit.stream-events.-s.patchset-created.-s.change-restored.-s.comment-added"
+ val elapsed = 12
+ val auditUUID = "audit:dd74e098-9260-4720-9143-38a0a0a5e500"
+
+ val event = SshAuditEvent(sessionId, Some(CurrentUser(accessPath, Some(AccountId(accountId)))), timeAtStart, what, elapsed, AuditUUID(auditUUID))
+
+ val expectedJson: JValue =
+ ("session_id" -> sessionId) ~
+ ("who" ->
+ ("access_path" -> accessPath) ~
+ ("account_id" -> accountId)
+ ) ~
+ ("time_at_start" -> timeAtStart) ~
+ ("what" -> what) ~
+ ("elapsed" -> elapsed) ~
+ ("uuid" -> auditUUID)
+
+ parse(AuditEvent.toJsonString(event)) shouldBe expectedJson
+ }
}
\ No newline at end of file