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

reta pushed a commit to branch 3.6.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/3.6.x-fixes by this push:
     new 12a35866d4d [CXF-9205] Remove SOAP Header attribute duplications to 
its child el. (#2960)
12a35866d4d is described below

commit 12a35866d4dfa7faf142590b254060657ac2df20
Author: jrihtarsic <[email protected]>
AuthorDate: Tue Mar 17 12:42:33 2026 +0100

    [CXF-9205] Remove SOAP Header attribute duplications to its child el. 
(#2960)
    
    * [CXF-9205] Remove SOAP Header attribute duplications to its child elements
    
    * [CXF-9205] Add unit tests for the fix
    
    * [CXF-9205] Add message property to control the  SOAP Header attribute 
duplications to its child elements
    
    ---------
    
    Co-authored-by: Joze RIHTARSIC <[email protected]>
    (cherry picked from commit 3c24fd99050aab17c670bead72a40bfd65e7aa13)
---
 .../soap/interceptor/ReadHeadersInterceptor.java   | 58 +++++++++++------
 .../interceptor/ReadHeadersInterceptorTest.java    | 73 +++++++++++++++++++---
 2 files changed, 102 insertions(+), 29 deletions(-)

diff --git 
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
 
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
index 02d151ba7a3..7376c059e69 100644
--- 
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
+++ 
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
@@ -73,6 +73,9 @@ public class ReadHeadersInterceptor extends 
AbstractSoapInterceptor {
     public static final String BODY_EVENTS = "body.events";
     public static final String ENVELOPE_PREFIX = "envelope.prefix";
     public static final String BODY_PREFIX = "body.prefix";
+    private static final String SOAP_ADD_NAMESPACE_CONTEXT = 
"org.apache.cxf.binding.soap.addNamespaceContext";
+    private static final String SOAP_PROPAGATE_ATTRIBUTES = 
"org.apache.cxf.binding.soap.propagateHeaderAttributes";
+
     /**
      *
      */
@@ -195,7 +198,7 @@ public class ReadHeadersInterceptor extends 
AbstractSoapInterceptor {
                 } else {
                     final boolean addNC =
                         MessageUtils.getContextualBoolean(
-                            message, 
"org.apache.cxf.binding.soap.addNamespaceContext", false);
+                            message, SOAP_ADD_NAMESPACE_CONTEXT, false);
                     Map<String, String> bodyNC = addNC ? new HashMap<String, 
String>() : null;
                     if (addNC) {
                         // add the Envelope-Level declarations
@@ -230,26 +233,15 @@ public class ReadHeadersInterceptor extends 
AbstractSoapInterceptor {
                     soapBody = DOMUtils.getChildrenWithName(element,
                                                                                
  body.getNamespaceURI(),
                                                                                
  body.getLocalPart());
-                    
-                    for (Element elem : elemList) {
+
+                final boolean copyParentAttributes = 
MessageUtils.getContextualBoolean(
+                            message, SOAP_PROPAGATE_ATTRIBUTES, false);
+
+                for (Element elem : elemList) {
                         Element hel = DOMUtils.getFirstElement(elem);
                         while (hel != null) {
-                            // Need to add any attributes that are present on 
the parent element
-                            // which otherwise would be lost.
-                            if (elem.hasAttributes()) {
-                                NamedNodeMap nnp = elem.getAttributes();
-                                for (int ct = 0; ct < nnp.getLength(); ct++) {
-                                    Node attr = nnp.item(ct);
-                                    Node headerAttrNode = hel.hasAttributes() 
? hel.getAttributes()
-                                        
.getNamedItemNS(attr.getNamespaceURI(), attr.getLocalName()) : null;
-
-                                    if (headerAttrNode == null) {
-                                        Attr attribute = hel.getOwnerDocument()
-                                            
.createAttributeNS(attr.getNamespaceURI(), attr.getNodeName());
-                                        
attribute.setNodeValue(attr.getNodeValue());
-                                        hel.setAttributeNodeNS(attribute);
-                                    }
-                                }
+                            if (elem.hasAttributes() && copyParentAttributes){
+                                propagateHeaderAttributes(elem, hel);
                             }
 
                             HeaderProcessor p = bus == null ? null : 
bus.getExtension(HeaderManager.class)
@@ -312,6 +304,34 @@ public class ReadHeadersInterceptor extends 
AbstractSoapInterceptor {
             }
         }
     }
+
+    /**
+     * Copies the attributes of the SOAP header element to its child elements, 
if
+     * those attributes are not already present on the child.
+     *
+     * This method is retained for backward‑compatibility and should not be 
used
+     * in new code, as it invalidates the signatures on the child elements.
+     *
+     * @param soapHeaderEl the SOAP header element
+     * @param child the child element of the SOAP header element
+     */
+    private static void propagateHeaderAttributes(Element soapHeaderEl, 
Element child) {
+        if (soapHeaderEl.hasAttributes()) {
+            NamedNodeMap nnp = soapHeaderEl.getAttributes();
+            for (int ct = 0; ct < nnp.getLength(); ct++) {
+                Node attr = nnp.item(ct);
+                Node headerAttrNode = child.hasAttributes() ? 
child.getAttributes()
+                    .getNamedItemNS(attr.getNamespaceURI(), 
attr.getLocalName()) : null;
+
+                if (headerAttrNode == null) {
+                    Attr attribute = child.getOwnerDocument()
+                        .createAttributeNS(attr.getNamespaceURI(), 
attr.getNodeName());
+                    attribute.setNodeValue(attr.getNodeValue());
+                    child.setAttributeNodeNS(attribute);
+                }
+            }
+        }
+    }
     //CHECKSTYLE:ON
 
     private void addCurrentNamespaceDecls(XMLStreamReader xmlReader, 
Map<String, String> bodyNsMap) {
diff --git 
a/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
 
b/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
index e4b097f5555..906d42a28b3 100644
--- 
a/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
+++ 
b/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
@@ -20,17 +20,26 @@
 package org.apache.cxf.binding.soap.interceptor;
 
 import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 import javax.xml.stream.XMLStreamReader;
 
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+
 import org.apache.cxf.binding.soap.Soap11;
 import org.apache.cxf.binding.soap.SoapMessage;
+import org.apache.cxf.headers.Header;
 import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.staxutils.StaxUtils;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -39,42 +48,86 @@ import static org.junit.Assert.assertNull;
 /**
  *
  */
+@RunWith(Parameterized.class)
 public class ReadHeadersInterceptorTest {
     private static final byte[] TEST_SOAP =
         ("<soap:Envelope 
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"
             + " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
-            + " xmlns:xs='http://www.w3.org/2001/XMLSchema' 
xmlns:bar='tmp:bar'>"
+            + " xmlns:xs='http://www.w3.org/2001/XMLSchema' 
xmlns:bar='tmp:bar'"
+            + " xmlns:x='http://example.com/schema'>"
+            + "<soap:Header testAttr='testValue'>"
+            + "<x:AuthToken>Token</x:AuthToken>"
+            + "</soap:Header>"
             + "<soap:Body>"
             + "<ns2:payload xmlns:ns2='urn:tmp:foo'/>"
             + "</soap:Body>"
             + "</soap:Envelope>").getBytes();
 
+    private final Object attributeValue;
+    private final int expectedTransferredAttributeCount;
+    private final boolean expectedAddNSContext;
     private ReadHeadersInterceptor interceptor;
 
+    public ReadHeadersInterceptorTest(Object attribute, int expectedCount, 
boolean expectedAddNSContext) {
+        this.attributeValue = attribute;
+        this.expectedTransferredAttributeCount = expectedCount;
+        this.expectedAddNSContext = expectedAddNSContext;
+    }
+
+    @Parameterized.Parameters(name = "{index}: attribute={0}, 
expectedCount={1}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+            {"false", 0, false},
+            {"true", 1, true},
+            {"TRUE", 1, true},
+            {true, 1, true},
+            {false, 0, false},
+            {null, 0, false},
+            {"", 0, false}
+        });
+    }
+
     @Before
     public void setUp() {
         interceptor = new ReadHeadersInterceptor(null);
     }
 
     @Test
-    public void testNotAddNSContext() throws Exception {
+    public void testAddNSContext() throws Exception {
         SoapMessage message = setUpMessage();
+        message.put("org.apache.cxf.binding.soap.addNamespaceContext", 
attributeValue);
         interceptor.handleMessage(message);
         Map<String, String> nsc = CastUtils.cast((Map<?, 
?>)message.get("soap.body.ns.context"));
-        assertNull(nsc);
+        if (expectedAddNSContext) {
+            assertNotNull(nsc);
+            assertEquals("http://www.w3.org/2001/XMLSchema-instance";, 
nsc.get("xsi"));
+            assertEquals("http://www.w3.org/2001/XMLSchema";, nsc.get("xs"));
+            assertEquals("tmp:bar", nsc.get("bar"));
+        } else {
+            assertNull(nsc);
+        }
     }
 
+    /**
+     * Test to verify that SOAP header attributes are not transferred to child 
elements of the header.
+     * See CXF-9205 for more details.
+     *
+     * @throws Exception may be thrown during test execution
+     */
     @Test
-    public void testAddNSContext() throws Exception {
+    public void testHeaderAttributesTransferredToChildren() throws Exception {
         SoapMessage message = setUpMessage();
-        message.put("org.apache.cxf.binding.soap.addNamespaceContext", "true");
+        message.put("org.apache.cxf.binding.soap.propagateHeaderAttributes", 
attributeValue);
         interceptor.handleMessage(message);
-        Map<String, String> nsc = CastUtils.cast((Map<?, 
?>)message.get("soap.body.ns.context"));
-        assertNotNull(nsc);
-        assertEquals("http://www.w3.org/2001/XMLSchema-instance";, 
nsc.get("xsi"));
-        assertEquals("http://www.w3.org/2001/XMLSchema";, nsc.get("xs"));
-        assertEquals("tmp:bar", nsc.get("bar"));
+        List<Header> cxfHeaders = message.getHeaders();
+        assertEquals(1, cxfHeaders.size());
+        Element headerElement = (Element) cxfHeaders.get(0).getObject();
+        NamedNodeMap attributes = headerElement.getAttributes();
 
+        assertEquals("AuthToken", headerElement.getLocalName());
+        assertEquals("http://example.com/schema";, 
headerElement.getNamespaceURI());
+        // The test SOAP header attribute "testAttr", must not be transferred 
to child element "AuthToken"
+        assertEquals(expectedTransferredAttributeCount, 
attributes.getLength());
     }
 
     private SoapMessage setUpMessage() throws Exception {

Reply via email to