You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/06/16 14:01:10 UTC

(camel) branch fix/CAMEL-23773 created (now b39c915f5136)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a change to branch fix/CAMEL-23773
in repository https://gitbox.apache.org/repos/asf/camel.git


      at b39c915f5136 CAMEL-23773: camel-cxf - make ws-security optional to avoid opensaml/shibboleth dependency

This branch includes the following new commits:

     new b39c915f5136 CAMEL-23773: camel-cxf - make ws-security optional to avoid opensaml/shibboleth dependency

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(camel) 01/01: CAMEL-23773: camel-cxf - make ws-security optional to avoid opensaml/shibboleth dependency

Posted by da...@apache.org.
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 <cl...@gmail.com>
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 <no...@anthropic.com>
    Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
 .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