Skip to content

OF-3268 & OF-3273: Refactor SASL EXTERNAL S2S success path#3311

Open
guusdk wants to merge 7 commits into
igniterealtime:mainfrom
guusdk:OF-3268_SaslAuth-redundancy
Open

OF-3268 & OF-3273: Refactor SASL EXTERNAL S2S success path#3311
guusdk wants to merge 7 commits into
igniterealtime:mainfrom
guusdk:OF-3268_SaslAuth-redundancy

Conversation

@guusdk
Copy link
Copy Markdown
Member

@guusdk guusdk commented May 6, 2026

Remove redundant inbound S2S post-auth verification from SASLAuthentication.handle(...) after saslServer.isComplete().

For SASL EXTERNAL, certificate/domain verification is already performed by ExternalServerSaslServer.evaluateResponse(...), while authentication method assignment is already handled in authenticationSuccessful(...).

This change simplifies control flow and clarifies responsibility without intended behavior change.

Additionally, this refactors `SASLAuthentication to no longer assumes that any (successful) authentication for S2S is SASL External. Instead, the method that was used is explicitly checked.

Some additional refactoring was added to clean up the code and introduce unit test coverage.

Summary by CodeRabbit

  • New Features

    • Session-aware SASL mechanism discovery; mechanism list may be suppressed (null) when none apply. EXTERNAL advertised only when TLS + trusted peer certificate conditions are met.
  • Bug Fixes

    • Authentication now records the negotiated SASL mechanism and validated domain for inbound sessions; mechanism requests are rejected if not eligible for the session.
  • Tests

    • Added unit tests covering discovery, eligibility, negotiation flows, and post-authentication state.

…checks

Remove redundant inbound S2S post-auth verification from `SASLAuthentication.handle(...)` after `saslServer.isComplete()`.

For SASL EXTERNAL, certificate/domain verification is already performed by `ExternalServerSaslServer.evaluateResponse(...)`, while authentication method assignment is already handled in `authenticationSuccessful(...)`.

This change simplifies control flow and clarifies responsibility without intended behavior change.
@guusdk guusdk requested review from Fishbowler, Copilot and dwd May 6, 2026 19:29
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6095d2a-4086-4bc7-bb35-e752799938aa

📥 Commits

Reviewing files that changed from the base of the PR and between feb0a7f and 223ef59.

📒 Files selected for processing (1)
  • xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java

📝 Walkthrough

Walkthrough

Centralizes SASL mechanism eligibility selection, enforces session-specific mechanism availability during handle(), delegates mechanism-specific verification to the SaslServer on completion, maps EXTERNAL to SASL_EXTERNAL via a ServerSession helper, and adds unit tests for eligibility and completion behaviors.

Changes

SASL Mechanisms & Completion Refactor

Layer / File(s) Summary
Imports / annotations
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Added/adjusted imports to support @Nonnull and @VisibleForTesting.
SASL mechanism advertisement
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Refactored getSASLMechanismsElement(...) to build the <mechanisms> element from a session-specific available-mechanisms set and return null when suppression is configured and none are available.
Handle-time enforcement
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Added an eligibility check in handle() to reject requested SASL mechanisms that are not available for the session.
SASL completion: delegate verification
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Removed inline inbound-server certificate verification in completion path; success finalization now passes the SASL mechanism name to a centralized finalizer, delegating mechanism-specific verification to the SaslServer.
authenticationSuccessful update
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Made authenticationSuccessful(...) visible for testing and accept the mechanism name; inbound server sessions now record the validated domain and set authentication method via fromSaslMechanismName(mechanismName).
Available-mechanisms selector
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Added getAvailableMechanismsForSession(...) to centralize selection based on session type.
Client-side rules
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Added getAvailableMechanismsForClientSession(ClientSession) computing EXTERNAL eligibility guarded by encryption and peer-certificate trust/revalidation.
Server-side rules
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
Added getAvailableMechanismsForServerSession(LocalIncomingServerSession) computing server-side eligible mechanisms (EXTERNAL only when peer certificate is trusted).
ServerSession enum helper
xmppserver/src/main/java/org/jivesoftware/openfire/session/ServerSession.java
Added AuthenticationMethod.fromSaslMechanismName(String) mapping "EXTERNAL" (case-insensitive) to SASL_EXTERNAL, otherwise OTHER; updated header years.
Tests
xmppserver/src/test/java/org/jivesoftware/openfire/net/SASLAuthenticationTest.java, xmppserver/src/test/java/org/jivesoftware/openfire/session/ServerSessionTest.java
Added JUnit 5 tests covering mechanism eligibility, handle-time rejections, negotiation behavior, successful authentication outcomes, available-mechanisms helpers, validated-domain recording, and the fromSaslMechanismName mapping.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

backport 5.0

Suggested reviewers

  • akrherz

Poem

🐰
I nibbled code beneath the moonlight,
Moved checks where they belong tonight,
SASL sings a tidier song,
Mechanisms sorted, tests hop along,
A carrot for clean, green bytes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically references the main refactoring work (SASL EXTERNAL S2S success path) covered in the PR, capturing the primary change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the SASL S2S success path by removing an inbound server-side post-authentication certificate/domain verification block from SASLAuthentication.handle(...), relying instead on the SASL mechanism implementation (notably EXTERNAL) to perform any required verification.

Changes:

  • Removed redundant inbound S2S certificate verification and authentication-method assignment from the saslServer.isComplete() success path.
  • Added an explanatory comment clarifying that mechanism-specific verification is expected to happen inside the SaslServer implementation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

guusdk added 3 commits May 8, 2026 16:37
…thentication

Ensure SASL mechanism selection in `SASLAuthentication.handle(...)` is constrained to the mechanisms available for the current session, matching stream feature advertisement behavior.

Previously, Openfire validated mechanisms against globally enabled configuration only, which allowed peers to attempt mechanisms that were not advertised for a specific connection/session type. This change adds a session-scoped eligibility check and rejects non-available mechanisms with `invalid-mechanism`. This aligns mechanism acceptance with negotiated capabilities and prevents use of mechanisms outside per-session policy.
Stop assuming inbound s2s SASL authentication always uses EXTERNAL. When SASL succeeds, derive `ServerSession.AuthenticationMethod` from the actual negotiated mechanism name instead:

- EXTERNAL -> SASL_EXTERNAL
- any other mechanism -> OTHER

This makes session state reflect the real authentication method used and removes brittle coupling to current mechanism availability.
…sions

Replace Element-based mechanism lookups with direct set operations. Eliminates redundant dom4j manipulation.

Adds unit test coverage

None of this is expected to introduce functional changes. Pre-existing public method signatures are left intact for backwards compatibility.
@guusdk guusdk changed the title OF-3268: Refactor SASL EXTERNAL S2S success path to remove duplicate checks OF-3268 & OF-3273: Refactor SASL EXTERNAL S2S success path to remove duplicate checks May 8, 2026
@guusdk guusdk requested a review from Copilot May 8, 2026 15:41
@guusdk guusdk changed the title OF-3268 & OF-3273: Refactor SASL EXTERNAL S2S success path to remove duplicate checks OF-3268 & OF-3273: Refactor SASL EXTERNAL S2S success path May 8, 2026
Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java`:
- Around line 831-846: getAvailableMechanismsForServerSession currently adds
"EXTERNAL" based only on TLS/certificate checks and can advertise a mechanism
that getSupportedMechanisms() (and configuration sasl.mechs) may have disabled;
update getAvailableMechanismsForServerSession to check the global supported
mechanism set (call getSupportedMechanisms() or otherwise consult sasl.mechs)
and only add "EXTERNAL" if it is present there as well. Also add a regression
test that disables EXTERNAL in sasl.mechs, ensures stream features do NOT
advertise EXTERNAL for an encrypted server session with a trusted cert, and
verifies the server rejects EXTERNAL when disabled. Ensure references to
getAvailableMechanismsForServerSession and getSupportedMechanisms() are used so
the gate is clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: da8150bc-a595-470f-889d-3a87b1f7c73f

📥 Commits

Reviewing files that changed from the base of the PR and between 655e743 and d492349.

📒 Files selected for processing (4)
  • xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/session/ServerSession.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/net/SASLAuthenticationTest.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/session/ServerSessionTest.java
✅ Files skipped from review due to trivial changes (1)
  • xmppserver/src/main/java/org/jivesoftware/openfire/session/ServerSession.java

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java Outdated
Comment thread xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java Outdated
guusdk added 2 commits May 8, 2026 17:50
Only include EXTERNAL in available server-session mechanisms when it is enabled/supported, preventing it from being advertised in stream features when disabled in sasl.mechs.
Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java (1)

162-189: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Publish SASL mechanism refreshes atomically.

This listener now rebuilds mechanisms from a dispatcher thread, but initMechanisms() publishes a new mutable HashSet before it is fully populated. Readers in handle() / getSupportedMechanisms() can observe an empty or partial set immediately after a sasl.mechs change, which can transiently hide advertised mechanisms or reject valid auth attempts. Please switch this to a copy-on-write snapshot (or otherwise synchronize all reads/writes) before wiring it to async property callbacks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java`
around lines 162 - 189, The PropertyEventListener currently calls
initMechanisms() from a dispatcher thread which publishes a mutable HashSet into
the shared mechanisms reference while it is being populated; change
initMechanisms() to build the new set locally (e.g., a temporary HashSet), fully
populate it, then atomically replace the shared reference with an
unmodifiable/copy-on-write snapshot so readers in handle() and
getSupportedMechanisms() never see a partially-built set; alternatively make
mechanisms a volatile/AtomicReference<Set<String>> and swap the completed set,
or synchronize all reads/writes to mechanisms, ensuring the listener uses the
atomic replace approach before wiring into PropertyEventDispatcher.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java`:
- Around line 439-444: authenticationSuccessful(...) may short-circuit to
authenticationFailed(...) so the current code erroneously clears SaslServer,
records SaslMechanism and returns Status.authenticated even when authentication
actually failed; update authenticationSuccessful (in SASLAuthentication) to
return a boolean (or throw on failure) and here, after calling
authenticationSuccessful(session, saslServer.getAuthorizationID(),
saslServer.getMechanismName(), challenge), check its returned success flag (or
catch the thrown exception) and only then call
session.removeSessionData("SaslServer"), session.setSessionData("SaslMechanism",
saslServer.getMechanismName()) and return Status.authenticated; if
authenticationSuccessful indicates failure, do not clear/set session state and
instead return the appropriate failure Status (or propagate the exception) so
the caller does not see a successful status for a failed exchange.

---

Outside diff comments:
In
`@xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java`:
- Around line 162-189: The PropertyEventListener currently calls
initMechanisms() from a dispatcher thread which publishes a mutable HashSet into
the shared mechanisms reference while it is being populated; change
initMechanisms() to build the new set locally (e.g., a temporary HashSet), fully
populate it, then atomically replace the shared reference with an
unmodifiable/copy-on-write snapshot so readers in handle() and
getSupportedMechanisms() never see a partially-built set; alternatively make
mechanisms a volatile/AtomicReference<Set<String>> and swap the completed set,
or synchronize all reads/writes to mechanisms, ensuring the listener uses the
atomic replace approach before wiring into PropertyEventDispatcher.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c1ae283-42d8-4455-80f8-8f57351ef243

📥 Commits

Reviewing files that changed from the base of the PR and between 7ff81c7 and feb0a7f.

📒 Files selected for processing (1)
  • xmppserver/src/main/java/org/jivesoftware/openfire/net/SASLAuthentication.java

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