@@ -45,23 +45,23 @@ private[ror] object AuditSerializationHelper {
4545 }
4646
4747 def serialize (responseContext : AuditResponseContext ,
48- fields : Map [AuditFieldName , AuditFieldValueDescriptor ],
48+ fields : Map [AuditFieldPath , AuditFieldValueDescriptor ],
4949 allowedEventMode : AllowedEventMode ): Option [JSONObject ] = {
5050 responseContext match {
5151 case Allowed (requestContext, verbosity, reason) =>
5252 allowedEvent(
5353 allowedEventMode,
5454 verbosity,
55- createEntry(fields, EventData (matched = true , " ALLOWED " , reason, responseContext.duration, requestContext, None ))
55+ createEntry(fields, EventData (matched = true , FinalState . Allowed , reason, responseContext.duration, requestContext, None ))
5656 )
5757 case ForbiddenBy (requestContext, _, reason) =>
58- Some (createEntry(fields, EventData (matched = true , " FORBIDDEN " , reason, responseContext.duration, requestContext, None )))
58+ Some (createEntry(fields, EventData (matched = true , FinalState . Forbidden , reason, responseContext.duration, requestContext, None )))
5959 case Forbidden (requestContext) =>
60- Some (createEntry(fields, EventData (matched = false , " FORBIDDEN " , " default" , responseContext.duration, requestContext, None )))
60+ Some (createEntry(fields, EventData (matched = false , FinalState . Forbidden , " default" , responseContext.duration, requestContext, None )))
6161 case RequestedIndexNotExist (requestContext) =>
62- Some (createEntry(fields, EventData (matched = false , " INDEX NOT EXIST " , " Requested index doesn't exist" , responseContext.duration, requestContext, None )))
62+ Some (createEntry(fields, EventData (matched = false , FinalState . IndexNotExist , " Requested index doesn't exist" , responseContext.duration, requestContext, None )))
6363 case Errored (requestContext, cause) =>
64- Some (createEntry(fields, EventData (matched = false , " ERRORED " , " error" , responseContext.duration, requestContext, Some (cause))))
64+ Some (createEntry(fields, EventData (matched = false , FinalState . Errored , " error" , responseContext.duration, requestContext, Some (cause))))
6565 }
6666 }
6767
@@ -76,23 +76,48 @@ private[ror] object AuditSerializationHelper {
7676 }
7777 }
7878
79- private def createEntry (fields : Map [AuditFieldName , AuditFieldValueDescriptor ],
79+ private def createEntry (fields : Map [AuditFieldPath , AuditFieldValueDescriptor ],
8080 eventData : EventData ) = {
8181 val resolveAuditFieldValue = resolver(eventData)
82- val resolvedFields : Map [String , Any ] =
83- Map (" @timestamp" -> timestampFormatter.format(eventData.requestContext.timestamp)) ++
84- fields.map { case (name, valueDescriptor) => name.value -> resolveAuditFieldValue(valueDescriptor) }
82+ val resolvedFields : Map [AuditFieldPath , Any ] =
83+ Map (AuditFieldPath ( " @timestamp" ) -> timestampFormatter.format(eventData.requestContext.timestamp)) ++
84+ fields.map { case (name, valueDescriptor) => name -> resolveAuditFieldValue(valueDescriptor) }
8585
86- resolvedFields
87- .foldLeft(new JSONObject ()) { case (soFar, (key, value)) => soFar.put(key, value) }
88- .mergeWith(eventData.requestContext.generalAuditEvents)
86+ resolvedFields.foldLeft(new JSONObject ()) { case (soFar, (path, value)) =>
87+ putNested(soFar, path.path.toList, value)
88+ }.mergeWith(eventData.requestContext.generalAuditEvents)
89+ }
90+
91+ private def putNested (json : JSONObject , path : List [String ], value : Any ): JSONObject = {
92+ path match {
93+ case Nil =>
94+ json
95+ case key :: Nil =>
96+ json.put(key, value)
97+ json
98+ case key :: tail =>
99+ val child = Option (json.optJSONObject(key)).getOrElse(new JSONObject ())
100+ json.put(key, putNested(child, tail, value))
101+ json
102+ }
89103 }
90104
91105 private def resolver (eventData : EventData ): AuditFieldValueDescriptor => Any = auditValue => {
92106 val requestContext = eventData.requestContext
93107 auditValue match {
94108 case AuditFieldValueDescriptor .IsMatched => eventData.matched
95- case AuditFieldValueDescriptor .FinalState => eventData.finalState
109+ case AuditFieldValueDescriptor .FinalState => eventData.finalState match {
110+ case FinalState .Allowed => " ALLOWED"
111+ case FinalState .Forbidden => " FORBIDDEN"
112+ case FinalState .Errored => " ERRORED"
113+ case FinalState .IndexNotExist => " INDEX NOT EXIST"
114+ }
115+ case AuditFieldValueDescriptor .EcsEventOutcome => eventData.finalState match {
116+ case FinalState .Allowed => " success"
117+ case FinalState .Forbidden => " failure"
118+ case FinalState .Errored => " unknown"
119+ case FinalState .IndexNotExist => " unknown"
120+ }
96121 case AuditFieldValueDescriptor .Reason => eventData.reason
97122 case AuditFieldValueDescriptor .User => SerializeUser .serialize(requestContext).orNull
98123 case AuditFieldValueDescriptor .LoggedUser => requestContext.loggedInUserName.orNull
@@ -102,6 +127,7 @@ private[ror] object AuditSerializationHelper {
102127 case AuditFieldValueDescriptor .InvolvedIndices => if (requestContext.involvesIndices) requestContext.indices.toList.asJava else List .empty.asJava
103128 case AuditFieldValueDescriptor .AclHistory => requestContext.history
104129 case AuditFieldValueDescriptor .ProcessingDurationMillis => eventData.duration.toMillis
130+ case AuditFieldValueDescriptor .ProcessingDurationNanos => eventData.duration.toNanos
105131 case AuditFieldValueDescriptor .Timestamp => timestampFormatter.format(requestContext.timestamp)
106132 case AuditFieldValueDescriptor .Id => requestContext.id
107133 case AuditFieldValueDescriptor .CorrelationId => requestContext.correlationId
@@ -121,6 +147,8 @@ private[ror] object AuditSerializationHelper {
121147 case AuditFieldValueDescriptor .EsNodeName => eventData.requestContext.auditEnvironmentContext.esNodeName
122148 case AuditFieldValueDescriptor .EsClusterName => eventData.requestContext.auditEnvironmentContext.esClusterName
123149 case AuditFieldValueDescriptor .StaticText (text) => text
150+ case AuditFieldValueDescriptor .NumericValue (value) => value.bigDecimal
151+ case AuditFieldValueDescriptor .BooleanValue (value) => value
124152 case AuditFieldValueDescriptor .Combined (values) => values.map(resolver(eventData)).mkString
125153 }
126154 }
@@ -141,12 +169,24 @@ private[ror] object AuditSerializationHelper {
141169 }
142170
143171 private final case class EventData (matched : Boolean ,
144- finalState : String ,
172+ finalState : FinalState ,
145173 reason : String ,
146174 duration : FiniteDuration ,
147175 requestContext : AuditRequestContext ,
148176 error : Option [Throwable ])
149177
178+ private sealed trait FinalState
179+
180+ private object FinalState {
181+ case object Allowed extends FinalState
182+
183+ case object Forbidden extends FinalState
184+
185+ case object Errored extends FinalState
186+
187+ case object IndexNotExist extends FinalState
188+ }
189+
150190 sealed trait AllowedEventMode
151191
152192 object AllowedEventMode {
@@ -155,7 +195,15 @@ private[ror] object AuditSerializationHelper {
155195 final case class Include (types : Set [Verbosity ]) extends AllowedEventMode
156196 }
157197
158- final case class AuditFieldName (value : String )
198+ final case class AuditFieldPath private (path : List [String ])
199+
200+ object AuditFieldPath {
201+ def apply (name : String ): AuditFieldPath =
202+ AuditFieldPath (List (name))
203+
204+ def apply (head : String , tail : List [String ]): AuditFieldPath =
205+ AuditFieldPath (head :: tail)
206+ }
159207
160208 sealed trait AuditFieldValueDescriptor
161209
@@ -166,6 +214,8 @@ private[ror] object AuditSerializationHelper {
166214
167215 case object FinalState extends AuditFieldValueDescriptor
168216
217+ case object EcsEventOutcome extends AuditFieldValueDescriptor
218+
169219 case object Reason extends AuditFieldValueDescriptor
170220
171221 @ deprecated(" [ROR] The User audit field value descriptor should not be used. Use LoggedUser or PresentedIdentity instead" , " 1.68.0" )
@@ -185,6 +235,8 @@ private[ror] object AuditSerializationHelper {
185235
186236 case object ProcessingDurationMillis extends AuditFieldValueDescriptor
187237
238+ case object ProcessingDurationNanos extends AuditFieldValueDescriptor
239+
188240 // Identifiers
189241 case object Timestamp extends AuditFieldValueDescriptor
190242
@@ -230,6 +282,10 @@ private[ror] object AuditSerializationHelper {
230282
231283 final case class StaticText (value : String ) extends AuditFieldValueDescriptor
232284
285+ final case class BooleanValue (value : Boolean ) extends AuditFieldValueDescriptor
286+
287+ final case class NumericValue (value : BigDecimal ) extends AuditFieldValueDescriptor
288+
233289 final case class Combined (values : List [AuditFieldValueDescriptor ]) extends AuditFieldValueDescriptor
234290
235291 }
@@ -244,42 +300,42 @@ private[ror] object AuditSerializationHelper {
244300 case object FullRequestContentFields extends AuditFieldGroup
245301 }
246302
247- private val commonFields : Map [AuditFieldName , AuditFieldValueDescriptor ] = Map (
248- AuditFieldName (" match" ) -> AuditFieldValueDescriptor .IsMatched ,
249- AuditFieldName (" block" ) -> AuditFieldValueDescriptor .Reason ,
250- AuditFieldName (" id" ) -> AuditFieldValueDescriptor .Id ,
251- AuditFieldName (" final_state" ) -> AuditFieldValueDescriptor .FinalState ,
252- AuditFieldName (" @timestamp" ) -> AuditFieldValueDescriptor .Timestamp ,
253- AuditFieldName (" correlation_id" ) -> AuditFieldValueDescriptor .CorrelationId ,
254- AuditFieldName (" processingMillis" ) -> AuditFieldValueDescriptor .ProcessingDurationMillis ,
255- AuditFieldName (" error_type" ) -> AuditFieldValueDescriptor .ErrorType ,
256- AuditFieldName (" error_message" ) -> AuditFieldValueDescriptor .ErrorMessage ,
257- AuditFieldName (" content_len" ) -> AuditFieldValueDescriptor .ContentLengthInBytes ,
258- AuditFieldName (" content_len_kb" ) -> AuditFieldValueDescriptor .ContentLengthInKb ,
259- AuditFieldName (" type" ) -> AuditFieldValueDescriptor .Type ,
260- AuditFieldName (" origin" ) -> AuditFieldValueDescriptor .RemoteAddress ,
261- AuditFieldName (" destination" ) -> AuditFieldValueDescriptor .LocalAddress ,
262- AuditFieldName (" xff" ) -> AuditFieldValueDescriptor .XForwardedForHttpHeader ,
263- AuditFieldName (" task_id" ) -> AuditFieldValueDescriptor .TaskId ,
264- AuditFieldName (" req_method" ) -> AuditFieldValueDescriptor .HttpMethod ,
265- AuditFieldName (" headers" ) -> AuditFieldValueDescriptor .HttpHeaderNames ,
266- AuditFieldName (" path" ) -> AuditFieldValueDescriptor .HttpPath ,
267- AuditFieldName (" user" ) -> AuditFieldValueDescriptor .User ,
268- AuditFieldName (" logged_user" ) -> AuditFieldValueDescriptor .LoggedUser ,
269- AuditFieldName (" presented_identity" ) -> AuditFieldValueDescriptor .PresentedIdentity ,
270- AuditFieldName (" impersonated_by" ) -> AuditFieldValueDescriptor .ImpersonatedByUser ,
271- AuditFieldName (" action" ) -> AuditFieldValueDescriptor .Action ,
272- AuditFieldName (" indices" ) -> AuditFieldValueDescriptor .InvolvedIndices ,
273- AuditFieldName (" acl_history" ) -> AuditFieldValueDescriptor .AclHistory
303+ private val commonFields : Map [AuditFieldPath , AuditFieldValueDescriptor ] = Map (
304+ AuditFieldPath (" match" ) -> AuditFieldValueDescriptor .IsMatched ,
305+ AuditFieldPath (" block" ) -> AuditFieldValueDescriptor .Reason ,
306+ AuditFieldPath (" id" ) -> AuditFieldValueDescriptor .Id ,
307+ AuditFieldPath (" final_state" ) -> AuditFieldValueDescriptor .FinalState ,
308+ AuditFieldPath (" @timestamp" ) -> AuditFieldValueDescriptor .Timestamp ,
309+ AuditFieldPath (" correlation_id" ) -> AuditFieldValueDescriptor .CorrelationId ,
310+ AuditFieldPath (" processingMillis" ) -> AuditFieldValueDescriptor .ProcessingDurationMillis ,
311+ AuditFieldPath (" error_type" ) -> AuditFieldValueDescriptor .ErrorType ,
312+ AuditFieldPath (" error_message" ) -> AuditFieldValueDescriptor .ErrorMessage ,
313+ AuditFieldPath (" content_len" ) -> AuditFieldValueDescriptor .ContentLengthInBytes ,
314+ AuditFieldPath (" content_len_kb" ) -> AuditFieldValueDescriptor .ContentLengthInKb ,
315+ AuditFieldPath (" type" ) -> AuditFieldValueDescriptor .Type ,
316+ AuditFieldPath (" origin" ) -> AuditFieldValueDescriptor .RemoteAddress ,
317+ AuditFieldPath (" destination" ) -> AuditFieldValueDescriptor .LocalAddress ,
318+ AuditFieldPath (" xff" ) -> AuditFieldValueDescriptor .XForwardedForHttpHeader ,
319+ AuditFieldPath (" task_id" ) -> AuditFieldValueDescriptor .TaskId ,
320+ AuditFieldPath (" req_method" ) -> AuditFieldValueDescriptor .HttpMethod ,
321+ AuditFieldPath (" headers" ) -> AuditFieldValueDescriptor .HttpHeaderNames ,
322+ AuditFieldPath (" path" ) -> AuditFieldValueDescriptor .HttpPath ,
323+ AuditFieldPath (" user" ) -> AuditFieldValueDescriptor .User ,
324+ AuditFieldPath (" logged_user" ) -> AuditFieldValueDescriptor .LoggedUser ,
325+ AuditFieldPath (" presented_identity" ) -> AuditFieldValueDescriptor .PresentedIdentity ,
326+ AuditFieldPath (" impersonated_by" ) -> AuditFieldValueDescriptor .ImpersonatedByUser ,
327+ AuditFieldPath (" action" ) -> AuditFieldValueDescriptor .Action ,
328+ AuditFieldPath (" indices" ) -> AuditFieldValueDescriptor .InvolvedIndices ,
329+ AuditFieldPath (" acl_history" ) -> AuditFieldValueDescriptor .AclHistory
274330 )
275331
276- private val esEnvironmentFields : Map [AuditFieldName , AuditFieldValueDescriptor ] = Map (
277- AuditFieldName (" es_node_name" ) -> AuditFieldValueDescriptor .EsNodeName ,
278- AuditFieldName (" es_cluster_name" ) -> AuditFieldValueDescriptor .EsClusterName
332+ private val esEnvironmentFields : Map [AuditFieldPath , AuditFieldValueDescriptor ] = Map (
333+ AuditFieldPath (" es_node_name" ) -> AuditFieldValueDescriptor .EsNodeName ,
334+ AuditFieldPath (" es_cluster_name" ) -> AuditFieldValueDescriptor .EsClusterName
279335 )
280336
281- private val requestContentFields : Map [AuditFieldName , AuditFieldValueDescriptor ] = Map (
282- AuditFieldName (" content" ) -> AuditFieldValueDescriptor .Content
337+ private val requestContentFields : Map [AuditFieldPath , AuditFieldValueDescriptor ] = Map (
338+ AuditFieldPath (" content" ) -> AuditFieldValueDescriptor .Content
283339 )
284340
285341}
0 commit comments