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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 18948157d10f CAMEL-23773: camel-cxf - make ws-security optional to 
avoid opensaml/shibboleth dependency (#24054)
18948157d10f is described below

commit 18948157d10fd59c0cd6209fbf3407678cd468b6
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Jun 16 17:49:39 2026 +0200

    CAMEL-23773: camel-cxf - make ws-security optional to avoid 
opensaml/shibboleth dependency (#24054)
    
    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.
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-authored-by: Claude <[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

Reply via email to