Skip to content

JAXB3 Migration#1023

Open
maximthomas wants to merge 21 commits into
OpenIdentityPlatform:masterfrom
maximthomas:features/jaxb3-update-2026-05-19
Open

JAXB3 Migration#1023
maximthomas wants to merge 21 commits into
OpenIdentityPlatform:masterfrom
maximthomas:features/jaxb3-update-2026-05-19

Conversation

@maximthomas

Copy link
Copy Markdown
Contributor

No description provided.

@maximthomas maximthomas requested a review from vharseko May 21, 2026 10:13
@vharseko vharseko linked an issue May 27, 2026 that may be closed by this pull request
@vharseko vharseko added this to the 16.2.0 milestone Jun 1, 2026
vharseko added 11 commits June 20, 2026 12:56
… after JAXB3 migration

getIDPSSOConfigOrSPSSOConfig() now returns List<JAXBElement<BaseConfigType>>,
but removeFromEntityConfig still cast elements directly to BaseConfigType /
AttributeType. Unwrap via getValue(), mirroring addToEntityConfig.
…B3 migration

getSingleLogoutService() returns List<SingleLogoutServiceElement> (JAXBElement
wrappers); unwrap to List<EndpointType> before LogoutUtil.doLogout().
Unwrap the SingleLogoutService wrapper list to List<EndpointType> before
LogoutUtil.doLogout(). The wrapper-expecting getSLOServiceLocation() path is
left unchanged.
…r JAXB3 migration

convertAttributes() added the AttributeValueElement wrapper instead of its
content to the list passed to setAttributeValueString(), which casts each
element to String. Add the unwrapped value.
…ws after JAXB3 migration

getIDPSSOConfigOrSPSSOConfigOrAuthnAuthorityConfig() returns
List<JAXBElement<BaseConfigType>>; unwrap via getValue() before use.
…ter JAXB3 migration

getUse() returns null when the optional use attribute is absent (one key for
both signing and encryption); guard before calling value().
… migration

SingleLogoutService / NameIDMappingService / AuthnQueryService /
AssertionIDRequestService endpoints were created with createAttributeServiceType(),
which injects a spurious xsi:type="AttributeServiceType" on marshal. Use
createEndpointType().
…DFFCOTUtils after JAXB3 migration

getAffiliationDescriptorConfig() is null for ordinary SP/IDP entities; guard the
wrapper before getValue() in add/remove EntityConfig.
…ements after JAXB3 migration

Optional JAXB elements are now possibly-null JAXBElement wrappers. Add a
null-safe jaxbValue() helper in IDPPBaseContainer and use it across the IDPP
container read-back methods.
…nt after JAXB3 migration

getIDPSSODescriptor() can return null for an unknown issuer; reject with
InvalidGrantException instead of dereferencing the null wrapper.
AttributeValueElement.getValue() returns the scalar content (not a List) for
string-valued attributes, so the "instanceof List" guard silently skipped
context matching. Normalize scalar and list content to restore the previous
getContent() behaviour.

@vharseko vharseko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

PR #1023 "JAXB3 Migration" — fix registry

Branch tip after follow-up work: f168117cc6 (11 commits on top of 63ff4448ae).
PR: #1023


Root cause (common to all 11 fixes)

The migration moves XML binding from JAXB 1.0 / javax.xml.bind to JAXB 3.0 / jakarta.xml.bind and stops checking generated classes into the repo — they are now generated at build time (jaxb-maven-plugin). In doing so the shape of the generated API changes, which breaks consumer code in three ways that compile but fail at runtime:

  1. Global XML elements are now JAXBElement<T> wrappers.
    A getter that used to return the concrete type now returns a wrapper, so callers must call .getValue().

    • Any remaining raw cast (Type) iter.next() or enhanced-for for (Type x : rawList) over a list of wrappers → ClassCastException. It only compiles because the list is declared as a raw List.
  2. Optional elements are now nullable wrappers.
    A getXxx().getValue() chain on an absent (optional) element → NullPointerException, and the existing if (x != null) guard below becomes dead code.

  3. ObjectFactory.createXxx() factory methods now take a value argument.
    Passing the subtype AttributeServiceType into a factory whose declared value type is EndpointType makes JAXB emit a spurious xsi:type="AttributeServiceType" on marshal → invalid SAML2 metadata.


Commit registry

# Commit File(s) Type Essence
1 dac13f3c06 WSFederationCOTUtils.java CCE remove path not migrated
2 9e8cad2dd5 SAML2SingleLogoutHandler.java CCE wrappers into doLogout(List<EndpointType>)
3 ea473f7fc0 SPSingleLogout.java CCE same in SP SLO
4 6a4d81419b AttributeQueryUtil.java CCE wrapper added instead of value
5 3435063154 GetCircleOfTrusts.java, ImportSAML2MetaData.java CCE raw cast to BaseConfigType
6 271ad11235 SAMLv2ModelImpl.java NPE getUse() == null
7 faf0be2e7e SAMLv2ModelImpl.java XML spurious xsi:type on endpoints
8 d9d4b8e13e IDFFCOTUtils.java NPE no affiliation config
9 5aa4d1c5e3 IDPP* containers NPE optional Liberty PP elements
10 f77dbb76bd Saml2GrantTypeHandler.java NPE unknown issuer
11 f168117cc6 SAML2IDPProxyFRImpl.java logic silently disabled context matching

Per-fix detail

1. dac13f3c06 — WSFederationCOTUtils.removeFromEntityConfig (CCE)

File: openam-federation-library/.../wsfederation/meta/WSFederationCOTUtils.java
Problem: getIDPSSOConfigOrSPSSOConfig() returns List<JAXBElement<BaseConfigType>>. addToEntityConfig was migrated to .getValue(), but the sibling removeFromEntityConfig was not: it still did (BaseConfigType) iter.next() and (AttributeType) iter2.next(). Removing a WS-Fed entity from a circle of trust → CCE.
Fix: generics + .getValue(), mirroring the add method.

2. 9e8cad2dd5 — Multi-protocol SOAP single logout (CCE)

File: .../multiprotocol/SAML2SingleLogoutHandler.java
Problem: getSingleLogoutService()List<SingleLogoutServiceElement> (which are JAXBElement<EndpointType>). The list is passed to LogoutUtil.doLogout(List<EndpointType>), whose for (EndpointType e : list) → CCE. Sibling paths (IDPSessionListener/SPSessionListener) were fixed; this one was missed.
Fix: .stream().map(JAXBElement::getValue).collect(...) before doLogout.

3. ea473f7fc0 — SP single logout (CCE)

File: .../saml2/profile/SPSingleLogout.java
Problem: same wrapper list passed into doLogout(List<EndpointType>).
Fix: unwrap to List<EndpointType>. The second slosList (feeds getSLOServiceLocation, which expects the wrappers) is intentionally left unchanged.

4. 6a4d81419b — SAML2 attribute query conversion (CCE)

File: .../saml2/profile/AttributeQueryUtil.java
Problem: convertAttributes() added the wrapper AttributeValueElement instead of its content. The list goes to setAttributeValueString(), which does (String) iter.next() → CCE. The computed content was unused.
Fix: add content (the unwrapped value).

5. 3435063154 — OpenFM workflows (CCE)

Files: OpenFM/.../workflow/GetCircleOfTrusts.java, ImportSAML2MetaData.java
Problem: getIDPSSOConfigOrSPSSOConfigOrAuthnAuthorityConfig()List<JAXBElement<BaseConfigType>>, but the code did (BaseConfigType) config.iterator().next() → CCE when resolving the realm for a hosted entity.
Fix: generics + .getValue().

6. 271ad11235 — KeyDescriptor without use (NPE)

File: openam-console/.../federation/model/SAMLv2ModelImpl.java
Problem: keyOne.getValue().getUse().value()getUse() is null when a <KeyDescriptor> omits the optional use attribute (one key for both signing and encryption). The next line if (type == null ...) was written for exactly that null case but is now unreachable. NPE when viewing the entity in the console.
Fix: null guard before .value().

7. faf0be2e7e — invalid metadata endpoints (XML)

File: openam-console/.../federation/model/SAMLv2ModelImpl.java
Problem: SingleLogoutService / NameIDMappingService / AuthnQueryService / AssertionIDRequestService endpoints were built with createAttributeServiceType(). Since AttributeServiceType extends EndpointType, marshalling injects a spurious xsi:type="AttributeServiceType" → off-schema metadata. 8 sites.
Fix: createEndpointType(). The two legitimate createAttributeServiceElement(createAttributeServiceType()) sites are left untouched.

8. d9d4b8e13e — IDFFCOTUtils without affiliation config (NPE)

File: .../federation/meta/IDFFCOTUtils.java
Problem: getAffiliationDescriptorConfig().getValue() — for ordinary SP/IDP entities the getter is null, so .getValue() fails before the dead if (affiConfig != null) check. 2 sites (add/remove EntityConfig).
Fix: null-check the wrapper before .getValue().

9. 5aa4d1c5e3 — Liberty Personal Profile containers (NPE)

Files: IDPPBaseContainer.java + IDPPFacade, IDPPDemographics, IDPPLegalIdentity, IDPPEmploymentIdentity, IDPPCommonName, IDPPAddressCard
Problem: read-back methods (getXxxMap/createAddressCard) do obj.getXxx().getValue() on optional PP elements; for absent ones (common, e.g. middle name) → NPE. The old code stored the nullable value and downstream getAttributeMap handled null fine.
Fix: add protected static <T> T jaxbValue(JAXBElement<T>) to the base class and apply it at every read site.

10. f77dbb76bd — OAuth2 SAML2 bearer, unknown issuer (NPE)

File: openam-oauth2-saml2/.../core/Saml2GrantTypeHandler.java
Problem: metaManager.getIDPSSODescriptor(...) can return null for an unknown issuer; idpSsoDescriptor.getValue() → NPE. Previously null was handled gracefully (clean rejection). This turns a rejection into an uncaught exception.
Fix: null-check the descriptor → InvalidGrantException("Unknown assertion issuer").

11. f168117cc6 — IDP proxy: authn-context matching (logic)

File: .../saml2/plugins/SAML2IDPProxyFRImpl.java
Problem: AttributeValueElement.getValue() returns a scalar (String) for string-valued attributes, not a List, so the instanceof List guard silently skipped the whole proxy-IDP authn-context selection block (a silent regression, no error).
Fix: normalize scalar and list content to List, restoring the previous getContent() behaviour.


Verification

  • Build (JDK 26 / Maven 3.9.16) of the affected modules — BUILD SUCCESS:
    Federation Library, OpenFM, Admin Console, OAuth2-SAML2.
  • Schema modules (saml2/liberty/wsfederation/xacml3) build and generate classes correctly.
  • The cumulative diff of all 11 commits is byte-identical to the patch that was built and verified.
  • Push to the PR branch was a fast-forward (63ff4448ae..f168117cc6).

Still to verify manually (not covered by compilation)

  • JSPs (not compile-checked): SA_IDP.jsp, SA_SP.jsp, spSingleLogoutInit.jsp, realmSelection.jsp.
  • Functionally: SAML2 SLO (SOAP + multi-protocol), attribute query, COT add/remove (SAML2 / WS-Fed / IDFF), Liberty Personal Profile, OAuth2 SAML2 bearer.

vharseko added 2 commits June 20, 2026 14:07
- RestAuthNameValueOutputCallbackHandler.java: keep correct NameValueOutputCallback Javadoc
- ResourceOwnerSessionValidator.java: merge copyright attributions
@vharseko vharseko added the refactoring Code cleanup, refactor, dead-code or dependency removal label Jul 8, 2026

@vharseko vharseko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review: JAXB 1.x → JAXB 3 migration

Deep review of the full diff vs merge base 0ee32ac4 (2,787 files, +4,327/−541,024). The build/codegen architecture was verified empirically (regenerated all three schema modules offline with the PR's plugin config and bindings, plus an unmarshal/marshal runtime probe with jaxb-runtime 3.0.2 on the PR's own test resources). The remaining findings are call-site bugs of the same classes already fixed in the follow-up commits on this branch.

What checks out (verified, not just read)

  • Package parity confirmed: codegen reproduces every deleted JAXB1 package exactly; every class-bearing package gets an ObjectFactory, so all JAXBContext.newInstance(contextPath) call sites resolve. Metadata + entity-config round-trip probe passes.
  • All 31 jaxb.properties correctly rewritten to org.glassfish.jaxb.runtime.v2.ContextFactory (the deprecated jakarta.xml.bind.context.factory key is accepted by the 3.0 ContextFinder).
  • xs:dateTime kept on java.util.Calendar and anyURI on String via globalBindings — avoids the XMLGregorianCalendar break; *Element classes recreated via jxb:class as JAXBElement<T> subclasses; the curation from ~127 to ~35 element classes in saml2 leaves zero dangling references.
  • Prefix-mapper key change to org.glassfish.jaxb.namespacePrefixMapper is correct and required (the old com.sun.xml.bind.* key throws PropertyException on the new runtime — verified).
  • No javax.xml.bind references remain anywhere in main sources. No hand-fixes were lost in the deleted generated code (checked full git history of those files).
  • Nice bonus fixes: the pre-existing iter/iterV bug in AttributeQueryUtil.convertAttributes, null check in Saml2GrantTypeHandler; new SAML2MetaUtilsTest + Playwright REST-STS e2e test.

Blocking bugs

CI is green because it compiles and runs unit tests, but these break at runtime ("needs testing" label is accurate).

WS-Federation — broken entirely

  • WSFederationMetaUtils.getAttributes (~line 231): casts members of List<AttributeElement> to AttributeType → guaranteed ClassCastException. Every WSFed extended-config read funnels through this helper (RPSigninRequest/RPSigninResponse, account/attribute mappers, WSFederationUtils.createSAML11Token).
  • WSFederationMetaUtils.getAttribute (~line 296): avp.getName() now resolves to JAXBElement.getName() returning a QName; equals(String key) is always false → every lookup (SIGNING_CERT_ALIAS, AUTH_URL, ASSERTION_TIME_SKEW, ACTIVE_REQUESTOR_PROFILE_ENABLED, ENDPOINT_BASE_URL, NameID settings) silently returns null. Needs avp.getValue().getName().

IDFF — broken entirely

  • IDFFMetaUtils.getAttributes (lines 297–306): same (AttributeType) cast on a List<AttributeElement> → CCE. This is the central helper for the whole IDFF stack (FSAssertionManager, FSAuthnDecisionHandler, attribute mappers, IDP proxy, …).
  • IDFFCOTUtils.updateCOTAttrInConfig (line 226) / removeCOTNameFromConfig (line 263): (BaseConfigType) and (AttributeType) casts on element lists, and a raw AttributeType added into a List<AttributeElement>. The create path in the same file was migrated; the update helpers were missed — adding/removing an IDFF entity to/from a COT fails whenever the entity has an entity config.
  • federation/key/KeyUtil.getKeyDescriptor (~295–318): kd.getValue().getUse().value() NPEs when use is absent (the following use == null handling is now dead), and the no-match branch returns noUsageKD.getValue() → NPE where the old code returned null to null-tolerant callers.
  • IDFFProviderManager (~128/188/214), LibertyManager (~1276/2025), EncryptedNameIdentifier (~225): .getValue() chained onto nullable getSPDescriptorConfig(...) before the null check → NPE for IDP-only entities; the SP→IDP fallback logic is now dead code.

SAML2 — optional-Boolean unboxing NPEs (highest-impact class)

JAXB1 generated primitive boolean for optional XSD attributes (absent → false); XJC 3 generates nullable Boolean (attributes are use="optional" with no default in the XSDs). Direct unboxing now NPEs whenever the attribute is absent — which is schema-legal and common in third-party metadata:

  • IDPSSOUtil.java:1865, 1953, 2026acs.getValue().isIsDefault() looping over all ACS entries of the remote SP; getDefaultACSurl runs on the common no-ACS-in-request path → IDP-side SSO fails for typical third-party SP metadata with any ACS entry lacking isDefault.
  • SPSSOFederate.java:377, 448, 1000; SAML2Utils.java:495 (verifyResponse); IDPArtifactResolution.java:472; IDPSSOUtil.java:3017; UtilProxySAMLAuthenticator.java:161; IDPProxyUtil.java:222; SPACSUtils.java:511; QueryClient.java:811; QueryHandlerServlet.java:421isWantAssertionsSigned() / isAuthnRequestsSigned() / isWantAuthnRequestsSigned().
  • isHosted() — same class: SAML2MetaManager.java:1237/1408/1494/1532, SAML2MetaSecurityUtils.java:182/475, IDFFMetaManager.java:837/871, WSFederationMetaManager.java:833/896/982, ImportMetaData.java:192/264/324, OpenFM workflow tasks, console TaskModelImpl. OpenAM always writes the attribute, but hand-imported extended configs may omit it.
  • Systematic fix: Boolean.TRUE.equals(...) at call sites (the PR already does exactly this for isPassive() in UtilProxySAMLAuthenticator), or XSD defaults, or an xjb customization. Note the added test resources set every optional boolean (hosted="1", isDefault="1", …), which is why tests don't catch this class — please add a metadata fixture with the optional attributes omitted.
  • (IDFF metadata side is not affected: lib-arch-metadata.xsd has default="false" — checked.)

SAML2 — other

  • AttributeQueryUtil.isSameAttribute:988desired.getName().equals(supported.getName()) compares String to QName (always false) → every AttributeQuery naming specific attributes is rejected with "Attribute name not supported". Needs supported.getValue().getName().
  • SAML2MetaSecurityUtils.setExtendedAttributeValue:565(AttributeType) cast on an AttributeElement list → CCE; breaks ssoadm update-entity-keyinfo (the add at line 575 was fixed, the removal loop wasn't).
  • SAML2MetaSecurityUtils.updateProviderKeyInformation:483-495.getValue() NPEs before the intended entityNotIDP/entityNotSP errors (now dead code).
  • SAML2ProviderManager (lines ~96, ~167, ~191, ~216) — getSPSSOConfig(...).getValue() NPEs for IDP-only providers; the IDP fallback in all four methods is dead; the catch handles SAML2MetaException only.

Liberty PP / Discovery / WSS Policy

  • WSSPolicyManager (lines 120, 131, 249–250, 302–303): createExactlyOneElement(createPolicyElement()) substitutes the anonymous Policy type where OperatorContentType is expected — the runtime reports SUBSTITUTED_BY_ANONYMOUS_TYPE and marshalling aborts → getPolicy()/getInputPolicy()/getOutputPolicy() always throw. Use createOperatorContentType().
  • IDPPEncryptKey/IDPPSignKey (getContainerObject): bare X509DataType added into KeyInfo content (an @XmlElementRefs list) → MarshalException: missing an @XmlRootElement annotation. Should be of.createX509DataElement(x509DataType) (the read paths in the same files already expect X509DataElement).
  • IDPPLegalIdentity:489(AltIDType) cast after instanceof AltIDElement → CCE (AltIDElement extends JAXBElement<AltIDType> now); :105createAltIDElement(getAltID(userMap)) is never null, so an empty <AltID/> is always added where the old code added nothing.
  • IDPPDemographics:281IDPPBaseContainer.getAttributeMap: List<LanguageElement> members match neither instanceof DSTString nor DSTURI → PP Language values silently dropped on read and potentially wiped on modify.
  • DiscoveryService (~221, ~362): getEncryptedResourceID().getValue() NPEs when both ResourceID variants are absent.

Console

  • ImportEntityModelImpl (~145 createSAMLv2Entity, ~316 createWSFedEntity): (BaseConfigType) config.iterator().next() on List<JAXBElement<BaseConfigType>> → CCE when importing hosted entities with extended metadata via the console (the identical pattern was fixed in OpenFM ImportSAML2MetaData/GetCircleOfTrusts; the console counterpart was missed).
  • SAMLv2ModelImpl:2035, 2096(AttributeType) casts → CCE opening the XACML PEP/PDP extended-metadata pages (lines 1605/1676 in the same file show the correct pattern).
  • SAMLv2ModelImpl:1733-1737getClass().getName().contains("KeySizeImpl") matches nothing under JAXB3 (no *Impl classes) → encryption key size silently never read. Should be instanceof EncryptionMethodType.KeySize.
  • IDFFModelImpl:497, 565, 1247, 1306, 1494; SAMLv2ModelImpl:2321, 2380.getValue() before the null check: first-time access to an IDFF entity's IDP/SP tab now NPEs instead of auto-creating the default extended config; invalid.entity.name errors degraded to NPE.
  • SMDiscoEntryData (~212–267): directives unwrapped to bare DirectiveType and added to an @XmlAnyElement list → MarshalException + element identity (which directive) lost; the read path checks instanceof AuthenticateRequesterElement etc., so the round-trip is broken. Add the JAXBElement wrappers, not their values.

Build / packaging

  • openam-clientsdk/pom.xml (~146): shade includes the non-existent coordinate jakarta.xml.bind:jaxb-api (correct: jakarta.xml.bind:jakarta.xml.bind-api) and omits jaxb-runtime's transitive deps (org.glassfish.jaxb:jaxb-core, com.sun.istack:istack-commons-runtime, org.glassfish.jaxb:txw2, jakarta.activation:jakarta.activation-api) → standalone clientsdk consumers get NoClassDefFoundError on first JAXB use.
  • openam-wsfederation-schema/pom.xml:85-88: leftover com.sun.xml.bind:jaxb1-impl dependency (removed from saml2/liberty poms in this PR) — drags the dead JAXB1 runtime into every WAR. Related: openam-auth-saml2 SAML2ProxyTest.java:26 still imports com.sun.xml.bind.StringInputStream (exists only in jaxb1-impl).

Risks / cleanups (non-blocking)

  • Sweep the .getValue()-before-null-check pattern — beyond the cases above there are ~a dozen more (e.g. SAML2Utils.java:556/2194/3556, SPACSUtils.java:392, IDPSessionListener.java:159, NameIDMapping.java:206, DoManageNameID.java:291, AssertionIDRequestUtil.java:405, multiprotocol IDFFSingleLogoutHandler:226, SingleLogoutManager:689, ValidRelayStateExtractor, DefaultAttributeMapper, ValidWReplyExtractor, ConfigureGoogleApps:94). Formerly handled null-metadata cases now surface as NPEs.
  • BaseConfigType flipped abstract="true""false" in three XSDs — necessary for JAXB3 instantiation and only used as codegen input, but worth recording as intentional in the PR description (which is currently empty).
  • Split package: org.w3._2001.xmlschema.Adapter1 is now generated into both the liberty and wsfederation jars (same bytecode; duplicate-class checkers/JPMS will complain). saml2 avoids this via its schema.xsd package-mapping trick — the same could be applied to wsfed.
  • FAMClassLoader (OpenFM): lists now internally inconsistent — classesToBeLoaded says jakarta.xml.bind. but maskedResouces still masks javax/xml/bind/, and com.sun.xml.bind.* (JAXB1 RI) entries remain.
  • Dead JAXB1 cruft to delete with this PR: ~25 bgm.ser grammar caches still packaged in the schema jars; JAXB1 entries in THIRD-PARTY.properties; com.sun.xml.bind.util.ListImpl/ProxyListImpl whitelist entries in IOUtils and serverdefaults.properties:191; root pom <jaxb1.version> property; orphan jaxb.properties in packages with no classes (.../common/jaxb/xmlsec/, parent common/jaxb/).
  • PPRequestHandler: stray junk import org.glassfish.jaxb.core.v2.model.core.ID; getMarshaller().setProperty(...); getMarshaller().marshal(...) uses two different marshaller instances so the prefix mapper never applies (pre-existing).
  • SLOLocationTest: createSingleLogoutServiceElement(of.createAttributeServiceType()) works only because AttributeServiceType extends EndpointType — use createEndpointType().
  • Upgrade note: previously serialized blobs containing JAXB1 com.sun.xml.bind.util.ListImpl instances can no longer deserialize after upgrade (class gone from classpath).

Suggested order of fixes

  1. WSFederationMetaUtils.getAttributes/getAttribute, IDFFMetaUtils.getAttributes, IDFFCOTUtils update helpers (restores WSFed + IDFF).
  2. Systematic optional-Boolean sweep (Boolean.TRUE.equals(...) or XSD defaults) + a test metadata fixture with optional attributes omitted.
  3. .getValue()-before-null-check sweep.
  4. WSSPolicyManager, PP containers, SMDiscoEntryData, console CCEs (ImportEntityModelImpl, SAMLv2ModelImpl).
  5. clientsdk shade config + jaxb1-impl removal.

Resolves the runtime defects found reviewing the JAXB1->JAXB3 migration:
- WSFederation/IDFF metadata helpers: unwrap AttributeElement/JAXBElement
  lists (ClassCastException) and compare element value names, not QNames
- SAML2/WSFed/IDFF: guard optional Boolean metadata attributes with
  Boolean.TRUE.equals(...) (absent attribute is false, not an NPE) on the
  core SSO, artifact, logout and query paths
- KeyUtil/SAML2MetaSecurityUtils/SAML2ProviderManager and ~12 other sites:
  null-check JAXB elements before getValue() so designed null/fallback
  paths run instead of throwing
- Liberty PP, WS-Policy and Disco: marshal element wrappers (not bare
  types) so KeyInfo/Policy/Directive content serialises
- Console: fix entity-import CCEs, XACML PEP/PDP casts, key-size read and
  extended-config auto-creation
- clientsdk shade: correct jakarta.xml.bind-api coordinate and add the
  jaxb-runtime transitive stack
- Build cleanup: drop leftover jaxb1-impl, dead bgm.ser/jaxb.properties,
  jaxb1 THIRD-PARTY entries and jaxb1.version; move wsfed Adapter1 out of
  the split org.w3._2001.xmlschema package
- Add sp-metadata-no-optional-booleans.xml + SAML2MetaUtilsTest coverage
  pinning the nullable-Boolean generated shape
@vharseko

Copy link
Copy Markdown
Member

Fixes pushed — a18c3c1 (addresses the review findings above)

All blocking bugs, risks, and cleanups from the review are now fixed on this branch. Summary mapped to the findings, with verification at the end.

Blocking bugs — fixed

WS-Federation (was broken entirely)

  • WSFederationMetaUtils.getAttributes — unwrap AttributeElement before use (was (AttributeType) cast → CCE).
  • WSFederationMetaUtils.getAttribute — compare avp.getValue().getName() (was JAXBElement.getName() QName, always ≠ String key).

IDFF (was broken entirely)

  • IDFFMetaUtils.getAttributes — unwrap AttributeElement (CCE).
  • IDFFCOTUtils.updateCOTAttrInConfig / removeCOTNameFromConfig — unwrap descriptor-config JAXBElements (handling both the wrapped SP/IDP lists and the unwrapped affiliation entry), unwrap AttributeElement, and add new attributes via createAttributeElement.
  • federation/key/KeyUtil.getKeyDescriptor — null-safe getUse(), and return null instead of noUsageKD.getValue() when there is no match.
  • IDFFProviderManager / LibertyManager / EncryptedNameIdentifier — null-check the config element before getValue() so the IDP fallback runs (was NPE for IDP-only entities).

SAML2 — optional-Boolean unboxing (highest-impact class)
Absent optional attributes are now treated as false via Boolean.TRUE.equals(...) instead of NPE-ing on the nullable Boolean: IDPSSOUtil (ACS isIsDefault, isWantAssertionsSigned), SPSSOFederate, SAML2Utils.verifyResponse, IDPArtifactResolution, UtilProxySAMLAuthenticator, IDPProxyUtil, SPACSUtils, QueryClient, QueryHandlerServlet, AttributeQueryUtil (isSupportsX509Query), and every isHosted() site in SAML2MetaManager / SAML2MetaSecurityUtils / IDFFMetaManager / IDFFMetaSecurityUtils / WSFederationMetaManager / cli ImportMetaData.

SAML2 — other

  • AttributeQueryUtil.isSameAttributesupported.getValue().getName() (was String.equals(QName), always false).
  • SAML2MetaSecurityUtils.setExtendedAttributeValue — unwrap AttributeElement in the removal loop (CCE); updateProviderKeyInformation — null-check role config/descriptor before getValue() so entityNotIDP/entityNotSP fires.
  • SAML2ProviderManager — restored the SP→IDP fallback via a null-safe unwrap in all four methods.

Liberty PP / Discovery / WS-Policy

  • WSSPolicyManager — pass createOperatorContentType() (was anonymous-type PolicyElementMarshalException).
  • IDPPEncryptKey / IDPPSignKey — add createX509DataElement(...) wrapper to KeyInfo (was bare X509DataTypeMarshalException).
  • IDPPLegalIdentity((AltIDElement)…).getValue() (CCE) and add AltID only when non-null.
  • IDPPDemographics / IDPPBaseContainer.getAttributeMap — unwrap JAXBElement list members so Language values survive; also fixed the pre-existing DSTURI loop-var typo.
  • SMDiscoEntryData — put the directive element wrappers into getAny() so the round-trip and read-path instanceof checks work.
  • DiscoveryService — null-safe getEncryptedResourceID().

Console

  • ImportEntityModelImpl — unwrap JAXBElement<BaseConfigType> on hosted-entity import (SAMLv2 + WSFed CCE).
  • SAMLv2ModelImpl — unwrap AttributeElement on the XACML PEP/PDP pages (CCE); replace getClass().getName().contains("KeySizeImpl") with instanceof EncryptionMethodType.KeySize; null-check configs so invalid.entity.name is reachable; Boolean.TRUE.equals(...) on the remaining nullable-Boolean reads.
  • IDFFModelImpl — null-check descriptor/affiliation configs so first-time tabs auto-create the extended config instead of NPE.
  • SAMLv2SPServicesViewBean — render isDefault as false (not "null") when absent.

Build / packaging

  • openam-clientsdk shade — corrected jakarta.xml.bind:jakarta.xml.bind-api and added the jaxb-runtime transitive stack (jaxb-core, istack-commons-runtime, txw2, jakarta.activation-api).
  • Removed leftover com.sun.xml.bind:jaxb1-impl from the wsfederation-schema pom, and migrated SAML2ProxyTest off com.sun.xml.bind.StringInputStream to ByteArrayInputStream.

Risks / cleanups — done

  • Swept the .getValue()-before-null-check pattern across SAML2Utils, SPACSUtils, IDPSessionListener, NameIDMapping, DoManageNameID, AssertionIDRequestUtil, ValidRelayStateExtractor, multiprotocol handlers, DefaultAttributeMapper, ValidWReplyExtractor (null-safe unwrap).
  • BaseConfigType anonymous subclasses → ObjectFactory.createBaseConfigType().
  • Split-package org.w3._2001.xmlschema.Adapter1 in the wsfed jar fixed via a new schema.xsd package binding (verified by regenerating: Adapter1 now lands in com.sun.identity.wsfederation.jaxb.schema, no org.w3._2001.xmlschema classes, compiles clean).
  • FAMClassLoader mask/load lists made jakarta/glassfish-consistent.
  • Deleted dead JAXB1 cruft: 41 bgm.ser, 3 orphan jaxb.properties, jaxb1-impl entries in THIRD-PARTY.properties, <jaxb1.version> in the root pom, and the com.sun.xml.bind.util.* whitelist entries in IOUtils / serverdefaults.properties.
  • PPRequestHandler — dropped the junk import and used a single Marshaller instance so the prefix mapper applies; ConfigureGoogleApps null-guard; CreateWSFedMetaDataTemplate uses the computed ssneUri; normalized jaxb.properties.

New test coverage

sp-metadata-no-optional-booleans.xml + SAML2MetaUtilsTest.optionalBooleanAttributesUnmarshalAsNull pins the JAXB 3 generated shape (isIsDefault()/isWantAssertionsSigned()/isAuthnRequestsSigned() return null when the attribute is absent) — the class of regression the existing fixtures did not exercise, since they set every optional boolean.

Verification

  • Schema regeneration (JAXB 3) + compile of openam-schema/*, openam-shared, openam-federation-library, OpenFM, openam-console, openam-cli-impl, openam-auth-saml2, openam-clientsdkBUILD SUCCESS on JDK 26.
  • Confirmed the generated IndexedEndpointType.isIsDefault() is a nullable Boolean (the premise of the unboxing bugs).
  • Tests: openam-federation-library 45/45, SAML2ProxyTest 10/10, 0 failures/errors.

Net: 153 files changed (+490 / −336), including the JAXB1 cruft deletions and 2 new files.

@vharseko vharseko self-requested a review July 14, 2026 12:54
Optional config/metadata getters now return JAXBElement wrappers; several
call sites unwrapped them with a bare .getValue(), throwing NPE on entities
that lack an extended/role config where the pre-migration code degraded
gracefully (null flowed through to a null-tolerant consumer). Add a
null-safe unwrap() helper to SAML2MetaUtils / IDFFMetaUtils and route the
affected sites through it, or guard inline.

HIGH:
- IDPPBaseContainer: unwrap the JAXBElement in the scalar attribute branch
  (mirroring the list branch) so Personal Profile scalar values are no
  longer silently cleared on Modify.
- DiscoveryService.getResourceID: accept and marshal the
  EncryptedResourceIDElement (a JAXBElement) instead of the non-root
  EncryptedResourceIDType, which failed to marshal and misrouted encrypted
  resource-ID lookups to the implied-resource (B2E) path.
- IDFFMetaUtils.getExtendedConfig, SAML2MetaManager (PEP branch guard to
  match the PDP branch), AssertionIDRequestUtil, SPSessionListener,
  AuthnQueryUtil.

MED (unguarded .getValue() on a nullable config getter whose consumer
tolerates null; NPE otherwise escaped an IDFFMetaException-only catch):
- IDPSingleLogout, IDPSessionListener, SPSingleLogout (idpConfig is null
  for all non-SOAP bindings), QueryClient, FSLoginHelper, FSServiceManager,
  FSAttributeStatementHelper, FSPreLogin, FSReturnLogoutServlet,
  FSTerminationReturnServlet, FSPostLogin, FSIDPFinderService,
  FSAssertionManager, FSSingleLogoutHandler.

Cleanup:
- Cache DatatypeFactory in a static field in Time.java.
- Drop the orphaned jaxb.version property and the redundant JAXB version
  pins / commented-out JAXB2 dependency blocks in openam-core.
@vharseko

Copy link
Copy Markdown
Member

Fix round: JAXB3 migration NPE regressions in config/metadata unwrapping (07b3585)

Follow-up on a review of the migration. The recurring issue: optional config/metadata getters now return JAXBElement wrappers, and several call sites unwrapped them with a bare .getValue(). On entities that lack an extended/role config the getter returns null, so .getValue() throws an NPE — where the pre-migration code let null flow through to a null-tolerant consumer. Added a null-safe unwrap() helper to SAML2MetaUtils / IDFFMetaUtils and routed the affected sites through it (or guarded inline). Behaviour-preserving: null → null, no control-flow change.

HIGH

  • IDPPBaseContainer — unwrap the JAXBElement in the scalar attribute branch (the list branch already did). Without it every instanceof DSTString/DSTURI/… missed the *Element wrapper, so Personal Profile scalar values (Gender, DOB, MaritalStatus, …) were silently cleared on Modify.
  • DiscoveryService.getResourceID — accept and marshal the EncryptedResourceIDElement (a JAXBElement) instead of the non-root EncryptedResourceIDType. The raw type has no @XmlRootElement, so marshal threw, was swallowed, returned null, and encrypted resource-ID lookups were misrouted to the implied-resource (B2E) path → wrong principal.
  • IDFFMetaUtils.getExtendedConfig, SAML2MetaManager (PEP branch guarded to match the PDP branch), AssertionIDRequestUtil, SPSessionListener, AuthnQueryUtil.

MED (unguarded .getValue() on a nullable config getter; the NPE also escaped an IDFFMetaException-only catch)

  • IDPSingleLogout, IDPSessionListener, SPSingleLogout (note: idpConfig is null for all non-SOAP bindings), QueryClient, FSLoginHelper, FSServiceManager, FSAttributeStatementHelper, FSPreLogin, FSReturnLogoutServlet, FSTerminationReturnServlet, FSPostLogin, FSIDPFinderService, FSAssertionManager, FSSingleLogoutHandler.

Cleanup

  • Cache DatatypeFactory in a static field in Time.java.
  • Drop the orphaned jaxb.version property and the redundant JAXB version pins / commented-out JAXB2 dependency blocks in openam-core.

Verified: openam-shared + openam-federation-library compile cleanly (863 source files, no errors).

Not changed (deliberate):

  • LOW hosted-entity self-lookups where the config is practically always present or the NPE is absorbed by a catch(Exception) (registration/termination servlets, FSAccountManager, FSSOAPReceiver, DefaultFedletAdapter, DefaultAccountMapper, wsfederation plugins where the pre-migration code dereferenced the same nullable config identically — not migration regressions).
  • openam-wsfederation-schema has no <schemaIncludes> (globs every xsd in src/main/xsd, including import-only schemas) and the openam-xacml3-schema regeneration profile is still pinned to com.sun.xml.bind:jaxb-xjc:2.1.10 (would re-emit javax.* imports). Left as-is pending a CI build to confirm the generated class set before touching working codegen.
  • IOUtils deserialization whitelist (dropped com.sun.xml.bind.util.ListImpl/ProxyListImpl) — fail-closed; verify no rolling-upgrade session relies on them.

vharseko added 2 commits July 14, 2026 18:52
Creating the factory in a static initializer ran it on every Time
class-load, which nearly all code paths (and test setup) trigger; on a
classpath with an older javax.xml.datatype JAXP jar this threw
IllegalAccessError (FactoryFinder -> jdk.xml.internal.SecuritySupport),
failing 91 openam-core tests. Move it to a lazy holder so it initializes
only on first getXMLGregorianCalendarInstance use, off the class-init path.
Under JAXB3 several generated element classes are JAXBElement wrappers
rather than their value types, and top-level types lack @XmlRootElement.
Fix the call sites that still assumed the JAXB1 shape:

- IDPPBaseContainer.toXMLDocument: wrap PPType via ObjectFactory.createPP
  before marshalling (PPType has no @XmlRootElement) so Personal Profile
  containers render instead of throwing MarshalException.
- PPRequestHandler: unwrap Resource/EncryptedResourceID elements before
  DSTRequestHandler.getResourceID so IDPP Query/Modify are not rejected
  with NO_RESOURCE.
- IDPP container getXxxMap helpers (CommonName, Demographics,
  EmploymentIdentity, LegalIdentity, VAT): unwrap the top-level container
  element so whole-container Modify applies instead of "invalid Element".
- Discovery authorization (DiscoUtils lookup, DiscoveryService insert and
  remove): pass ResourceOfferingType (and unwrap DiscoEntryElement on the
  remove path) so DefaultDiscoAuthorizer authorizes policy-protected calls.
- IDFFMetaSecurityUtils: unwrap AttributeElement before reading AttributeType
  so certificate-alias updates don't throw ClassCastException.
- IDPPLegalIdentity: unwrap IDValueElement before casting to DSTString for
  AltID/VAT IDValue updates.
@vharseko

Copy link
Copy Markdown
Member

Fix round: unwrap JAXB3 element wrappers in Liberty IDPP & Discovery flows (89062d3)

Addresses the reported P1/P2 regressions where JAXB1 call sites relied on generated element classes being their value types. Under JAXB3 those are JAXBElement wrappers (and top-level types lack @XmlRootElement), breaking core Liberty Personal Profile and policy-enabled Discovery at runtime. Fixed the consuming call sites (the wrappers themselves are correct; we unwrap at the boundary with the older API). Verified each against the generated sources; openam-federation-library compiles (863 files).

P1

  • IDPPBaseContainer.toXMLDocumentgetContainerObject() returns a bare PPType, which has no @XmlRootElement (root is ObjectFactory.createPP(PPType)JAXBElement<PPType>). Now wrapped via createPP(...) before marshalling, so PP containers render instead of throwing MarshalException. Centralized fix covers every container.
  • PPRequestHandler (Query + Modify) — getResourceID() / getEncryptedResourceID() now return *Element wrappers; DSTRequestHandler.getResourceID() only knows *Type. Unwrap before the call so IDPP requests are not rejected with NO_RESOURCE.
  • IDPP getXxxMap helpers (CommonName, Demographics, EmploymentIdentity, LegalIdentity, VAT) — the Modify dispatch passes *Element; the helpers test instanceof *Type. Unwrap the top-level container element so whole-container Modify applies instead of throwing invalid Element. (IDPPFacade already consumed the element form — left as-is.)
  • Discovery authorizationInsertEntryType.getResourceOffering() now returns ResourceOfferingElement, but DefaultDiscoAuthorizer casts data to ResourceOfferingType. Pass .getValue() at all three isAuthorized(...) sites: DiscoUtils (lookup) and DiscoveryService insert/remove. DiscoUtils's marshalling call (convertJAXBToElement) still receives the element and is intentionally unchanged.

P2

  • DiscoveryService.isUpdateAllowed (remove path)getDiscoEntries() returns Map<…, DiscoEntryElement>; the code cast to InsertEntryType. Now ((DiscoEntryElement) …).getValue().getResourceOffering().getValue(), fixing a ClassCastException.
  • IDFFMetaSecurityUtils.setExtendedAttributeValueconfig.getAttribute() is List<AttributeElement> (extends JAXBElement<AttributeType>); unwrap before reading AttributeType so certificate-alias updates do not ClassCastException.
  • IDPPLegalIdentity.getDataMapForSelectIDValueElement extends JAXBElement<DSTString>; unwrap before the DSTString cast for AltID/VAT IDValue updates.

9 files, +38/−7. (Also, the previous Time fix that was failing CI is now green.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

help wanted needs testing refactoring Code cleanup, refactor, dead-code or dependency removal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenAM has doubled in size since version 14

2 participants