Skip to content

Commit

Permalink
665: Can't load JSON schemas with URN value in id field (#906)
Browse files Browse the repository at this point in the history
* 665: Can't load JSON schemas with URN value in id field

* Better handling of URNs
  • Loading branch information
martin-sladecek authored Dec 8, 2023
1 parent 97b4cba commit b138dc6
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 4 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<version>1.0.87</version>
<version>1.0.88</version>
<packaging>bundle</packaging>
<name>JsonSchemaValidator</name>
<description>A json schema validator that supports draft v4, v6, v7, v2019-09 and v2020-12</description>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/networknt/schema/JsonSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public JsonNode getRefSchemaNode(String ref) {
if (node.isMissingNode()) {
node = handleNullNode(ref, schema);
}
} else if (ref.startsWith("#") && ref.length() > 1) {
} else if ((ref.startsWith("#") && ref.length() > 1) || (ref.startsWith("urn:") && ref.length() > 4)) {
node = this.metaSchema.getNodeByFragmentRef(ref, node);
if (node == null) {
node = handleNullNode(ref, schema);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/networknt/schema/JsonSchemaFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public Builder() {
for (final String scheme : URLFactory.SUPPORTED_SCHEMES) {
this.uriFactoryMap.put(scheme, urlFactory);
}
// Adds support for creating URNs.
this.uriFactoryMap.put(URNURIFactory.SCHEME, new URNURIFactory());

// Adds support for fetching with {@link URL}s.
final URIFetcher urlFetcher = new URLFetcher();
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/networknt/schema/RefValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.CollectorContext.Scope;
import com.networknt.schema.uri.URIFactory;
import com.networknt.schema.uri.URNURIFactory;
import com.networknt.schema.urn.URNFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -35,6 +36,7 @@ public class RefValidator extends BaseJsonValidator {
private JsonSchema parentSchema;

private static final String REF_CURRENT = "#";
private static final String URN_SCHEME = URNURIFactory.SCHEME;

public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.REF, validationContext);
Expand Down Expand Up @@ -78,6 +80,12 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
if (schemaUri == null) {
return null;
}
} else if (URN_SCHEME.equals(schemaUri.getScheme())) {
// Try to resolve URN schema as a JsonSchemaRef to some sub-schema of the parent
JsonSchemaRef ref = getJsonSchemaRef(parent, validationContext, schemaUri.toString(), refValueOriginal);
if (ref != null) {
return ref;
}
}

// This should retrieve schemas regardless of the protocol that is in the uri.
Expand All @@ -91,6 +99,13 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
if (refValue.equals(REF_CURRENT)) {
return new JsonSchemaRef(parent.findAncestor());
}
return getJsonSchemaRef(parent, validationContext, refValue, refValueOriginal);
}

private static JsonSchemaRef getJsonSchemaRef(JsonSchema parent,
ValidationContext validationContext,
String refValue,
String refValueOriginal) {
JsonNode node = parent.getRefSchemaNode(refValue);
if (node != null) {
JsonSchemaRef ref = validationContext.getReferenceParsingInProgress(refValueOriginal);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/networknt/schema/uri/URLFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
*/
public final class URLFactory implements URIFactory {
// These supported schemes are defined in {@link #URL(String, String, int, String)}.
public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet<String>(
public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("http", "https", "ftp", "file", "jar")));

/**
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/networknt/schema/uri/URLFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
Expand All @@ -32,7 +34,7 @@ public final class URLFetcher implements URIFetcher {

// These supported schemes are defined in {@link #URL(String, String, int, String)}.
// This fetcher also supports the {@link URL}s created with the {@link ClasspathURIFactory}.
public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(URLFactory.SUPPORTED_SCHEMES);
public static final Set<String> SUPPORTED_SCHEMES = URLFactory.SUPPORTED_SCHEMES;

/**
* {@inheritDoc}
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/networknt/schema/uri/URNURIFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.networknt.schema.uri;

import java.net.URI;
import java.util.Collections;
import java.util.Set;

/**
* A URIFactory that handles "urn" scheme of {@link URI}s.
*/
public final class URNURIFactory implements URIFactory {

public static final String SCHEME = "urn";

@Override
public URI create(final String uri) {
try {
return URI.create(uri);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Unable to create URI.", e);
}
}

@Override
public URI create(final URI baseURI, final String segment) {
String urnPart = baseURI.getRawSchemeSpecificPart();
int pos = urnPart.indexOf(':');
String namespace = pos < 0 ? urnPart : urnPart.substring(0, pos);
return URI.create(SCHEME + ":" + namespace + ":" + segment);
}
}
49 changes: 49 additions & 0 deletions src/test/java/com/networknt/schema/Issue665Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.networknt.schema;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.util.Collections;
import java.util.Set;

public class Issue665Test extends BaseJsonSchemaValidatorTest {

@Test
void testUrnUriAsLocalRef() throws IOException {
JsonSchema schema = getJsonSchemaFromClasspath("draft7/urn/issue665.json", SpecVersion.VersionFlag.V7);
Assertions.assertNotNull(schema);
Assertions.assertDoesNotThrow(schema::initializeValidators);
Set<ValidationMessage> messages = schema.validate(getJsonNodeFromStringContent(
"{\"myData\": {\"value\": \"hello\"}}"));
Assertions.assertEquals(messages, Collections.emptySet());
}

@Test
void testUrnUriAsLocalRef_ExternalURN() {
JsonSchemaFactory factory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
.uriFetcher(uri -> uri.equals(URI.create("urn:data"))
? Thread.currentThread().getContextClassLoader()
.getResourceAsStream("draft7/urn/issue665_external_urn_subschema.json")
: null,
"urn")
.build();

try (InputStream is = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("draft7/urn/issue665_external_urn_ref.json")) {
JsonSchema schema = factory.getSchema(is);
Assertions.assertNotNull(schema);
Assertions.assertDoesNotThrow(schema::initializeValidators);
Set<ValidationMessage> messages = schema.validate(getJsonNodeFromStringContent(
"{\"myData\": {\"value\": \"hello\"}}"));
Assertions.assertEquals(messages, Collections.emptySet());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

}
26 changes: 26 additions & 0 deletions src/test/resources/draft7/urn/issue665.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"myData"
],
"properties": {
"myData": {
"$ref": "urn:data"
}
},
"definitions": {
"data": {
"$id": "urn:data",
"type": "object",
"required": [
"value"
],
"properties": {
"value": {
"type": "string"
}
}
}
}
}
12 changes: 12 additions & 0 deletions src/test/resources/draft7/urn/issue665_external_urn_ref.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"myData"
],
"properties": {
"myData": {
"$ref": "urn:data"
}
}
}
13 changes: 13 additions & 0 deletions src/test/resources/draft7/urn/issue665_external_urn_subschema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "urn:data",
"type": "object",
"required": [
"value"
],
"properties": {
"value": {
"type": "string"
}
}
}

0 comments on commit b138dc6

Please sign in to comment.