refactor!: move the SSO authenticators to fess-sso-* plugins - #3430
Merged
Merged
Conversation
All four authenticators leave the distribution: saml, spnego, entraid and oic become fess-sso-saml, fess-sso-spnego, fess-sso-entraid and fess-sso-oidc. Together they were 15 jars of third-party SDKs -- java-saml with xmlsec and woodstox, msal4j, the Nimbus OAuth2/OIDC stack, and the SPNEGO library -- on four independent release cadences, in a distribution where most installations configure none of them. What core keeps is the mapping from an sso.type value to the component name <sso.type>Authenticator, so a type core has never heard of reaches a plugin that registers that name. fess_sso.xml keeps ssoManager and the ++ merge point; fess_sso++.xml is deleted, because a component of the same name in the war and in a plugin makes getComponent throw TooManyRegistrationComponentException and runs @PostConstruct twice, and fess_sso+<component>.xml cannot override it either -- RedefinableComponentTagHandler.redefine() refuses a base path containing '+'. A test asserts core ships the base file and no ++ file. SsoAction stays: FessSearchAction references SsoAction.class, and the SAML responses it serves are redirects and a metadata stream rather than JSP. The administration screens stay too, because a plugin jar is mounted at /WEB-INF/classes and cannot supply a JSP -- so admin_general.jsp keeps offering all four types, and the labels and the 47 form fields stay with it. The four credential classes move with their authenticators, out of org.codelibs.fess.app.web.base.login and into org.codelibs.fess.sso.<name>. Nothing resolved them by name: FessLoginAssist handles LocalUserCredential and delegates every other type to auth.resolveCredential(), and they implement only LoginCredential and FessCredential, which stay. Keeping them under org.codelibs.fess.app would have put plugin classes in the smart-deploy scan path for no benefit. getAuthenticator() answered null and said nothing, at any log level. Every caller turns that null into a redirect, so the entire symptom was a GET /sso/ answering 302 to /login/ with a configuration that is complete and correct -- what is missing is a plugin. That became the ordinary upgrade path with this change, so SsoManager now warns once per sso.type, naming the component it looked for and the plugin that provides it. Blank and none say nothing: /sso/ is reachable whether or not SSO is configured, and it is anonymous, so the warning is deduplicated to keep an unauthenticated client from filling the log. Warning, not error: an ERROR line is a notification trigger in Fess, and this is an installation that has not finished rather than a fault. The wording follows the exception StorageClientFactory writes for the same situation. mvn dependency:list -DincludeScope=runtime before and after: 256 runtime artifacts to 241. Exactly 15 removals, no additions, no version changes, totalling 5,827,073 bytes. commons-text stays -- java-saml is one of three requesters, with fess-crawler and handlebars. woodstox-core and stax2-api do leave, reached only through xmlsec 4.0.1, and no artifact left in the war needs a StAX implementation. bcprov-jdk18on is unaffected: fess-crawler-lasta reaches it through bcjmail. Checked across every repository in the workspace: msal4j and azure-json appear in fess-ds-microsoft365, but inside its own shade includes -- it bundles them rather than taking them from the war. No other plugin pom names any of the 15, and no plugin source imports org.codelibs.saml2, org.codelibs.spnego, com.nimbusds, com.microsoft.aad.msal4j, com.ctc.wstx or org.apache.xml.security. deps.xml names none of them, so the child-process class paths under WEB-INF/env are untouched. google-oauth-client is kept, though only the OIDC authenticator used it. Removing the declaration removes no jar -- google-api-client 2.7.2 requests it transitively at 1.36.0 and stays for fess-ds-gsuite -- so the only effect would be to move the choice of version out of this pom. Its types extend google-http-client's, which is pinned here, and a split between them fails at run time rather than at compile time. mvn test: 7770 tests to 7462. The difference reconciles exactly: 312 in the eight test classes that moved to the plugins, one from merging two FessLoginAssistTest cases that used SamlCredential.SamlUser and OpenIdConnectCredential.OpenIdUser as two stand-ins for the same thing -- any FessUser that is not the local document -- and five added to SsoManagerTest for the warning. mvn javadoc:jar passes, which needed AdminSysteminfoAction's {@link} to EntraIdAuthenticator turned into {@code} plus where it went. The ten DI-only tunables fess_sso++.xml carried, on samlAuthenticator and entraidAuthenticator, move to the plugins as commented properties exactly as they were. None is promoted to a configuration key: none appears anywhere in the seven documentation trees, so they are internal defaults rather than settings anyone was told about.
marevol
force-pushed
the
feat/extract-sso-authenticators
branch
from
September 10, 2026 04:25
2b3d8d1 to
2e19f79
Compare
marevol
added a commit
to codelibs/fess-workspace
that referenced
this pull request
Sep 10, 2026
…sitories (#35) 15.9 moves the S3 and GCS storage clients and the four SSO authenticators out of the Fess distribution, so sets/all.yaml now clones and builds the six repos that carry them: fess-storage-s3, fess-storage-gcs, fess-sso-saml, fess-sso-spnego, fess-sso-entraid and fess-sso-oidc. All six default to main. That takes the set from 48 repositories to 54 -- fess-storage-gcs had never been added, so this covers the GCS split as well as the ones just extracted. README.md gains a table per group, with the sso.type / storage.type value each plugin serves. fess-sso-oidc is the one place the repo name and the type differ, since the type stays oic. The prose says what core keeps: SsoManager resolving <sso.type>Authenticator, StorageClientFactory resolving <storage.type>StorageClient, with the implementation and the SDK in the plugin, and the s3:/gcs: crawler client registered by the plugin around a class that stays in fess-crawler. plugins.yaml is left alone: it covers data stores only, which the README now says outright rather than leaving a reader to infer it. CLAUDE.md records where the code went, so a grep of repos/fess that comes up empty is not a surprise: the storage half is already on master, the SSO half is codelibs/fess#3430. It also records the version-pairing constraint -- an SSO plugin from a different minor than the war registers the same component name twice, because core ships its own fess_sso++.xml up to 15.8 and LastaDi merges every ++ file on the class path, so container.init() succeeds and getComponent() then throws TooManyRegistrationComponentException. Storage has no counterpart before 15.9: fess_storage.xml and the <storage.type>StorageClient lookup are both new. And a plugin repo's prefix is also its PluginHelper.ArtifactType, where STORAGE and SSO were added in 15.9. Skills: - fess-doc-review/refs/source-mapping.md: the four SSO rows and three new storage rows point at the plugin repos, each naming what stayed in core -- sso.type and the entraid keys with their aad fallback in FessProp, StorageClientFactory's lookup, StorageType's endpoint detection -- plus a note on where the same classes live when reviewing 15.8 or earlier docs. The OIDC credential also changed package, out of org.codelibs.fess.app.web.base.login. - fess-version-bump: fess-sso-* and fess-storage-* belong to wave 5. The script derives its list from all.yaml, so only the documented wave needed an edit. - fess-tech-doc: both plugin families added to the "where to look" list. Verified by parsing sets/all.yaml with the workspace's own yaml_parser.sh (54 repositories, the six resolving to main), cloning all six, and running status.sh --short.
This was referenced Sep 10, 2026
marevol
added a commit
to codelibs/fess-storage-gcs
that referenced
this pull request
Sep 15, 2026
Building a Cloud Storage client fails on current Fess main with NoClassDefFoundError: com/google/protobuf/MessageOrBuilder, raised from DefaultStorageRetryStrategy.<clinit>. Both GcsStorageClient and fess-crawler's GcsClient build through it, so the storage page, uploads and gcs: crawling all break once the plugin is installed. protobuf-java reached the war through org.opensearch:opensearch, which codelibs/fess#3438 and codelibs/fess#3439 took out. Stop excluding it so the SDK's protobuf-java 4.33.6 is shaded. The comment claimed every excluded artifact is still in the war. That is no longer true of woodstox-core and stax2-api either; they left with codelibs/fess#3430. They stay excluded because only jackson-dataformat-xml uses them and neither GcsStorageClient nor the gcs: crawler client reaches it, and the comment now says so.
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.
All four authenticators leave the distribution, the way #3428 moved the S3 storage backend. Together they were 15 jars, 5,827,073 bytes of third-party SDKs on four independent release cadences — java-saml with xmlsec and woodstox, msal4j, the Nimbus OAuth2/OIDC stack, the SPNEGO library — in a distribution where most installations configure none of them.
What core keeps, and why
Core keeps the mapping from an
sso.typevalue to the component name<sso.type>Authenticator, so a type core has never heard of reaches a plugin that registers that name.SsoActionstays:FessSearchActionreferencesSsoAction.class, and the responses it serves are redirects and a metadata stream, not JSP.The administration screens stay, and that is forced rather than chosen.
FessWebResourceRoot.processWebInfLib()mounts aFess-WebAppJarplugin at/WEB-INF/classesas aCLASSES_JARand never adds aRESOURCE_JAR, so a plugin cannot supply a JSP.admin_general.jsptherefore keeps offering all four types, with the 47 form fields and the labels.fess_sso.xmlkeepsssoManagerand stays as the++merge point.fess_sso++.xmlis deleted, because there is no way to share the file: a component of the same name in the war and in a plugin makesgetComponentthrowTooManyRegistrationComponentExceptionand runs@PostConstructtwice, andfess_sso+<component>.xmlcannot override it either —RedefinableComponentTagHandler.redefine()refuses a base path containing+. A test asserts core ships the base file and no++file.The four credential classes move with their authenticators, out of
org.codelibs.fess.app.web.base.loginintoorg.codelibs.fess.sso.<name>. Nothing resolved them by name:FessLoginAssisthandlesLocalUserCredentialand delegates every other type toauth.resolveCredential(), and they implement onlyLoginCredentialandFessCredential, which stay. Leaving them underorg.codelibs.fess.appwould have put plugin classes in the smart-deploy scan path for no benefit.Reporting an
sso.typenothing servesgetAuthenticator()answerednulland logged nothing. Every caller turns that null into a redirect, soGET /sso/answered 302 to/login/, andSsoAction.index()puterrors.sso_login_erroron that page — "SSO login process failed.", which names neither a cause nor a remedy. The only trace in the log was a debug line reading "SSO is available but no user found."; nothing at warning level or above, with a configuration that is complete and correct — what is missing is a plugin. That became the ordinary upgrade path with this change, soSsoManagernow warns once persso.type, naming the component it looked for and the plugin that provides it:Blank and
nonesay nothing —/sso/is reachable whether or not SSO is configured. It is also anonymous, so the warning is deduplicated per type to keep an unauthenticated client from filling the log. Warning rather than error, because anERRORline is a notification trigger in Fess and this is an installation that has not finished. The wording follows the exceptionStorageClientFactorywrites for the same situation.The pairs are spelled out rather than derived, because
sso.type=oicis served byfess-sso-oidc— the one place the plugin name and the type differ, sinceoicis an internal abbreviation and the documentation has always said OIDC.Dependency measurement
mvn dependency:list -DincludeScope=runtimebefore and after: 256 runtime artifacts to 241. Exactly 15 removals, no additions, no version changes.commons-textstays — java-saml is one of three requesters, with fess-crawler and handlebars.woodstox-coreandstax2-apido leave, reached only throughxmlsec4.0.1, and nothing left in the war needs a StAX implementation.bcprov-jdk18onis unaffected:fess-crawler-lastareaches it throughbcjmail.msal4jandazure-jsonappear infess-ds-microsoft365, but inside its own shade<includes>— it bundles them rather than taking them from the war. No other plugin pom names any of the 15, and no plugin source importsorg.codelibs.saml2,org.codelibs.spnego,com.nimbusds,com.microsoft.aad.msal4j,com.ctc.wstxororg.apache.xml.security.deps.xmlnames none of them, so the child-process class paths underWEB-INF/envare untouched.google-oauth-clientis kept, though only the OIDC authenticator used it. Removing the declaration removes no jar —google-api-client2.7.2 requests it transitively at 1.36.0 and stays forfess-ds-gsuite— so the only effect would be to move the choice of version out of this pom. Its types extendgoogle-http-client's, which is pinned here, and a split between them fails at run time rather than at compile time.Tests
mvn test: 7770 → 7462, and the difference reconciles exactly.FessLoginAssistTestcases. They usedSamlCredential.SamlUserandOpenIdConnectCredential.OpenIdUseras two stand-ins for the same thing — anyFessUserthat is not the local document, which is allneedsLoginSessionSyncCheckasks — so they are now one test with a local anonymousFessUser.SsoManagerTestfor the warning: the message content, once-per-type, theaadmapping namingfess-sso-entraid, silence for blank/none/null, and the classpath assertion above.mvn javadoc:jarpasses, which CI runs and which neededAdminSysteminfoAction's{@link}toEntraIdAuthenticatorturned into{@code}plus where it went. Ten remaining mentions of the moved class names are prose that is still true, and naming the class is how a reader finds the behaviour.Runtime verification
A LastaDi container was booted on a plain JVM with only
fess_sso.xml, on a class path of this branch'sWEB-INF/classes, this branch's runtime jars (no SSO SDK), and thefess-sso-entraidjar:fess_sso++.xmlresolves from the plugin alone, which is the point of the deletion. Adding a second copy of that file declaring the same component — what a 15.8 war would do — reproduces the reason it cannot be shared:The container initializes and the failure lands on
getComponent(), so a mismatched pair starts normally and returns 500 from/sso/. That is why the plugins are version-paired and say so in their READMEs.One consequence beyond SSO: the StAX implementation
woodstox-coreandstax2-apireach the war only throughxmlsec4.0.1, which only java-samlasks for, so they leave with it. Nothing else in the distribution needs them, and nothing else
supplies a replacement: no jar among the remaining 241 runtime artifacts carries a
META-INF/services/javax.xml.stream.*entry, soXMLInputFactory.newFactory()now resolves tothe JDK implementation instead of Woodstox. The JDK's is a complete implementation, so this is a
change of implementation rather than a loss, but it applies to every installation and not only to
the ones using SAML.
fess-sso-samldeliberately does not ship Woodstox back. Bundling it alone would be worsethan not having it: Woodstox registers itself through that same ServiceLoader entry while its
stax2-apicompanion is not shaded, soXMLInputFactory.newFactory()would then throwClassNotFoundException: org.codehaus.stax2.XMLInputFactory2for the whole webapp -- reproducedwhile building the plugin. Bundling both would instead make the webapp's XML parser depend on
whether an SSO plugin happens to be installed. Excluding it keeps one answer either way, and
java-saml never uses the StAX API: scanning every class in
java-saml-3.1.2.jarandjava-saml-core-3.1.2.jarfinds zero references tojavax/xml/stream,org/codehaus/stax2,com/ctc/wstxororg/apache/xml/security/stax-- it uses Santuario's DOM API.The ten DI-only tunables
fess_sso++.xmlcarried ten commented<property>entries onsamlAuthenticatorandentraidAuthenticator—maxRequestIds,maxStates, the Graph timeouts, the group cache bounds and so on. They move to the plugins exactly as they were, and none is promoted to a configuration key: none appears anywhere in the seven documentation trees, so they are internal defaults rather than settings anyone was told about.Left alone deliberately
admin_general.jspstill offers all foursso.typevalues with no hint that a plugin is required; the log line above is what reports it. An emptysso.typestill makesavailable()returntrue— existing assertions pin that — and stays quiet.bcprov-jdk18on's direct declaration is untouched; whether its version pin existed for java-saml is unverified.fess-parentstill definesjava.saml.version,spnego.version,msal4j.versionandoauth2.oidc.sdk.version, which this pom no longer references and the plugins now need.