This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-23773 in repository https://gitbox.apache.org/repos/asf/camel.git
commit b39c915f5136e888c434f423a6130638bca36f9a Author: Claus Ibsen <[email protected]> AuthorDate: Tue Jun 16 16:00:47 2026 +0200 CAMEL-23773: camel-cxf - make ws-security optional to avoid opensaml/shibboleth dependency Make cxf-rt-ws-security optional in camel-cxf-soap so most CXF users avoid the 13+ transitive org.opensaml JARs hosted on the unreliable build.shibboleth.net repository. Extract wss4j-dependent cert extraction into WsSecurityHelper (loaded only when wss4j is on the classpath). Exclude org.opensaml from both camel-cxf-soap and camel-cxf-spring-soap. Remove the now-unnecessary .mvn/rrf/groupId-B_shibboleth.txt filter. Co-Authored-By: Claude <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .mvn/rrf/groupId-B_shibboleth.txt | 3 - components/camel-cxf/camel-cxf-soap/pom.xml | 7 ++ .../component/cxf/jaxws/DefaultCxfBinding.java | 85 +++------------ .../component/cxf/jaxws/WsSecurityHelper.java | 115 +++++++++++++++++++++ components/camel-cxf/camel-cxf-spring-soap/pom.xml | 6 ++ .../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc | 22 ++++ 6 files changed, 167 insertions(+), 71 deletions(-) diff --git a/.mvn/rrf/groupId-B_shibboleth.txt b/.mvn/rrf/groupId-B_shibboleth.txt deleted file mode 100644 index 0a644833082c..000000000000 --- a/.mvn/rrf/groupId-B_shibboleth.txt +++ /dev/null @@ -1,3 +0,0 @@ -net.shibboleth -net.shibboleth.utilities -org.opensaml diff --git a/components/camel-cxf/camel-cxf-soap/pom.xml b/components/camel-cxf/camel-cxf-soap/pom.xml index 87135ea35e78..d7b24157d9bb 100644 --- a/components/camel-cxf/camel-cxf-soap/pom.xml +++ b/components/camel-cxf/camel-cxf-soap/pom.xml @@ -194,6 +194,13 @@ <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>${cxf-version}</version> + <optional>true</optional> + <exclusions> + <exclusion> + <groupId>org.opensaml</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> </dependency> diff --git a/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java index 591f7974c6c1..cbe2af5c8d90 100644 --- a/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java +++ b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java @@ -95,9 +95,6 @@ import org.apache.cxf.service.model.BindingOperationInfo; import org.apache.cxf.service.model.MessagePartInfo; import org.apache.cxf.service.model.OperationInfo; import org.apache.cxf.staxutils.StaxUtils; -import org.apache.wss4j.dom.engine.WSSecurityEngineResult; -import org.apache.wss4j.dom.handler.WSHandlerConstants; -import org.apache.wss4j.dom.handler.WSHandlerResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -389,82 +386,34 @@ public class DefaultCxfBinding implements CxfBinding, HeaderFilterStrategyAware } } + private static final boolean WSS4J_AVAILABLE; + static { + boolean available; + try { + Class.forName("org.apache.wss4j.dom.handler.WSHandlerConstants"); + available = true; + } catch (ClassNotFoundException e) { + available = false; + } + WSS4J_AVAILABLE = available; + } + private static void addInboundX509CertificatesToSubject(Message cxfMessage, Subject subject) { - if (cxfMessage == null || subject == null) { + if (!WSS4J_AVAILABLE || cxfMessage == null || subject == null) { return; } - // If it’s read-only, don’t break the route; just skip. if (subject.isReadOnly()) { return; } - final Object recv = cxfMessage.get(WSHandlerConstants.RECV_RESULTS); - if (recv == null) { + Collection<X509Certificate> certs; + try { + certs = WsSecurityHelper.extractCertificates(cxfMessage); + } catch (NoClassDefFoundError e) { return; } - // We only need the cert objects. - Collection<X509Certificate> certs = null; - - if (recv instanceof Map<?, ?> map) { - Object v = map.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES); - - if (v instanceof Collection<?> coll) { - certs = new ArrayList<>(); - for (Object o : coll) { - if (o instanceof X509Certificate cert) { - certs.add(cert); - } else if (o instanceof X509Certificate[] arr) { - for (X509Certificate c : arr) { - if (c != null) { - certs.add(c); - } - } - } - } - } else if (v instanceof X509Certificate[] arr) { - certs = new ArrayList<>(); - for (X509Certificate c : arr) { - if (c != null) { - certs.add(c); - } - } - } - - } else if (recv instanceof List<?> list) { - // Typical CXF case: List<WSHandlerResult> - if (!list.isEmpty() && list.get(0) instanceof WSHandlerResult) { - certs = new ArrayList<>(); - for (Object hrObj : list) { - if (!(hrObj instanceof WSHandlerResult hr)) { - continue; - } - for (WSSecurityEngineResult r : hr.getResults()) { - Object v = r.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES); - - if (v instanceof X509Certificate[] arr) { - for (X509Certificate c : arr) { - if (c != null) { - certs.add(c); - } - } - } else if (v instanceof X509Certificate cert) { - certs.add(cert); - } else { - Object leaf = r.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE); - if (leaf instanceof X509Certificate cert) { - certs.add(cert); - } - } - } - } - if (certs.isEmpty()) { - certs = null; - } - } - } - if (certs == null || certs.isEmpty()) { return; } diff --git a/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/WsSecurityHelper.java b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/WsSecurityHelper.java new file mode 100644 index 000000000000..8942747c743c --- /dev/null +++ b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/WsSecurityHelper.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.cxf.jaxws; + +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.message.Message; +import org.apache.wss4j.dom.engine.WSSecurityEngineResult; +import org.apache.wss4j.dom.handler.WSHandlerConstants; +import org.apache.wss4j.dom.handler.WSHandlerResult; + +/** + * Extracts X.509 certificates from WS-Security processing results. This class is isolated so that + * {@link DefaultCxfBinding} does not require wss4j on the classpath — it is only loaded when wss4j is present. + */ +final class WsSecurityHelper { + + private WsSecurityHelper() { + } + + static Collection<X509Certificate> extractCertificates(Message cxfMessage) { + final Object recv = cxfMessage.get(WSHandlerConstants.RECV_RESULTS); + if (recv == null) { + return null; + } + + Collection<X509Certificate> certs = null; + + if (recv instanceof Map<?, ?> map) { + certs = extractFromMap(map); + } else if (recv instanceof List<?> list) { + certs = extractFromHandlerResults(list); + } + + return certs; + } + + private static Collection<X509Certificate> extractFromMap(Map<?, ?> map) { + Object v = map.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES); + + Collection<X509Certificate> certs = null; + if (v instanceof Collection<?> coll) { + certs = new ArrayList<>(); + for (Object o : coll) { + if (o instanceof X509Certificate cert) { + certs.add(cert); + } else if (o instanceof X509Certificate[] arr) { + for (X509Certificate c : arr) { + if (c != null) { + certs.add(c); + } + } + } + } + } else if (v instanceof X509Certificate[] arr) { + certs = new ArrayList<>(); + for (X509Certificate c : arr) { + if (c != null) { + certs.add(c); + } + } + } + return certs; + } + + private static Collection<X509Certificate> extractFromHandlerResults(List<?> list) { + if (list.isEmpty() || !(list.get(0) instanceof WSHandlerResult)) { + return null; + } + + Collection<X509Certificate> certs = new ArrayList<>(); + for (Object hrObj : list) { + if (!(hrObj instanceof WSHandlerResult hr)) { + continue; + } + for (WSSecurityEngineResult r : hr.getResults()) { + Object v = r.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES); + + if (v instanceof X509Certificate[] arr) { + for (X509Certificate c : arr) { + if (c != null) { + certs.add(c); + } + } + } else if (v instanceof X509Certificate cert) { + certs.add(cert); + } else { + Object leaf = r.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE); + if (leaf instanceof X509Certificate cert) { + certs.add(cert); + } + } + } + } + return certs.isEmpty() ? null : certs; + } +} diff --git a/components/camel-cxf/camel-cxf-spring-soap/pom.xml b/components/camel-cxf/camel-cxf-spring-soap/pom.xml index 550f9aba5118..5b4be84a6dd3 100644 --- a/components/camel-cxf/camel-cxf-spring-soap/pom.xml +++ b/components/camel-cxf/camel-cxf-spring-soap/pom.xml @@ -192,6 +192,12 @@ <artifactId>cxf-rt-ws-security</artifactId> <version>${cxf-version}</version> <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.opensaml</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc index 1d28db3c3884..7f6cb3af3851 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc @@ -824,6 +824,28 @@ The same pattern applies to HTTP-based bridges (`platform-http`/`jetty`/`netty -http`/`http` -> `cxf:`) and any other transport whose default `HeaderFilterStrategy` filters `Camel*` headers. +==== WS-Security dependency is now optional + +The `cxf-rt-ws-security` dependency in `camel-cxf-soap` is now **optional**. Most Camel CXF users do +not need WS-Security / SAML support, yet the dependency transitively pulled in 13+ `org.opensaml` +JARs hosted on a third-party Maven repository (`build.shibboleth.net`) that has proven unreliable, +causing intermittent CI and build failures. + +If your project uses WS-Security features (signatures, encryption, username tokens, SAML), you will +need to add `cxf-rt-ws-security` explicitly to your project's POM: + +[source,xml] +---- +<dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-ws-security</artifactId> +</dependency> +---- + +If you also need OpenSAML (for SAML-based WS-Security), add the `org.opensaml` dependencies +explicitly and configure the Shibboleth Maven repository (`https://build.shibboleth.net/nexus/content/repositories/releases`) +in your project's POM or Maven `settings.xml`. + === camel-dns - potential breaking change The Exchange header constants in `DnsConstants` have been renamed to follow the
