Validate parsed QName to identity 43/68543/4
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 22 Feb 2018 14:01:52 +0000 (15:01 +0100)
committerRobert Varga <nite@hq.sk>
Mon, 5 Mar 2018 10:47:42 +0000 (10:47 +0000)
We need to validate if parsed QName refers to an existing identity
before allowing it in Identityref codecs. Introduce IdentityCodecUtil
along with a test suite and migrate users over.

JIRA: YANGTOOLS-846
Change-Id: I673ae6df4406110202b86329121a3ca9673fb0ed
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/IdentityrefJSONCodec.java
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/NormalizedNodeToJsonStreamTest.java
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java
yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-augmentation-in-choice-in-container.json
yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-external-augmentation-in-choice-in-container.json
yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java
yang/yang-data-util/pom.xml
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtil.java [new file with mode: 0644]
yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtilTest.java [new file with mode: 0644]
yang/yang-data-util/src/test/resources/yangtools846.yang [new file with mode: 0644]

index 088e8ea4bac9288d3d790bf3035f602712cb4827..6042a46dd16764554d857d983e685163745998ac 100644 (file)
@@ -16,6 +16,7 @@ import java.util.Iterator;
 import java.util.Optional;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil;
 import org.opendaylight.yangtools.yang.data.util.codec.QNameCodecUtil;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -36,7 +37,7 @@ final class IdentityrefJSONCodec implements JSONCodec<QName> {
 
     @Override
     public QName parseValue(final Object ctx, final String value) {
-        return QNameCodecUtil.decodeQName(value, prefix -> {
+        return IdentityCodecUtil.parseIdentity(value, schemaContext, prefix -> {
             if (prefix.isEmpty()) {
                 return parentModule;
             }
@@ -44,7 +45,7 @@ final class IdentityrefJSONCodec implements JSONCodec<QName> {
             final Iterator<Module> modules = schemaContext.findModules(prefix).iterator();
             checkArgument(modules.hasNext(), "Could not find module %s", prefix);
             return modules.next().getQNameModule();
-        });
+        }).getQName();
     }
 
     /**
index a5e0781b65d105505002832f6258780d70c59eee..20373bb85a3fb5527515dc5ff5d7b7b362c30787 100644 (file)
@@ -212,7 +212,7 @@ public class NormalizedNodeToJsonStreamTest {
         assertEquals("lf15_21 value", lf15_21.getAsString());
         assertEquals("lf13 value", lf13.getAsString());
         assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
-        assertEquals("complexjson:lf11", lf15_12.getAsString());
+        assertEquals("complexjson:ident", lf15_12.getAsString());
     }
 
     /**
@@ -249,7 +249,7 @@ public class NormalizedNodeToJsonStreamTest {
         assertEquals("lf15_12 value from augmentation", lf15_12Augment.getAsString());
         assertEquals("lf13 value", lf13.getAsString());
         assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
-        assertEquals("complexjson:lf11", lf15_12.getAsString());
+        assertEquals("complexjson:ident", lf15_12.getAsString());
     }
 
     /**
index fd4f5684847f5c94d79f302395024ab312679fb3..b1e7f67f0fea603c62f554e0959f32bc0e22f256 100644 (file)
@@ -152,7 +152,7 @@ public final class TestingNormalizedNodeStructuresCreator {
     private static LeafNode<Object> lf15_12Node() {
         return Builders.leafBuilder()
                 .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf15_12")))
-                .withValue(QName.create("ns:complex:json", "2014-08-11", "lf11")).build();
+                .withValue(QName.create("ns:complex:json", "2014-08-11", "ident")).build();
     }
 
     private static LeafNode<Object> lf15_11Node() {
index beb5d5cd4c06a0a93fc7aaf34fdf2edbb92d9986..fc9898e18f88b83622b1285c214496ca676e273e 100644 (file)
@@ -3,6 +3,6 @@
         "lf15_11" : "one two",
         "lf13" : "lf13 value",
         "lf15_21" : "lf15_21 value",
-        "lf15_12" : "complexjson:lf11"
+        "lf15_12" : "complexjson:ident"
     }
 }
index 4dffc9c8f7e00725ac6b463e2142c823e3e5ac1d..d8d81dcc142b65743121c400313cad445810458f 100644 (file)
@@ -4,6 +4,6 @@
         "lf15_11" : "one two",
         "lf13" : "lf13 value",
         "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
-        "lf15_12" : "complexjson:lf11"
+        "lf15_12" : "complexjson:ident"
     }
 }
index 355e2c2cff44e7f9f4ab80f10e2fddc9975652d4..068a2bdda4f6cd371447f9bbca8f1334749b0fe9 100644 (file)
@@ -29,7 +29,7 @@
         "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation",
         "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
         "lf15_11" : "one two",
-        "lf15_12" : "complexjson:lf11",
+        "lf15_12" : "complexjson:ident",
         "lf15_21" : "lf15_21 value",
         "lf17" : "lf17 value",
 
index dd36e738deaa06de79af438edd774ab2c853b7a6..87623bc9967ff4bfb2b6cf620309910bb7dc96e2 100644 (file)
@@ -5,7 +5,6 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.yangtools.yang.data.codec.xml;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -19,6 +18,7 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil;
 import org.opendaylight.yangtools.yang.data.util.codec.QNameCodecUtil;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -39,7 +39,7 @@ final class IdentityrefXmlCodec implements XmlCodec<QName> {
 
     @Override
     public QName parseValue(final NamespaceContext ctx, final String str) {
-        return QNameCodecUtil.decodeQName(str, prefix -> {
+        return IdentityCodecUtil.parseIdentity(str, schemaContext, prefix -> {
             if (prefix.isEmpty()) {
                 return parentModule;
             }
@@ -48,7 +48,7 @@ final class IdentityrefXmlCodec implements XmlCodec<QName> {
             final Iterator<Module> modules = schemaContext.findModules(URI.create(prefixedNS)).iterator();
             checkArgument(modules.hasNext(), "Could not find module for namespace %s", prefixedNS);
             return modules.next().getQNameModule();
-        });
+        }).getQName();
     }
 
     @Override
index d15e02815de16f028638967911137d361e6cc6d6..1618da9e73a29439f76442b1cb35a70d9e63e4a8 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>yang-model-util</artifactId>
         </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-test-util</artifactId>
+        </dependency>
     </dependencies>
 
        <build>
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtil.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtil.java
new file mode 100644 (file)
index 0000000..be001cd
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.util.codec;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.annotation.concurrent.ThreadSafe;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Utility methods for implementing string-to-identity codecs.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+@ThreadSafe
+public final class IdentityCodecUtil {
+
+    private IdentityCodecUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Parse a string into a QName using specified prefix-to-QNameModule mapping function, interpreting the result
+     * as an IdentitySchemaNode existing in specified SchemaContext.
+     *
+     * @param value string value to parse
+     * @param schemaContext Parent schema context
+     * @param prefixToModule prefix-to-QNameModule mapping function
+     * @return Corresponding IdentitySchemaNode.
+     * @throws IllegalArgumentException if the value is invalid or does not refer to an existing identity
+     */
+    public static IdentitySchemaNode parseIdentity(final String value, final SchemaContext schemaContext,
+            final Function<String, QNameModule> prefixToModule) {
+        final QName qname = QNameCodecUtil.decodeQName(value, prefixToModule);
+        final Optional<Module> optModule = schemaContext.findModule(qname.getModule());
+        checkState(optModule.isPresent(), "Parsed QName %s refers to a non-existent module", qname);
+
+        for (IdentitySchemaNode identity : optModule.get().getIdentities()) {
+            if (qname.equals(identity.getQName())) {
+                return identity;
+            }
+        }
+
+        throw new IllegalArgumentException("Parsed QName " + qname + " does not refer to a valid identity");
+    }
+}
diff --git a/yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtilTest.java b/yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtilTest.java
new file mode 100644 (file)
index 0000000..0212361
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.util.codec;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class IdentityCodecUtilTest {
+    private static final QNameModule MODULE = QNameModule.create(URI.create("yangtools846"));
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void init() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResource("/yangtools846.yang");
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void testCorrectInput() {
+        assertNotNull(IdentityCodecUtil.parseIdentity("yt846:foo", SCHEMA_CONTEXT,
+            IdentityCodecUtilTest::resolvePrefix));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNonExistent() {
+        IdentityCodecUtil.parseIdentity("yt846:bar", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEmptyLocalName() {
+        IdentityCodecUtil.parseIdentity("yt846:", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testEmptyString() {
+        IdentityCodecUtil.parseIdentity("", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNoPrefix() {
+        IdentityCodecUtil.parseIdentity("foo", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testEmptyPrefix() {
+        IdentityCodecUtil.parseIdentity(":foo", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testColon() {
+        IdentityCodecUtil.parseIdentity(":", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+
+    private static QNameModule resolvePrefix(final String prefix) {
+        // TODO: QNameCodecUtil should deal with some of the malformed stuff here by throwing IAE. We throw an ISE
+        //       to discern what is happening.
+        checkState("yt846".equals(prefix), "Unexpected prefix %s", prefix);
+        return MODULE;
+    }
+}
diff --git a/yang/yang-data-util/src/test/resources/yangtools846.yang b/yang/yang-data-util/src/test/resources/yangtools846.yang
new file mode 100644 (file)
index 0000000..4e577c8
--- /dev/null
@@ -0,0 +1,7 @@
+module yangtools846 {
+    namespace "yangtools846";
+    prefix yt846;
+
+    identity foo;
+}
+