fix(sso): derive SAML SP URLs per request and ignore blank overrides - #3217
Merged
Conversation
This was referenced Aug 9, 2026
marevol
force-pushed
the
saml-sp-url-settings
branch
from
August 10, 2026 00:51
5ba8c7e to
9b211f4
Compare
marevol
force-pushed
the
saml-sp-url-settings
branch
from
August 10, 2026 01:34
9b211f4 to
0006d9d
Compare
The SP endpoint defaults were frozen at startup and a blank property could
wipe them out.
- buildDefaultUrl() was called only from init(), so sp.entityid,
sp.assertion_consumer_service.url and sp.single_logout_service.url were
fixed at startup. saml.sp.base.url, however, is writable at runtime from
the admin UI, so changing "SP Base URL" there left http://localhost:8080
in the metadata and in AuthnRequests until Fess was restarted. The three
URLs are now built in getSettings(), which already re-reads every other
saml.* property per call.
- The saml.* overlay in getSettings() copied blank values over the defaults.
SettingsBuilder treats a blank value as absent, so a stray
"saml.sp.entityid=" in system.properties silently produced
sp_entityId_not_found / sp_acs_not_found. Blank values are now skipped.
- The requested_authncontextcomparison key carried a duplicated
"onelogin.saml2.security." prefix and was therefore ignored by
SettingsBuilder. The value it set ("exact") is also the library default,
so correcting the key does not change behaviour.
- Dropped five defaults whose value was the empty string (sp.x509cert,
sp.privatekey, security.sign_metadata, idp.single_logout_service.response.url
and organization.lang). SettingsBuilder requires a non-blank value, so none
of them had any effect.
- Extracted the default map into createDefaultSettings() so tests can assert
against the production values.
SamlAuthenticatorTest previously hand-copied the default map and asserted
against its own copy, which had already drifted (it still declared rsa-sha1
after the production default moved to rsa-sha256) without failing. The tests
now call the real methods and cover the two fixes above; both new regression
tests were confirmed to fail against the previous behaviour.
marevol
force-pushed
the
saml-sp-url-settings
branch
from
August 10, 2026 01:57
0006d9d to
87f1bf0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two ways the SP endpoint settings could end up wrong, plus cleanup of dead configuration and a test file that could not detect any of it.
1.
saml.sp.base.urlrequired a restartbuildDefaultUrl()was called only from@PostConstruct init(), sosp.entityid,sp.assertion_consumer_service.urlandsp.single_logout_service.urlwere computed once at startup and cached indefaultSettings.But
saml.sp.base.urlis writable at runtime:AdminGeneralActionwrites it viasetSystemPropertywhen the "SP Base URL" field on the admin General page is saved.getSettings()re-reads every othersaml.*property on every call, so an admin who follows the documented "Option 1: set the base URL" path sawhttp://localhost:8080/...in the SP metadata and in AuthnRequests until Fess was restarted — and withstrict=truethe resulting Destination/Audience mismatches fail the login.The three URLs are now built inside
getSettings().2. A blank
saml.*property wiped out the defaultThe overlay loop copied values into the settings map without checking for blanks:
SettingsBuilder#isStringrequiresisNotBlank, so a blank value is not "no override" — it replaces a good default with something the builder then treats as absent. A straysaml.sp.entityid=insystem.propertiessilently yields:which surfaces as a
SettingsExceptionfrom theAuthconstructor. Blank values are now skipped.(The admin UI does not produce these: LastaFlute maps empty text parameters to
nullandsetSystemProperty(key, null)removes the key. This is a guard against hand-edited or migratedsystem.properties.)3. Dead configuration
onelogin.saml2.security.onelogin.saml2.security.requested_authncontextcomparisonhad a duplicated prefix, soSettingsBuildernever read it. Verified against the library: setting that key tominimumleavesgetRequestedAuthnContextComparison()atexact. The value it intended to set is also the library default, so fixing the key is behaviour-neutral.sp.x509cert,sp.privatekey,security.sign_metadata,idp.single_logout_service.response.url,organization.lang.isStringrejects blanks, so none of them did anything. Removed.4. The tests could not fail
SamlAuthenticatorTesthad a privatecreateDefaultSettings()that hand-copied the production map, and six tests asserted against that copy without ever touchingSamlAuthenticator. The copy had already drifted — it still declaredlong after the production default moved to
rsa-sha256, and stayed green. The privatesetDefaultSettings()helper was never called at all.The map is now extracted into a
protected createDefaultSettings()on the authenticator, and the tests call the real methods. New coverage:test_getSettings_spUrlsFollowBaseUrlChangedAfterStartup— fix 1test_getSettings_blankPropertyKeepsDefault— fix 2test_createDefaultSettings_hasNoBlankValues— keeps fix 3 from regressingtest_createDefaultSettings_security— pins the signature algorithm and the corrected comparison keyBoth regression tests were confirmed to fail when the corresponding fix is reverted.