You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@santuario.apache.org by co...@apache.org on 2020/04/17 08:01:28 UTC

svn commit: r1876653 - in /santuario/xml-security-java/trunk/src: main/java/org/apache/xml/security/utils/DOMNamespaceContext.java test/java/org/apache/xml/security/utils/ test/java/org/apache/xml/security/utils/DOMNamespaceContextTest.java

Author: coheigea
Date: Fri Apr 17 08:01:28 2020
New Revision: 1876653

URL: http://svn.apache.org/viewvc?rev=1876653&view=rev
Log:
SANTUARIO-534 - DOMNamespaceContext now implements the NamespaceContext contract correctly + settable context node. Thanks to Peter De Maeyer for the patch. This closes #23.

Added:
    santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/utils/
    santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/utils/DOMNamespaceContextTest.java
Modified:
    santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/utils/DOMNamespaceContext.java

Modified: santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/utils/DOMNamespaceContext.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/utils/DOMNamespaceContext.java?rev=1876653&r1=1876652&r2=1876653&view=diff
==============================================================================
--- santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/utils/DOMNamespaceContext.java (original)
+++ santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/utils/DOMNamespaceContext.java Fri Apr 17 08:01:28 2020
@@ -18,58 +18,125 @@
  */
 package org.apache.xml.security.utils;
 
-import java.util.HashMap;
 import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Objects;
 
 import javax.xml.namespace.NamespaceContext;
 
-import org.w3c.dom.Attr;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 
+import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
+import static javax.xml.XMLConstants.NULL_NS_URI;
+import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE;
+import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
+import static javax.xml.XMLConstants.XML_NS_PREFIX;
+import static javax.xml.XMLConstants.XML_NS_URI;
+
 /**
+ * This class adapts the {@link Node} namespace/prefix lookup API to that of {@link NamespaceContext}.
+ * There are some differences:
+ * <table>
+ *     <tr>
+ *         <th>Function</th>
+ *         <th>{@link NamespaceContext} API</th>
+ *         <th>{@link Node} API</th>
+ *     </tr>
+ *     <tr>
+ *         <td>Look up the prefix for a given namespace URI.</td>
+ *         <td>{@link NamespaceContext#getPrefix(String)}</td>
+ *         <td>{@link Node#lookupPrefix(String)}</td>
+ *     </tr>
+ *     <tr>
+ *         <td>Look up all the prefixes for a given namespace URI.</td>
+ *         <td>{@link NamespaceContext#getPrefixes(String)}</td>
+ *         <td>/</td>
+ *     </tr>
+ *     <tr>
+ *         <td>Look up the namespace URI for a given prefix.</td>
+ *         <td>{@link NamespaceContext#getNamespaceURI(String)}</td>
+ *         <td>{@link Node#lookupNamespaceURI(String)}</td>
+ *     </tr>
+ *     <tr>
+ *         <td>The default prefix.</td>
+ *         <td>{@link javax.xml.XMLConstants#DEFAULT_NS_PREFIX}</td>
+ *         <td>{@code null}</td>
+ *     </tr>
+ *     <tr>
+ *         <td>The default namespace URI.</td>
+ *         <td>{@link javax.xml.XMLConstants#NULL_NS_URI}</td>
+ *         <td>{@code null}</td>
+ *     </tr>
+ * </table>
  */
 public class DOMNamespaceContext implements NamespaceContext {
 
-    private Map<String, String> namespaceMap = new HashMap<>();
+    private Node context;
 
-    public DOMNamespaceContext(Node contextNode) {
-        addNamespaces(contextNode);
+    public DOMNamespaceContext(Node context) {
+        setContext(context);
     }
 
-    public String getNamespaceURI(String arg0) {
-        return namespaceMap.get(arg0);
+    public void setContext(Node context) {
+        this.context = context;
     }
 
-    public String getPrefix(String arg0) {
-        for (Entry<String, String> entry : namespaceMap.entrySet()) {
-            if (entry.getValue().equals(arg0)) {
-                return entry.getKey();
+    public String getNamespaceURI(String prefix) {
+        if (prefix == null) {
+            throw new IllegalArgumentException("prefix is null");
+        }
+        if (prefix.equals(DEFAULT_NS_PREFIX)) {
+            prefix = null;
+        }
+        if (context != null) {
+            String namespaceURI = context.lookupNamespaceURI(prefix);
+            if (namespaceURI != null) {
+                return namespaceURI;
             }
         }
-        return null;
-    }
-
-    public Iterator<String> getPrefixes(String arg0) {
-        return namespaceMap.keySet().iterator();
+        if (prefix == null) {
+            return NULL_NS_URI;
+        } else if (prefix.equals(XML_NS_PREFIX)) {
+            return XML_NS_URI;
+        } else if (prefix.equals(XMLNS_ATTRIBUTE)) {
+            return XMLNS_ATTRIBUTE_NS_URI;
+        }
+        return NULL_NS_URI;
     }
 
-    private void addNamespaces(Node element) {
-        if (element.getParentNode() != null) {
-            addNamespaces(element.getParentNode());
-        }
-        if (element instanceof Element) {
-            Element el = (Element)element;
-            NamedNodeMap map = el.getAttributes();
-            for (int x = 0; x < map.getLength(); x++) {
-                Attr attr = (Attr)map.item(x);
-                if ("xmlns".equals(attr.getPrefix())) {
-                    namespaceMap.put(attr.getLocalName(), attr.getValue());
-                }
+    public String getPrefix(String namespaceURI) {
+        if (namespaceURI == null) {
+            throw new IllegalArgumentException("namespace URI is null");
+        }
+        if (namespaceURI.equals(NULL_NS_URI)) {
+            namespaceURI = null;
+        }
+        if (context != null) {
+            String prefix = context.lookupPrefix(namespaceURI);
+            if (prefix != null) {
+                return prefix;
+            } else if (Objects.equals(context.lookupNamespaceURI(null), namespaceURI)) {
+                // context.lookupPrefix(namespaceURI) returns null when a namespace URI is unbound but also when it is
+                // bound to the default prefix.
+                // To distinguish the case of an unbound namespace URI from a bound one to the default prefix,
+                // we look up the namespace URI for the default prefix (null) and if it matches, we return the default
+                // prefix.
+                return DEFAULT_NS_PREFIX;
             }
         }
+        if (namespaceURI == null) {
+            return context.lookupNamespaceURI(null) != null ? null : DEFAULT_NS_PREFIX;
+        } else if (namespaceURI.equals(XML_NS_URI)) {
+            return XML_NS_PREFIX;
+        } else if (namespaceURI.equals(XMLNS_ATTRIBUTE_NS_URI)) {
+            return XMLNS_ATTRIBUTE;
+        }
+        return null;
+    }
+
+    /**
+     * Throws {@link UnsupportedOperationException}.
+     */
+    public Iterator<String> getPrefixes(String namespaceURI) {
+        throw new UnsupportedOperationException();
     }
 }

Added: santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/utils/DOMNamespaceContextTest.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/utils/DOMNamespaceContextTest.java?rev=1876653&view=auto
==============================================================================
--- santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/utils/DOMNamespaceContextTest.java (added)
+++ santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/utils/DOMNamespaceContextTest.java Fri Apr 17 08:01:28 2020
@@ -0,0 +1,137 @@
+/**
+ * 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.xml.security.utils;
+
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.DocumentTraversal;
+import org.w3c.dom.traversal.TreeWalker;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.StringReader;
+
+import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
+import static javax.xml.XMLConstants.NULL_NS_URI;
+import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE;
+import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
+import static javax.xml.XMLConstants.XML_NS_PREFIX;
+import static javax.xml.XMLConstants.XML_NS_URI;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.w3c.dom.traversal.NodeFilter.SHOW_ELEMENT;
+
+public class DOMNamespaceContextTest {
+
+    private static final DocumentBuilderFactory DEFAULT_DOCUMENT_BUILDER_FACTORY;
+
+    static {
+        DEFAULT_DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
+        DEFAULT_DOCUMENT_BUILDER_FACTORY.setNamespaceAware(true);
+    }
+
+    private static Document createDocument(String xml) throws IOException, SAXException, ParserConfigurationException {
+        return DEFAULT_DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
+    }
+
+    @Test
+    public void testUnboundDefaultNamespace() throws Exception {
+        Document document = createDocument("<root/>");
+        DOMNamespaceContext namespaceContext = new DOMNamespaceContext(document);
+        assertThrows(IllegalArgumentException.class, () -> namespaceContext.getNamespaceURI(null));
+        assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo(NULL_NS_URI)));
+        assertThat(namespaceContext.getNamespaceURI(XML_NS_PREFIX), is(equalTo(XML_NS_URI)));
+        assertThat(namespaceContext.getNamespaceURI(XMLNS_ATTRIBUTE), is(equalTo(XMLNS_ATTRIBUTE_NS_URI)));
+        assertThat(namespaceContext.getNamespaceURI("unbound-ns"), is(equalTo(NULL_NS_URI)));
+        assertThrows(IllegalArgumentException.class, () -> namespaceContext.getPrefix(null));
+        assertThat(namespaceContext.getPrefix(NULL_NS_URI), is(equalTo(DEFAULT_NS_PREFIX)));
+        assertThat(namespaceContext.getPrefix(XML_NS_URI), is(equalTo(XML_NS_PREFIX)));
+        assertThat(namespaceContext.getPrefix(XMLNS_ATTRIBUTE_NS_URI), is(equalTo(XMLNS_ATTRIBUTE)));
+        assertThat(namespaceContext.getPrefix("urn:unbound-ns"), is(nullValue()));
+    }
+
+    @Test
+    public void testBoundDefaultNamespace() throws Exception {
+        Document document = createDocument("<root xmlns='urn:ns'/>");
+        TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
+        Node root = walker.nextNode();
+        DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
+        assertThrows(IllegalArgumentException.class, () -> namespaceContext.getNamespaceURI(null));
+        assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns")));
+        assertThat(namespaceContext.getNamespaceURI(XML_NS_PREFIX), is(equalTo(XML_NS_URI)));
+        assertThat(namespaceContext.getNamespaceURI(XMLNS_ATTRIBUTE), is(equalTo(XMLNS_ATTRIBUTE_NS_URI)));
+        assertThat(namespaceContext.getNamespaceURI("unbound-ns"), is(equalTo(NULL_NS_URI)));
+        assertThrows(IllegalArgumentException.class, () -> namespaceContext.getPrefix(null));
+        assertThat(namespaceContext.getPrefix(NULL_NS_URI), is(nullValue()));
+        assertThat(namespaceContext.getPrefix("urn:ns"), is(equalTo(DEFAULT_NS_PREFIX)));
+        assertThat(namespaceContext.getPrefix(XML_NS_URI), is(equalTo(XML_NS_PREFIX)));
+        assertThat(namespaceContext.getPrefix(XMLNS_ATTRIBUTE_NS_URI), is(equalTo(XMLNS_ATTRIBUTE)));
+        assertThat(namespaceContext.getPrefix("urn:unbound-ns"), is(nullValue()));
+    }
+
+    @Test
+    public void testNamespaceInheritance() throws Exception {
+        Document document = createDocument("<root xmlns='urn:ns'><branch xmlns:ns1='urn:ns1'/></root>");
+        TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
+        Node root = walker.nextNode();
+        DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
+        assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns")));
+        assertThat(namespaceContext.getPrefix("urn:ns"), is(equalTo(DEFAULT_NS_PREFIX)));
+        assertThat(namespaceContext.getNamespaceURI("urn:ns1"), is(equalTo(DEFAULT_NS_PREFIX)));
+        assertThat(namespaceContext.getPrefix("ns1"), is(nullValue()));
+        Node branch = walker.nextNode();
+        namespaceContext.setContext(branch);
+        assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns")));
+        assertThat(namespaceContext.getPrefix("urn:ns"), is(equalTo(DEFAULT_NS_PREFIX)));
+        assertThat(namespaceContext.getNamespaceURI("ns1"), is(equalTo("urn:ns1")));
+        assertThat(namespaceContext.getPrefix("urn:ns1"), is(equalTo("ns1")));
+    }
+
+    @Test
+    public void testOverriddenDefaultNamespace() throws Exception {
+        Document document = createDocument("<root xmlns='urn:ns1'><branch xmlns='urn:ns2'/></root>");
+        TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
+        Node root = walker.nextNode();
+        DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
+        assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns1")));
+        assertThat(namespaceContext.getPrefix("urn:ns1"), is(equalTo(DEFAULT_NS_PREFIX)));
+        assertThat(namespaceContext.getPrefix("urn:ns2"), is(nullValue()));
+        Node branch = walker.nextNode();
+        namespaceContext.setContext(branch);
+        assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns2")));
+        assertThat(namespaceContext.getPrefix("urn:ns2"), is(equalTo(DEFAULT_NS_PREFIX)));
+        assertThat(namespaceContext.getPrefix("urn:ns1"), is(nullValue()));
+    }
+
+    @Test
+    public void testGetPrefixesIsUnsupported() throws Exception {
+        Document document = createDocument("<root/>");
+        TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
+        Node root = walker.nextNode();
+        DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
+        assertThrows(UnsupportedOperationException.class, () -> namespaceContext.getPrefixes(NULL_NS_URI));
+    }
+}