Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ private[ror] object AuditSerializationHelper {
case AuditFieldValueDescriptor.FinalState => eventData.finalState
case AuditFieldValueDescriptor.Reason => eventData.reason
case AuditFieldValueDescriptor.User => SerializeUser.serialize(requestContext).orNull
case AuditFieldValueDescriptor.LoggedUser => requestContext.loggedInUserName.orNull
case AuditFieldValueDescriptor.PresentedIdentity => requestContext.attemptedUserName.orNull
case AuditFieldValueDescriptor.ImpersonatedByUser => requestContext.impersonatedByUserName.orNull
case AuditFieldValueDescriptor.Action => requestContext.action
case AuditFieldValueDescriptor.InvolvedIndices => if (requestContext.involvesIndices) requestContext.indices.toList.asJava else List.empty.asJava
Expand Down Expand Up @@ -166,8 +168,13 @@ private[ror] object AuditSerializationHelper {

case object Reason extends AuditFieldValueDescriptor

@deprecated("[ROR] The User audit field value descriptor should not be used. Use LoggedUser or PresentedIdentity instead", "1.68.0")
case object User extends AuditFieldValueDescriptor

case object LoggedUser extends AuditFieldValueDescriptor

case object PresentedIdentity extends AuditFieldValueDescriptor

case object ImpersonatedByUser extends AuditFieldValueDescriptor

case object Action extends AuditFieldValueDescriptor
Expand Down Expand Up @@ -258,6 +265,8 @@ private[ror] object AuditSerializationHelper {
AuditFieldName("headers") -> AuditFieldValueDescriptor.HttpHeaderNames,
AuditFieldName("path") -> AuditFieldValueDescriptor.HttpPath,
AuditFieldName("user") -> AuditFieldValueDescriptor.User,
AuditFieldName("logged_user") -> AuditFieldValueDescriptor.LoggedUser,
AuditFieldName("presented_identity") -> AuditFieldValueDescriptor.PresentedIdentity,
AuditFieldName("impersonated_by") -> AuditFieldValueDescriptor.ImpersonatedByUser,
AuditFieldName("action") -> AuditFieldValueDescriptor.Action,
AuditFieldName("indices") -> AuditFieldValueDescriptor.InvolvedIndices,
Expand Down
18 changes: 17 additions & 1 deletion audit/src/test/scala/tech/beshu/ror/audit/SerializerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -80,6 +82,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -110,6 +114,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -138,6 +144,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -168,6 +176,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -197,6 +207,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -226,6 +238,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -255,6 +269,8 @@ class SerializerTest extends AnyWordSpec {
"headers" -> "array",
"path" -> "string",
"user" -> "string",
"logged_user" -> "string",
"presented_identity" -> "string",
"action" -> "string",
"indices" -> "array",
"acl_history" -> "string",
Expand Down Expand Up @@ -331,7 +347,7 @@ private object DummyAuditRequestContext extends AuditRequestContext {

override def involvesIndices: Boolean = false

override def attemptedUserName: Option[String] = None
override def attemptedUserName: Option[String] = Some("basic_auth_username")

override def rawAuthHeader: Option[String] = None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ package tech.beshu.ror.accesscontrol.audit.configurable

import cats.parse.{Parser0, Parser as P}
import cats.syntax.list.*
import org.apache.logging.log4j.scala.Logging
import tech.beshu.ror.audit.utils.AuditSerializationHelper.AuditFieldValueDescriptor

object AuditFieldValueDescriptorParser {
object AuditFieldValueDescriptorParser extends Logging {

private val lbrace = P.char('{')
private val rbrace = P.char('}')
Expand Down Expand Up @@ -59,7 +60,16 @@ object AuditFieldValueDescriptorParser {
case "IS_MATCHED" => Some(AuditFieldValueDescriptor.IsMatched)
case "FINAL_STATE" => Some(AuditFieldValueDescriptor.FinalState)
case "REASON" => Some(AuditFieldValueDescriptor.Reason)
case "USER" => Some(AuditFieldValueDescriptor.User)
case "USER" =>
logger.warn(
"""The USER audit value placeholder is deprecated and should not be used in the configurable audit log serializer.
|Please use LOGGED_USER or PRESENTED_IDENTITY instead. Check the list of available placeholders in the documentation:
|https://docs.readonlyrest.com/elasticsearch/audit#using-configurable-serializer.
|""".stripMargin
)
Some(AuditFieldValueDescriptor.User)
case "LOGGED_USER" => Some(AuditFieldValueDescriptor.LoggedUser)
case "PRESENTED_IDENTITY" => Some(AuditFieldValueDescriptor.PresentedIdentity)
case "IMPERSONATED_BY_USER" => Some(AuditFieldValueDescriptor.ImpersonatedByUser)
case "ACTION" => Some(AuditFieldValueDescriptor.Action)
case "INVOLVED_INDICES" => Some(AuditFieldValueDescriptor.InvolvedIndices)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ class AuditSettingsTests extends AnyWordSpec with Inside {
expectedAuditCluster = LocalAuditCluster
)
}
"custom serializer is set" in {
"QueryAuditLogSerializer serializer is set" in {
val config = rorConfigFromUnsafe(
"""
|readonlyrest:
Expand All @@ -538,6 +538,72 @@ class AuditSettingsTests extends AnyWordSpec with Inside {
expectedAuditCluster = LocalAuditCluster
)
}
"QueryAuditLogSerializer serializer is set and correctly serializes event without logged user" in {
val config = rorConfigFromUnsafe(
"""
|readonlyrest:
| audit:
| enabled: true
| outputs:
| - type: index
| serializer: "tech.beshu.ror.audit.instances.QueryAuditLogSerializer"
|
| access_control_rules:
|
| - name: test_block
| type: allow
| auth_key: admin:container
|
""".stripMargin)

assertIndexBasedAuditSinkSettingsPresent[QueryAuditLogSerializer](
config,
expectedIndexName = "readonlyrest_audit-2018-12-31",
expectedAuditCluster = LocalAuditCluster
)
val createdSerializer = serializer(config)
val serializedResponse = createdSerializer.onResponse(
AuditResponseContext.Forbidden(new DummyAuditRequestContext(loggedInUserName = None, attemptedUserName = None))
)

serializedResponse shouldBe defined
serializedResponse.get.get("user") shouldBe "Bearer 123"
serializedResponse.get.isNull("presented_identity")
serializedResponse.get.isNull("logged_user")
}
"QueryAuditLogSerializer serializer is set and correctly serializes event with logged user" in {
val config = rorConfigFromUnsafe(
"""
|readonlyrest:
| audit:
| enabled: true
| outputs:
| - type: index
| serializer: "tech.beshu.ror.audit.instances.QueryAuditLogSerializer"
|
| access_control_rules:
|
| - name: test_block
| type: allow
| auth_key: admin:container
|
""".stripMargin)

assertIndexBasedAuditSinkSettingsPresent[QueryAuditLogSerializer](
config,
expectedIndexName = "readonlyrest_audit-2018-12-31",
expectedAuditCluster = LocalAuditCluster
)
val createdSerializer = serializer(config)
val serializedResponse = createdSerializer.onResponse(
AuditResponseContext.Forbidden(new DummyAuditRequestContext(loggedInUserName = Some("my_user")))
)

serializedResponse shouldBe defined
serializedResponse.get.get("user") shouldBe "my_user"
serializedResponse.get.get("presented_identity") shouldBe "basic auth user"
serializedResponse.get.get("logged_user") shouldBe "my_user"
}
"custom environment-aware serializer is set and correctly serializes events" in {
val config = rorConfigFromUnsafe(
"""
Expand All @@ -562,7 +628,7 @@ class AuditSettingsTests extends AnyWordSpec with Inside {
expectedAuditCluster = LocalAuditCluster
)
val createdSerializer = serializer(config)
val serializedResponse = createdSerializer.onResponse(AuditResponseContext.Forbidden(DummyAuditRequestContext))
val serializedResponse = createdSerializer.onResponse(AuditResponseContext.Forbidden(new DummyAuditRequestContext))

serializedResponse shouldBe defined
serializedResponse.get.get("custom_field_for_es_node_name") shouldBe "testEsNode"
Expand Down Expand Up @@ -841,13 +907,13 @@ class AuditSettingsTests extends AnyWordSpec with Inside {
"ES version is greater than or equal 7.9.0" in {
val esVersions =
List(
EsVersion(8,17,0),
EsVersion(8,1,0),
EsVersion(8,0,0),
EsVersion(7,17,27),
EsVersion(7,10,0),
EsVersion(7,9,1),
EsVersion(7,9,0),
EsVersion(8, 17, 0),
EsVersion(8, 1, 0),
EsVersion(8, 0, 0),
EsVersion(7, 17, 27),
EsVersion(7, 10, 0),
EsVersion(7, 9, 1),
EsVersion(7, 9, 0),
)

val config = rorConfigFromUnsafe(
Expand Down Expand Up @@ -1969,7 +2035,8 @@ private class TestEnvironmentAwareAuditLogSerializer extends EnvironmentAwareAud

}

private object DummyAuditRequestContext extends AuditRequestContext {
private class DummyAuditRequestContext(override val loggedInUserName: Option[String] = Some("logged_user"),
override val attemptedUserName: Option[String] = Some("basic auth user")) extends AuditRequestContext {
override def timestamp: Instant = Instant.now()

override def id: String = ""
Expand Down Expand Up @@ -2002,15 +2069,11 @@ private object DummyAuditRequestContext extends AuditRequestContext {

override def httpMethod: String = ""

override def loggedInUserName: Option[String] = Some("logged_user")

override def impersonatedByUserName: Option[String] = None

override def involvesIndices: Boolean = false

override def attemptedUserName: Option[String] = None

override def rawAuthHeader: Option[String] = None
override def rawAuthHeader: Option[String] = Some("Bearer 123")

override def generalAuditEvents: JSONObject = new JSONObject

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,17 @@ class LocalClusterAuditingToolsSuite
auditEntries.exists(entry =>
entry("final_state").str == "ALLOWED" &&
entry("user").str == "username" &&
entry("logged_user").str == "username" &&
entry("presented_identity").str == "username" &&
entry("block").str.contains("name: 'Rule 1'") &&
entry.obj.get("es_node_name").isEmpty &&
entry.obj.get("es_cluster_name").isEmpty
) shouldBe true
auditEntries.exists(entry =>
entry("final_state").str == "ALLOWED" &&
entry("user").str == "username" &&
entry("logged_user").str == "username" &&
entry("presented_identity").str == "username" &&
entry("block").str.contains("name: 'Rule 1'") &&
Try(entry("es_node_name")).map(_.str) == Success("ROR_SINGLE_1") &&
Try(entry("es_cluster_name")).map(_.str) == Success("ROR_SINGLE")
Expand All @@ -109,6 +113,8 @@ class LocalClusterAuditingToolsSuite
auditEntries.exists(entry =>
entry("final_state").str == "ALLOWED" &&
entry("user").str == "username" &&
entry("logged_user").str == "username" &&
entry("presented_identity").str == "username" &&
entry("block").str.contains("name: 'Rule 1'") &&
Try(entry("es_node_name")).map(_.str) == Success("ROR_SINGLE_1") &&
Try(entry("es_cluster_name")).map(_.str) == Success("ROR_SINGLE") &&
Expand Down Expand Up @@ -136,6 +142,8 @@ class LocalClusterAuditingToolsSuite
auditEntries.exists(entry =>
entry("final_state").str == "ALLOWED" &&
entry("user").str == "username" &&
entry("logged_user").str == "username" &&
entry("presented_identity").str == "username" &&
entry("block").str.contains("name: 'Rule 1'") &&
Try(entry("es_node_name")).map(_.str) == Success("ROR_SINGLE_1") &&
Try(entry("es_cluster_name")).map(_.str) == Success("ROR_SINGLE") &&
Expand Down