- 
                Notifications
    You must be signed in to change notification settings 
- Fork 164
[RORDEV-1567] ECS serializer and improved configurable serializer #1165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 6 commits
e8dc296
              d7f70e0
              b3c3ba8
              2b6454e
              ff5f100
              1c5f7a4
              944662b
              f940792
              a57e942
              59e4797
              c0ca654
              aa15975
              51ed19d
              96304a7
              08f8c99
              3f63b3b
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||
| * This file is part of ReadonlyREST. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * ReadonlyREST is free software: you can redistribute it and/or modify | ||||||||||||||||||||||||||||||
| * it under the terms of the GNU General Public License as published by | ||||||||||||||||||||||||||||||
| * the Free Software Foundation, either version 3 of the License, or | ||||||||||||||||||||||||||||||
| * (at your option) any later version. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * ReadonlyREST is distributed in the hope that it will be useful, | ||||||||||||||||||||||||||||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||||||||||||||||||||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||||||||||||||||||||||||||
| * GNU General Public License for more details. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * You should have received a copy of the GNU General Public License | ||||||||||||||||||||||||||||||
| * along with ReadonlyREST. If not, see http://www.gnu.org/licenses/ | ||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| package tech.beshu.ror.accesscontrol.audit.ecs | ||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||
| import org.json.JSONObject | ||||||||||||||||||||||||||||||
| import tech.beshu.ror.accesscontrol.audit.ecs.EcsV1AuditLogSerializer.fields | ||||||||||||||||||||||||||||||
| import tech.beshu.ror.audit.utils.AuditSerializationHelper | ||||||||||||||||||||||||||||||
| import tech.beshu.ror.audit.utils.AuditSerializationHelper.{AllowedEventMode, AuditFieldName, AuditFieldValueDescriptor} | ||||||||||||||||||||||||||||||
| import tech.beshu.ror.audit.{AuditLogSerializer, AuditResponseContext} | ||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||
| class EcsV1AuditLogSerializer(val allowedEventMode: AllowedEventMode) extends AuditLogSerializer { | ||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||
| override def onResponse(responseContext: AuditResponseContext): Option[JSONObject] = { | ||||||||||||||||||||||||||||||
| AuditSerializationHelper.serialize(responseContext, fields, allowedEventMode) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||
| object EcsV1AuditLogSerializer { | ||||||||||||||||||||||||||||||
| private val fields: Map[AuditFieldName, AuditFieldValueDescriptor] = Map( | ||||||||||||||||||||||||||||||
|         
                  coutoPL marked this conversation as resolved.
              Show resolved
            Hide resolved | ||||||||||||||||||||||||||||||
| AuditFieldName("@timestamp") -> AuditFieldValueDescriptor.Timestamp, | ||||||||||||||||||||||||||||||
| AuditFieldName("trace") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("id") -> AuditFieldValueDescriptor.CorrelationId, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| AuditFieldName("url") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("path") -> AuditFieldValueDescriptor.HttpPath, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| AuditFieldName("source") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("address") -> AuditFieldValueDescriptor.RemoteAddress, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| AuditFieldName("destination") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("address") -> AuditFieldValueDescriptor.LocalAddress, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| AuditFieldName("http") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("request") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("method") -> AuditFieldValueDescriptor.HttpMethod, | ||||||||||||||||||||||||||||||
| AuditFieldName("body") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("content") -> AuditFieldValueDescriptor.Content, | ||||||||||||||||||||||||||||||
| AuditFieldName("bytes") -> AuditFieldValueDescriptor.ContentLengthInBytes, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| AuditFieldName("user") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("name") -> AuditFieldValueDescriptor.User, | ||||||||||||||||||||||||||||||
| AuditFieldName("effective") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("name") -> AuditFieldValueDescriptor.ImpersonatedByUser, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| AuditFieldName("event") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("id") -> AuditFieldValueDescriptor.Id, | ||||||||||||||||||||||||||||||
| AuditFieldName("action") -> AuditFieldValueDescriptor.Action, | ||||||||||||||||||||||||||||||
| AuditFieldName("type") -> AuditFieldValueDescriptor.Type, | ||||||||||||||||||||||||||||||
| AuditFieldName("reason") -> AuditFieldValueDescriptor.FinalState, | ||||||||||||||||||||||||||||||
| AuditFieldName("duration") -> AuditFieldValueDescriptor.ProcessingDurationNanos, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      67
     to 
      73
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix  
 Apply this diff: -      AuditFieldName("reason") -> AuditFieldValueDescriptor.FinalState,
+      AuditFieldName("reason") -> AuditFieldValueDescriptor.Reason,📝 Committable suggestion
 
        Suggested change
       
 🤖 Prompt for AI AgentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I refactored the  The ROR "reason" is now one of custom ECS labels: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query: 💡 Result: Summary definitions (ECS v1.6 semantics are the same as current ECS event docs): 
 Sources: 
 @mgoworko, I appreciate the refactor to ECS 1.6, but I have concerns about the semantic alignment with the ECS specification: Issues with current mappings: 
 Recommended mapping: AuditFieldName("event") -> AuditFieldValueDescriptor.Nested(
  AuditFieldName("id") -> AuditFieldValueDescriptor.Id,
  AuditFieldName("dataset") -> AuditFieldValueDescriptor.StaticText("ror.audit"),
  AuditFieldName("action") -> AuditFieldValueDescriptor.Action,
  AuditFieldName("reason") -> AuditFieldValueDescriptor.Reason,
  AuditFieldName("outcome") -> AuditFieldValueDescriptor.FinalState, // Map ALLOWED/FORBIDDEN to success/failure
  AuditFieldName("duration") -> AuditFieldValueDescriptor.ProcessingDurationNanos,
),
AuditFieldName("labels") -> AuditFieldValueDescriptor.Nested(
  AuditFieldName("request_type") -> AuditFieldValueDescriptor.Type,
  // ... other labels
),This aligns better with ECS semantics where event.reason explains "why," event.outcome captures the result, and event.dataset identifies the log source. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mgoworko is it ok now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 | ||||||||||||||||||||||||||||||
| AuditFieldName("error") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("type") -> AuditFieldValueDescriptor.ErrorType, | ||||||||||||||||||||||||||||||
| AuditFieldName("message") -> AuditFieldValueDescriptor.ErrorMessage, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| AuditFieldName("labels") -> AuditFieldValueDescriptor.Nested( | ||||||||||||||||||||||||||||||
| AuditFieldName("es_cluster_name") -> AuditFieldValueDescriptor.EsClusterName, | ||||||||||||||||||||||||||||||
| AuditFieldName("es_node_name") -> AuditFieldValueDescriptor.EsNodeName, | ||||||||||||||||||||||||||||||
| AuditFieldName("es_task_id") -> AuditFieldValueDescriptor.TaskId, | ||||||||||||||||||||||||||||||
| AuditFieldName("involved_indices") -> AuditFieldValueDescriptor.InvolvedIndices, | ||||||||||||||||||||||||||||||
| AuditFieldName("acl_history") -> AuditFieldValueDescriptor.AclHistory, | ||||||||||||||||||||||||||||||
| AuditFieldName("x_forwarded_for") -> AuditFieldValueDescriptor.XForwardedForHttpHeader, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's one level of nesting. Let's do it in a more generic way (to allow many nesting levels).
BTW. We define fields like that:
So, maybe we should not add Nested as a descriptor, but maybe we should modify the
AuditFieldNamedefinition instead.E.g.
AuditFieldNameis:AuditFieldNames (or strings)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, I see I was wrong about the nesting.
Nevertheless, let's consider the change above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually that was my first version of implementation - the field name as list of strings, representing nesting. I have it in git history, so I can do it with no problems if we so decide. But I honestly think that the current version is better, because:
Example from tests:
With the approach based on the list of strings, it would look like that:
I think that the nested approach is more readable in the code too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I agree. I see it as follows:
And the only thing that is JSON here is dthe ocument.
The internal representation is not a JSON, and the "fields" representation in ROR's YAML is not a JSON either (we don't support arrays).
But I don't want you to change the YAML's representation. It's fine. But obivously we should detect invalid settings like:
So, when we take into consideration the limitations, we could say that we have a map of simplified field path keys (without arrays) and value descriptor leafs.
In code, we represent it as
Map[AuditFieldName, AuditFieldValueDescriptor], whereAuditFieldValueDescriptoris always a leaf (of type string, int, bool ... maybe null?!)AuditFieldNameis a path to this leaf (e.g.,custom_section.double_nested.double_nested_nextornode_name_with_static_suffix)That's why I propose to define
AuditFieldName(in the domain ... maybe we should rename it to AuditFieldPath) like this:type AuditFieldPath = NonEmptyList[String].And
AuditFieldValueDescriptorwill be free of sth likeNested.WDYT?