Skip to content

Conversation

@mgoworko
Copy link
Collaborator

@mgoworko mgoworko commented Nov 13, 2025

🚀Added separate JWT authentication and authorization rules

Summary by CodeRabbit

  • New Features

    • JWT now supports separate authentication and authorization configs, plus a pseudo-authorization mode when no groups logic is provided.
    • JWT processing attaches token payload as metadata and can opt out of external auth calls.
    • New audit log output added for auditing.
  • Refactor

    • JWT auth refactored to compose authentication + authorization for more flexible wiring and decoding.
  • Tests

    • Expanded JWT authentication/authorization test coverage.

✏️ Tip: You can customize this high-level summary in your review settings.


// Pseudo-authorization rule should be used exclusively as part of the JwtAuthRule, when there are is no groups logic defined.
// It preserves the kbn_auth rule behavior from before introducing separate authn and authz rules.
final class JwtPseudoAuthorizationRule(val settings: Settings)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic from JwtAuthRule is now extracted into 3 parts:

  • the JwtAuthenticationRule
  • the JwtAuthorizationRule
  • and this JwtPseudoAuthorizationRule

The JwtAuthRule until now handled the situation, when there was no groups rule defined in a very specific way. It was even more complicated than the RorKbnAuth rule, which we refactored recently.

When groups rule is not defined, then the "authorization" part checks, if the current, preferred group is eligible. When the preferred group is not defined, then it is simply accepted and passes.

But this "pseduo-authorization" is different in other ways too - it does not add available groups to BlockContext's user metadata.

So if we would want to change this behavior, it would change the output BlockContext after the rule matches. We can either:

  • preserve this pseudo-authorization logic (the current implementation extracted 3 parts of logic from JwtAuthRule, but did not change the behavior)
  • remove pseudo-authorization and change the behavior

But as I wrote - this "pseudo-authorization" is even more complex, than the one for RorKbnAuth was. It does not add groups to context and additionally it accepts the situation, when there are no groups in JWT token - something that was not allowed in RorKbnAuth.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, let's think about it a little bit. I'd like to avoid the JwtPesudoAuthorizationRule. But first, let's analyse all the cases. I think we can find them in this code:


  private def handleGroupsClaimSearchResult[B <: BlockContext : BlockContextUpdater](blockContext: B,
                                                                                     result: Option[ClaimSearchResult[UniqueList[Group]]]) = {
    (result, settings.permittedGroups) match {
      // 1. authorization rule - groups are defined, but no groups cannot be extracted from JWT, because of not configured groups claim (FORBIDDEN - correct in context of authorization rule)
      case (None, Groups.Defined(_)) =>
        Left(())
      // 2. authentication rule - no groups definition in authentication rule; we don't have to look at groups from JWT (in this case, no groups claim name was configured, so we don't even know how to extract them) (ALLOWED - correct in context of authentication rule)
      case (None, Groups.NotDefined) =>
        Right(blockContext)
      // 3. authorization rule - groups are defined; there is a configured groups claim name, but no groups are extracted (FORBIDDEN - correct in context of authorization rule)
      case (Some(NotFound), Groups.Defined(_)) =>
        Left(())
      // 4. authorization rule - groups are not defined (we can assume it's allow all - "*"); there is a configured groups claim name, and groups are not extracted (should be FORBIDDEN - it looks it's bug. We had a similar use case in the context of the ror_kbn_auth, don't we?)
      case (Some(NotFound), Groups.NotDefined) =>
        Right(blockContext) 
      // 5. authorization rule - groups are defined; there is a configured groups claim name, and groups are extracted from JWT (FORBIDDEN/ALLOWED - result based on the groups logic)
      case (Some(Found(groups)), Groups.Defined(groupsLogic)) =>
        UniqueNonEmptyList.from(groups) match {
          case Some(nonEmptyGroups) =>
            groupsLogic.availableGroupsFrom(nonEmptyGroups) match {
              case Some(matchedGroups) =>
                checkIfCanContinueWithGroups(blockContext, UniqueList.from(matchedGroups))
                  .map(_.withUserMetadata(_.addAvailableGroups(matchedGroups)))
              case None =>
                Left(())
            }
          case None =>
            Left(())
        }
      // 6. authorization rule - groups are not defined (we can assume it's allow all - "*"); there is a configured groups claim name, and groups are extracted from JWT (FORBIDDEN/ALLOWED - result based on current group eligibility)
      case (Some(Found(groups)), Groups.NotDefined) =>
        checkIfCanContinueWithGroups(blockContext, groups)
    }
  }

I've added comments.
Could you please compare each case with the current ror_kbn_auth... rules logic?

I see that 2. and 4. points are problematic. Do you agree?
If so, let's focus on them and compare with:

  1. current ror_kbn_auth... implementation
  2. old ror_kbn_auth... implementation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually from my point of view, points 2 and 6 are most problematic, but let me summarize as I understand it:

The pseudo-authorization rule (the authorization part of old auth rule when no groups) is points 2,4,6 - all with Groups.NotDefined

  • point 2.
    • we did not have a similar case in ROR KBN rules, because we did not allow parsing token with missing groups
    • in jwt_auth rule we allowed situation, where there was a token with missing groups claim
    • it is now part of "pseudo-authorization" - because we don't want such behavior in "real-authorization" rule
  • point 4.
    • yes, exactly, that is the case similar to the one in the ROR KBN auth rule
    • yes, we changed the behavior of the ROR KBN auth rule (and the new authz rule) to not allow it
  • point 6.
    • it does the pseudo - authorization when there are groups in JWT, but no groups rule
    • but it is not equivalent to allow all - "*" - it does not modify the user metadata, so the result is different
    • probably we can change that, but it is an incompatible change in behavior

So the changes in the jwt_auth rule will be (if we remove the "pseudo-authorization" and use only the newly created "real-authorization"):

  • from point 2. - ROR will no longer accept JWT tokens without groups claim
    • we did not have similar case in ROR KBN, because we did not allow tokens without groups claim, but for JWT it looks like a feature to me
    • but it is similar to the point below, so I guess we can change that (with breaking change in behavior)
  • from point 4. - ROR will no longer accept siaution with not found groups claims in token
    • we fixed it in the same way for ROR KBN (with breaking change in behavior) so I guess we can do the same here
  • from point 6. - the real pseudo-authorization
    • we can use allow all - "*"
    • bu the resulting context (user metadata) will be different that before - it will start to contain user groups (they were not added to context in old implementation)

There are 14 failing tests when I switch from the pseudo to real authorization. Most of them fail, because before this change the result was ALLOWED, and now is FORBIDDEN, because of the breaking changes from those 3 points.

@mgoworko mgoworko marked this pull request as ready for review November 15, 2025 15:24
@coderabbitai

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as resolved.

@mgoworko mgoworko requested a review from coutoPL November 17, 2025 21:15
coderabbitai[bot]

This comment was marked as resolved.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's rename the file to JwtAuthRulesDecoders

extends RuleBaseDecoderWithoutAssociatedFields[JwtAuthRule] {
extends RuleBaseDecoderWithoutAssociatedFields[JwtAuthRule] with Logging {

override protected def decoder: Decoder[RuleDefinition[JwtAuthRule]] = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make JwtAuthRuleDecoder and RorKbnAuthRuleDecoder much more similar to each other (also consider changing the JwtAuthRuleDecoder if it would be better to achieve the goal)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even more than that - I created a common decoder implementation for those 2 families of rules (trait JwtLikeRulesDecoders)

import java.security.KeyPairGenerator
import java.util.Base64

class JwtAuthorizationRuleSettingsTests
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it should have similar tests to the RorKbnAuthorizationRuleSettingsTests suite. Shouldn't it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reorganized the tests. Now the ROR KBN and JWT suites are very similar. In both those cases I created those suites by copying, adjusting and taking only those tests from auth-rule suite, that were applicable to authn/z rules. And the JWT-specific tests (like testing signatures, algorithms etc.) are now only in the authn suites, not duplicated for authz and auth suites.

extends BaseRuleSettingsDecoderTest[JwtAuthenticationRule]
with MockFactory {

"A JwtAuthenticationRule" should {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same here. It seems that, at least, a subset of the tests should be similar to the RorKbnAuthenticationRuleSettingsTests suite's tests. Can you verify it?
It would be nice to see the difference at first glance.

Maybe we could have a different section for settings in the defs (because I guess, there are many differences /in RorKbnAuth...Rule, the def schema is pretty strict and not so configurable)

|
| - name: jwt1
| group_ids_claim: groups
| group_names_claim: group_names
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is suspicious.

We use the jwt_authentication rule, but we define group_names_claim. Isn't it kind of a misconfiguration? In the context of authentication, the groups should not be used. But you (as a user) can declare them. It looks like we should catch them.

Maybe JwtDef with None should not be valid in the context of the JwtAuthenticationRule? Should we distinguish it in types?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, in the current implementation, it is not a misconfiguration. The group_names_claim is a property of JWT service definition, and not of the authentication rule. But it is completely unused in this context and in this test.

In current implementation it is correct, because the same jwt1 definition may be used for multiple authn/authz/auth rules.

I added this issue to the thread about the compatibility of auth rule and the pseudo-authorization rule.


// Pseudo-authorization rule should be used exclusively as part of the JwtAuthRule, when there are is no groups logic defined.
// It preserves the kbn_auth rule behavior from before introducing separate authn and authz rules.
final class JwtPseudoAuthorizationRule(val settings: Settings)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, let's think about it a little bit. I'd like to avoid the JwtPesudoAuthorizationRule. But first, let's analyse all the cases. I think we can find them in this code:


  private def handleGroupsClaimSearchResult[B <: BlockContext : BlockContextUpdater](blockContext: B,
                                                                                     result: Option[ClaimSearchResult[UniqueList[Group]]]) = {
    (result, settings.permittedGroups) match {
      // 1. authorization rule - groups are defined, but no groups cannot be extracted from JWT, because of not configured groups claim (FORBIDDEN - correct in context of authorization rule)
      case (None, Groups.Defined(_)) =>
        Left(())
      // 2. authentication rule - no groups definition in authentication rule; we don't have to look at groups from JWT (in this case, no groups claim name was configured, so we don't even know how to extract them) (ALLOWED - correct in context of authentication rule)
      case (None, Groups.NotDefined) =>
        Right(blockContext)
      // 3. authorization rule - groups are defined; there is a configured groups claim name, but no groups are extracted (FORBIDDEN - correct in context of authorization rule)
      case (Some(NotFound), Groups.Defined(_)) =>
        Left(())
      // 4. authorization rule - groups are not defined (we can assume it's allow all - "*"); there is a configured groups claim name, and groups are not extracted (should be FORBIDDEN - it looks it's bug. We had a similar use case in the context of the ror_kbn_auth, don't we?)
      case (Some(NotFound), Groups.NotDefined) =>
        Right(blockContext) 
      // 5. authorization rule - groups are defined; there is a configured groups claim name, and groups are extracted from JWT (FORBIDDEN/ALLOWED - result based on the groups logic)
      case (Some(Found(groups)), Groups.Defined(groupsLogic)) =>
        UniqueNonEmptyList.from(groups) match {
          case Some(nonEmptyGroups) =>
            groupsLogic.availableGroupsFrom(nonEmptyGroups) match {
              case Some(matchedGroups) =>
                checkIfCanContinueWithGroups(blockContext, UniqueList.from(matchedGroups))
                  .map(_.withUserMetadata(_.addAvailableGroups(matchedGroups)))
              case None =>
                Left(())
            }
          case None =>
            Left(())
        }
      // 6. authorization rule - groups are not defined (we can assume it's allow all - "*"); there is a configured groups claim name, and groups are extracted from JWT (FORBIDDEN/ALLOWED - result based on current group eligibility)
      case (Some(Found(groups)), Groups.NotDefined) =>
        checkIfCanContinueWithGroups(blockContext, groups)
    }
  }

I've added comments.
Could you please compare each case with the current ror_kbn_auth... rules logic?

I see that 2. and 4. points are problematic. Do you agree?
If so, let's focus on them and compare with:

  1. current ror_kbn_auth... implementation
  2. old ror_kbn_auth... implementation

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthenticationRuleSettingsTests.scala (1)

125-154: Group claims in authentication context – addressed by existing comment.

The past review correctly identified that testing group_ids_claim / group_names_claim in a pure authentication context is semantically questionable. The current approach loads the shared JwtDef which may contain these claims, but authentication rules don't use them. This is an acceptable tradeoff for definition reuse.

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthorizationRuleSettingsTests.scala (1)

37-39: New test suite structure looks good – addressed by existing review comment.

The past review noted this should have similar tests to RorKbnAuthorizationRuleSettingsTests. This is a valid observation for ensuring parity.

🧹 Nitpick comments (6)
core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthRuleSettingsTests.scala (1)

295-301: Unused envVarsProvider override.

This provider returns RSA keys for SECRET_RSA, but no tests in this file exercise environment-variable-based key loading. Consider removing or adding relevant tests.

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthorizationRuleSettingsTests.scala (1)

268-274: Unused envVarsProvider override.

Same as in RorKbnAuthRuleSettingsTests - this provider isn't exercised by any test in this file.

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthorizationRuleSettingsTests.scala (3)

243-266: Ambiguous test name – conflicts with earlier test at line 113.

Line 113 has "no JWT definition name is defined in rule setting" and line 243 has "no JWT definition name is defined". While they test different scenarios (rule vs definition level), the naming is confusing.

-      "no JWT definition name is defined" in {
+      "no name attribute in JWT definition" in {

267-298: Duplicate test case – overlaps with lines 209-242.

Both test cases validate "both 'groups or' key and 'groups and' key used" with nearly identical assertions. Consider removing this duplicate.


328-334: Unused envVarsProvider override.

No tests in this file use RSA/EC signature algorithms that require the SECRET_RSA environment variable. Consider removing or adding relevant tests.

tests-utils/src/main/resources/log4j2_es_7.10_and_newer.properties (1)

18-146: Double‑check root logger wiring for readonlyrest_audit.log and potential console duplication

Overall the Log4j2 config looks coherent and aligned with ES 7.10+ patterns (console, rolling main log, deprecation, slowlogs, header warnings, ROR audit, etc.). Two points are worth explicitly confirming:

  1. Root logger → ror_audit_rolling (lines 101‑107)
    With rootLogger.appenderRef.ror_audit_router.ref=ror_audit_rolling, every log reaching the root logger (i.e. effectively all categories except those with additivity=false) will be written into readonlyrest_audit.log, not only the readonlyrest_audit logger.
    If the intent is for readonlyrest_audit.log to contain only ROR audit entries, consider removing the root logger reference and relying solely on logger.ror_audit:

    -rootLogger.appenderRef.ror_audit_router.type=AppenderRef
    -rootLogger.appenderRef.ror_audit_router.ref=ror_audit_rolling

    (Or keep it as‑is if you really want a catch‑all audit log.)

  2. Possible double console logging for org.elasticsearch.action (lines 111‑117 vs 101‑105)
    logger.action sends to console with additivity=true, and the root logger also sends to console. That can result in duplicated console lines for org.elasticsearch.action logs. If you see duplicates in practice, you may want to set logger.action.additivity=false or drop its explicit console appender and rely on root.

These are configuration semantics rather than hard bugs, but worth validating against the intended logging behavior and test expectations.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 07470fc and a54878d.

📒 Files selected for processing (15)
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/definitions/DefinitionsPack.scala (2 hunks)
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/ruleDecoders.scala (1 hunks)
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRuleDecoder.scala (0 hunks)
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRulesDecoders.scala (1 hunks)
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtLikeRulesDecoders.scala (1 hunks)
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/RorKbnRulesDecoders.scala (1 hunks)
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthRuleSettingsTests.scala (19 hunks)
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthenticationRuleSettingsTests.scala (1 hunks)
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthorizationRuleSettingsTests.scala (1 hunks)
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthRuleSettingsTests.scala (2 hunks)
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthenticationRuleSettingsTests.scala (3 hunks)
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthorizationRuleSettingsTests.scala (2 hunks)
  • integration-tests/src/test/resources/ror_audit/enabled_auditing_tools/readonlyrest.yml (1 hunks)
  • integration-tests/src/test/scala/tech/beshu/ror/integration/suites/audit/LocalClusterAuditingToolsSuite.scala (1 hunks)
  • tests-utils/src/main/resources/log4j2_es_7.10_and_newer.properties (4 hunks)
💤 Files with no reviewable changes (1)
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRuleDecoder.scala
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: mgoworko
Repo: sscarduzio/elasticsearch-readonlyrest-plugin PR: 1163
File: core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/variables/runtime/VariableContext.scala:96-98
Timestamp: 2025-10-03T21:07:20.002Z
Learning: In the ROR KBN authentication refactoring (PR #1163), the VariableUsage implicit for rorKbnAuthRule uses a union type `VariableUsage[RorKbnAuthRule | RorKbnAuthenticationRule]` because the decoder explicitly uses this union type to represent the fallback behavior where `ror_kbn_auth` without groups falls back to `RorKbnAuthenticationRule`. A separate `VariableUsage[RorKbnAuthRule]` implicit is not needed since there are no direct usages of that specific type - all usage goes through the union type in the decoder.
📚 Learning: 2025-10-03T21:07:20.002Z
Learnt from: mgoworko
Repo: sscarduzio/elasticsearch-readonlyrest-plugin PR: 1163
File: core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/variables/runtime/VariableContext.scala:96-98
Timestamp: 2025-10-03T21:07:20.002Z
Learning: In the ROR KBN authentication refactoring (PR #1163), the VariableUsage implicit for rorKbnAuthRule uses a union type `VariableUsage[RorKbnAuthRule | RorKbnAuthenticationRule]` because the decoder explicitly uses this union type to represent the fallback behavior where `ror_kbn_auth` without groups falls back to `RorKbnAuthenticationRule`. A separate `VariableUsage[RorKbnAuthRule]` implicit is not needed since there are no direct usages of that specific type - all usage goes through the union type in the decoder.

Applied to files:

  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthenticationRuleSettingsTests.scala
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthenticationRuleSettingsTests.scala
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthorizationRuleSettingsTests.scala
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthRuleSettingsTests.scala
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtLikeRulesDecoders.scala
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthRuleSettingsTests.scala
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRulesDecoders.scala
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/RorKbnRulesDecoders.scala
  • core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/ruleDecoders.scala
📚 Learning: 2025-09-21T14:09:30.387Z
Learnt from: mgoworko
Repo: sscarduzio/elasticsearch-readonlyrest-plugin PR: 1163
File: core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/rules/auth/base/RorKbnRuleHelper.scala:168-172
Timestamp: 2025-09-21T14:09:30.387Z
Learning: RorKbnDef.SignatureCheckMethod sealed trait has only three cases: Hmac(key: Array[Byte]), Rsa(pubKey: PublicKey), and Ec(pubKey: PublicKey). There is no NoCheck case.

Applied to files:

  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthenticationRuleSettingsTests.scala
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthenticationRuleSettingsTests.scala
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthRuleSettingsTests.scala
  • core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthRuleSettingsTests.scala
📚 Learning: 2025-11-19T16:33:39.709Z
Learnt from: mateuszkp96
Repo: sscarduzio/elasticsearch-readonlyrest-plugin PR: 1186
File: integration-tests/src/test/scala/tech/beshu/ror/integration/suites/audit/LocalClusterAuditingToolsSuite.scala:193-193
Timestamp: 2025-11-19T16:33:39.709Z
Learning: In LocalClusterAuditingToolsSuite, when truncating audit data via adminAuditManagers.values.foreach(_.head.truncate()), calling truncate on only the head manager is sufficient because all AuditIndexManager instances in each NonEmptyList point to the same cluster's audit index. Since the truncation affects the underlying index, there's no need to iterate all managers in the list.

Applied to files:

  • integration-tests/src/test/scala/tech/beshu/ror/integration/suites/audit/LocalClusterAuditingToolsSuite.scala
🧬 Code graph analysis (6)
core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthenticationRuleSettingsTests.scala (3)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/definitions/JwtDef.scala (1)
  • SignatureCheckMethod (41-46)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/HttpClientsFactory.scala (2)
  • HttpClientsFactory (49-81)
  • HttpClient (52-65)
core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthorizationRuleSettingsTests.scala (1)
  • envVarsProvider (328-334)
core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthorizationRuleSettingsTests.scala (1)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/RawRorConfigBasedCoreFactory.scala (3)
  • CoreCreationError (475-504)
  • DefinitionsLevelCreationError (478-478)
  • RulesLevelCreationError (480-480)
core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthRuleSettingsTests.scala (8)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/definitions/ExternalAuthenticationService.scala (1)
  • definitions (126-126)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/definitions/JwtDef.scala (4)
  • JwtDef (27-36)
  • JwtDef (37-51)
  • GroupsConfig (48-48)
  • SignatureCheckMethod (41-46)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/rules/auth/JwtPseudoAuthorizationRule.scala (3)
  • rules (41-45)
  • JwtPseudoAuthorizationRule (34-60)
  • JwtPseudoAuthorizationRule (62-64)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/rules/auth/JwtAuthorizationRule.scala (4)
  • rules (39-48)
  • JwtAuthorizationRule (32-65)
  • JwtAuthorizationRule (67-74)
  • Name (69-71)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/rules/auth/JwtAuthRule.scala (3)
  • JwtAuthRule (25-36)
  • JwtAuthRule (38-42)
  • Name (39-41)
core/src/main/scala/tech/beshu/ror/accesscontrol/domain/userAndGroups.scala (10)
  • GroupIdLike (80-121)
  • GroupId (81-82)
  • GroupId (84-86)
  • GroupsLogic (154-261)
  • GroupIds (123-125)
  • GroupIds (127-150)
  • from (69-69)
  • from (75-75)
  • from (100-104)
  • from (129-131)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/definitions/ldap/LdapService.scala (2)
  • Name (35-35)
  • Name (36-38)
core/src/main/scala/tech/beshu/ror/accesscontrol/domain/http.scala (1)
  • AuthorizationTokenDef (276-277)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtLikeRulesDecoders.scala (6)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/common.scala (1)
  • common (53-368)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/definitions/DefinitionsPack.scala (2)
  • Definitions (34-34)
  • Definitions (35-41)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/RuleBaseDecoder.scala (2)
  • RuleBaseDecoder (84-109)
  • RuleBaseDecoderWithoutAssociatedFields (86-94)
core/src/main/scala/tech/beshu/ror/accesscontrol/utils/CirceOps.scala (2)
  • CirceOps (42-419)
  • toSyncDecoder (279-279)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRulesDecoders.scala (7)
  • humanReadableName (34-34)
  • createAuthenticationRule (36-39)
  • createAuthorizationRule (39-42)
  • createAuthRule (45-48)
  • createAuthorizationRuleWithoutGroups (42-45)
  • createAuthRuleWithoutGroups (48-51)
  • serializeDefinitionId (51-54)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/RorKbnRulesDecoders.scala (7)
  • humanReadableName (37-37)
  • createAuthenticationRule (39-42)
  • createAuthorizationRule (42-45)
  • createAuthRule (48-51)
  • createAuthorizationRuleWithoutGroups (45-48)
  • createAuthRuleWithoutGroups (51-54)
  • serializeDefinitionId (54-57)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/RorKbnRulesDecoders.scala (4)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRulesDecoders.scala (7)
  • humanReadableName (34-34)
  • createAuthenticationRule (36-39)
  • createAuthorizationRule (39-42)
  • createAuthorizationRuleWithoutGroups (42-45)
  • createAuthRule (45-48)
  • createAuthRuleWithoutGroups (48-51)
  • serializeDefinitionId (51-54)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/rules/auth/RorKbnAuthenticationRule.scala (3)
  • RorKbnAuthenticationRule (33-76)
  • RorKbnAuthenticationRule (78-85)
  • Settings (84-84)
core/src/main/scala/tech/beshu/ror/accesscontrol/blocks/rules/auth/RorKbnAuthorizationRule.scala (3)
  • Settings (73-73)
  • RorKbnAuthorizationRule (32-65)
  • RorKbnAuthorizationRule (67-74)
core/src/main/scala/tech/beshu/ror/accesscontrol/domain/userAndGroups.scala (2)
  • GroupsLogic (154-261)
  • AnyOf (172-172)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/ruleDecoders.scala (2)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRulesDecoders.scala (1)
  • JwtAuthRulesDecoders (25-54)
core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/RorKbnRulesDecoders.scala (1)
  • RorKbnRulesDecoders (28-57)
🔇 Additional comments (20)
integration-tests/src/test/resources/ror_audit/enabled_auditing_tools/readonlyrest.yml (1)

18-22: Clarify the purpose of the new log-based audit output.

The new ECS serializer-based log output appears syntactically correct and is positioned consistently with other audit outputs. However, the relationship to the JWT authentication/authorization rule separation (PR objective) isn't immediately clear.

Is this log output specifically needed to support testing of the new JWT rules, or is it test infrastructure unrelated to the core JWT separation work? If it's required for the JWT tests, consider adding an inline comment explaining the dependency.

core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/definitions/DefinitionsPack.scala (1)

34-34: LGTM! Type parameter refinement improves type safety.

The upper bound ITEM <: Item allows callers to preserve the specific item type while ensuring it conforms to Item. This aligns well with the typed definitions used throughout DefinitionsPack.

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthenticationRuleSettingsTests.scala (2)

85-86: Good test organization.

Splitting tests into general config loading and token-related scenarios improves readability.

Also applies to: 397-398


364-366: Error message correctly references the authorization rule.

The updated message "ROR Kibana authorization rule 'kbn1'" aligns with the new separation of authentication and authorization concerns.

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthRuleSettingsTests.scala (1)

62-72: Composed rule access pattern looks correct.

The tests properly validate both authentication and authorization components of RorKbnAuthRule, reflecting the new separation.

Also applies to: 96-106

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/RorKbnAuthorizationRuleSettingsTests.scala (1)

51-53: Correct test setup for authorization rule.

Including auth_key_sha1 before ror_kbn_authorization is appropriate since authorization rules require prior authentication.

Also applies to: 84-86

core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/ruleDecoders.scala (2)

134-139: JWT decoder wiring is correct.

Routing to JwtAuthRulesDecoders.AuthRuleDecoder, AuthenticationRuleDecoder, and AuthorizationRuleDecoder properly delegates to the new consolidated decoder framework.


144-149: ROR KBN decoder wiring mirrors JWT pattern.

Consistent with JwtAuthRulesDecoders, the ROR KBN rules now use RorKbnRulesDecoders for authentication, authorization, and combined auth rules.

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthRuleSettingsTests.scala (2)

65-71: LGTM – assertions properly verify the new composed rule structure.

The tests correctly access rule.authentication.settings.jwt and verify the authorization rule type. The asInstanceOf usage is acceptable here for test assertions.


128-130: Consistent pattern for groups logic verification.

Properly asserts JwtAuthorizationRule with expected GroupsLogic.AnyOf configuration.

core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthenticationRuleSettingsTests.scala (2)

39-41: New test suite for standalone JWT authentication rule – LGTM.

Comprehensive coverage for the new JwtAuthenticationRule decoder including signature algorithms and external validators.


620-626: Environment variable provider pattern is consistent across test suites.

Matches the pattern used in other JWT test files.

core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtAuthRulesDecoders.scala (1)

25-54: Clean factory implementation for JWT rules.

The decoder properly implements JwtLikeRulesDecoders with correct rule construction. Factory methods are well-structured and consistent.

core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/RorKbnRulesDecoders.scala (2)

28-35: Design note: RorKbn uses same type for with/without groups.

Unlike JwtAuthRulesDecoders which uses distinct JwtPseudoAuthorizationRule, RorKbn reuses RorKbnAuthorizationRule for both cases. This is consistent with ROR Kibana's requirement that group validation always occurs, using a wildcard pattern "*" as the permissive default.


45-46: Wildcard group pattern for "without groups" scenario – LGTM.

Using GroupIdPattern.fromNes(nes("*")) effectively permits any group, matching the expected behavior when groups aren't explicitly configured.

core/src/main/scala/tech/beshu/ror/accesscontrol/factory/decoders/rules/auth/JwtLikeRulesDecoders.scala (5)

40-48: Clean abstraction for shared JWT/ROR-KBN decoding logic.

The generic trait with context bounds properly captures the common decoding pattern. The asymmetry in AUTHZ_WITHOUT_GROUPS_RULE (lacking the additional context bounds) is appropriate since it's only used internally for the deprecated fallback path.


68-87: LGTM!

Authentication decoder correctly rejects superfluous groups settings and provides actionable error messages pointing users to AUTHZ_RULE or AUTH_RULE alternatives.


89-107: LGTM!

Authorization decoder correctly enforces that groups logic is mandatory and provides a documentation link in the error message.


109-140: Good backward compatibility handling.

The deprecation warning for missing groups logic is well-crafted—it explains the implicit fallback behavior (groups_any_of: ["*"]), provides documentation, and suggests the alternative (AUTHN_RULE). This matches the pattern from the ROR KBN refactoring per prior learnings.


142-175: LGTM!

The helper decoders properly handle the three cases: simple string form, extended object form with groups logic, and the multiple-groups-defined error case. The ignored error in GroupsLogicNotDefined is intentional—each parent decoder handles the None case appropriately for its semantics.

}
)
}
"RSA algorithm is defined but on signature key" in {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo in test description.

"RSA algorithm is defined but on signature key" → should be "no signature key".

-      "RSA algorithm is defined but on signature key" in {
+      "RSA algorithm is defined but no signature key" in {
🤖 Prompt for AI Agents
In
core/src/test/scala/tech/beshu/ror/unit/acl/factory/decoders/rules/auth/JwtAuthenticationRuleSettingsTests.scala
around line 495, the test description contains a typo: replace the string "RSA
algorithm is defined but on signature key" with "RSA algorithm is defined but no
signature key" so the message correctly reads "no signature key".

This reverts commit 6ff6868.
@mgoworko mgoworko requested a review from coutoPL November 29, 2025 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants