b/161370934 Create extension schema validator with validation for three extensions Change-Id: I7aeed2ce1248605a68d27c482a2d58b95bccfe7b
diff --git a/build.gradle b/build.gradle index ea3e4a3..21d1f75 100644 --- a/build.gradle +++ b/build.gradle
@@ -48,7 +48,9 @@ // OpenAPI Libraries "openapiParser": "1.0.1", // 23 June 2020 "openapiCore": "1.0.1", // 23 June 2020 - "openapiSchemaValidator": "1.0.2" // 7 July 2020 + + // Json Libraries + "jsonSchemaValidator": "1.0.42" // 13 July 2020 ] allprojects {
diff --git a/oas-core/build.gradle b/oas-core/build.gradle index 25acf9d..ff16ae4 100644 --- a/oas-core/build.gradle +++ b/oas-core/build.gradle
@@ -2,6 +2,8 @@ implementation "org.openapi4j:openapi-parser:${libVersions.openapiParser}" implementation "org.openapi4j:openapi-core:${libVersions.openapiCore}" implementation "com.google.inject.extensions:guice-assistedinject:${libVersions.guice}" + implementation "com.networknt:json-schema-validator:${libVersions.jsonSchemaValidator}" + implementation "org.slf4j:slf4j-api:${libVersions.slf4j}" testImplementation project(":oas-test") } \ No newline at end of file
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtension.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtension.java index 0526ba4..3c456c7 100644 --- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtension.java +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtension.java
@@ -1,9 +1,12 @@ package com.apigee.security.oas.extendedvalidator; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.google.errorprone.annotations.Var; import com.google.inject.assistedinject.Assisted; +import com.networknt.schema.ValidationMessage; +import java.util.Map; +import java.util.Optional; import javax.inject.Inject; import org.openapi4j.parser.model.OpenApiSchema; @@ -11,13 +14,16 @@ final class BaseExtension implements Extension { private final String extensionName; private final JsonNode extensionContent; - private final ImmutableSet<Class<? extends OpenApiSchema>> extensionPath; + private final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> + extensionPath; @Inject BaseExtension( - @Var @Assisted String extensionName, - @Var @Assisted JsonNode extensionContent, - @Var @Assisted ImmutableSet<Class<? extends OpenApiSchema>> extensionPath) { + @Assisted String extensionName, + @Assisted JsonNode extensionContent, + @Assisted + ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> + extensionPath) { this.extensionName = extensionName; this.extensionContent = extensionContent; this.extensionPath = extensionPath; @@ -29,7 +35,8 @@ } @Override - public ImmutableSet<Class<? extends OpenApiSchema>> getExtensionPath() { + public ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> + getExtensionPath() { return extensionPath; } @@ -40,7 +47,7 @@ /** Validates the extension with given {@link ExtensionValidator}. */ @Override - public boolean validate(@Var ExtensionValidator extensionValidator) { + public ImmutableSet<ValidationMessage> validate(ExtensionValidator extensionValidator) { return extensionValidator.validate(this); } }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java new file mode 100644 index 0000000..d7095a3 --- /dev/null +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java
@@ -0,0 +1,80 @@ +package com.apigee.security.oas.extendedvalidator; + +import static com.apigee.security.oas.extendedvalidator.ExtensionName.valueOfExtensionName; +import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.defaultErrors; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableSet; +import com.google.common.flogger.FluentLogger; +import com.google.common.io.Resources; +import com.google.errorprone.annotations.Var; +import com.networknt.schema.JsonSchema; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.ValidationMessage; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Optional; +import javax.inject.Inject; + +final class BaseExtensionSchemaValidator implements ExtensionSchemaValidator { + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private static final URL LIST_WITH_STRINGS_SCHEMA_URL = + Resources.getResource("ListWithStringsSchema.json"); + private static final URL SECURITY_DEFINITIONS_SCHEMA_URL = + Resources.getResource("SecurityDefinitionsSchema.json"); + + private final JsonSchemaFactory jsonSchemaFactory; + + @Inject + BaseExtensionSchemaValidator(JsonSchemaFactory jsonSchemaFactory) { + this.jsonSchemaFactory = jsonSchemaFactory; + } + + private JsonSchema getJsonSchemaFromUrl(URL schemaUrl) throws URISyntaxException { + return jsonSchemaFactory.getSchema(schemaUrl.toURI()); + } + + /** Validates {@link Extension} by contrasting it against its appropriate schema. */ + @Override + public ImmutableSet<ValidationMessage> validate(Extension extension) { + Optional<ExtensionName> extensionName = valueOfExtensionName(extension.getExtensionName()); + @Var ImmutableSet<ValidationMessage> errors = ImmutableSet.of(); + + if (extensionName.isPresent()) { + JsonNode extensionContent = extension.getExtensionContent(); + switch (extensionName.get()) { + case X_SECURITY_TYPE: + case X_SECURITY_ALLOW: + errors = validateExtensionContent(LIST_WITH_STRINGS_SCHEMA_URL, extensionContent); + break; + case X_SECURITY_TYPE_DEFINITIONS: + errors = validateExtensionContent(SECURITY_DEFINITIONS_SCHEMA_URL, extensionContent); + break; + } + + // TODO(b/162242995): Add named extension path to each validation message in errors. + } else { + errors = defaultErrors(extension); + } + + logger.atInfo().log("Schema Errors : %s", errors); + + return errors; + } + + /** Validates an {@link JsonNode} against its {@link JsonSchema}. */ + private ImmutableSet<ValidationMessage> validateExtensionContent( + URL schemaUrl, JsonNode extensionContent) { + try { + JsonSchema schema = getJsonSchemaFromUrl(schemaUrl); + ImmutableSet<ValidationMessage> errors = + ImmutableSet.copyOf(schema.validate(extensionContent)); + return errors; + } catch (URISyntaxException e) { + String errorMessage = "Failed to validate extension schema."; + logger.atSevere().log("%s due to %s", errorMessage, e.getMessage()); + throw new ValidationException(errorMessage, e); + } + } +}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Extension.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Extension.java index 3428e2d..a18a880 100644 --- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Extension.java +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Extension.java
@@ -1,14 +1,18 @@ package com.apigee.security.oas.extendedvalidator; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.networknt.schema.ValidationMessage; +import java.util.Map; +import java.util.Optional; import org.openapi4j.parser.model.OpenApiSchema; /** Stores details of and validates the extension. */ public interface Extension { /** Validates the {@code Extension} with {@link ExtensionValidator}. */ - boolean validate(ExtensionValidator extensionValidator); + ImmutableSet<ValidationMessage> validate(ExtensionValidator extensionValidator); /** Returns the content of the Extension. */ JsonNode getExtensionContent(); @@ -17,7 +21,7 @@ * Returns the path of {@link org.openapi4j.parser.model.OpenApiSchema} classes from the root * node. */ - ImmutableSet<Class<? extends OpenApiSchema>> getExtensionPath(); + ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> getExtensionPath(); /** Returns the name of the Extension. */ String getExtensionName();
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionFactory.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionFactory.java index 22d7b9b..bf974ea 100644 --- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionFactory.java +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionFactory.java
@@ -1,7 +1,9 @@ package com.apigee.security.oas.extendedvalidator; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList; +import java.util.Map; +import java.util.Optional; import org.openapi4j.parser.model.OpenApiSchema; /** Factory to create an {@link Extension}. */ @@ -14,5 +16,5 @@ Extension create( String extensionName, JsonNode extensionContent, - ImmutableSet<Class<? extends OpenApiSchema>> extensionPath); + ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> extensionPath); }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java index 0a97951..70c3f7a 100644 --- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java
@@ -1,7 +1,10 @@ package com.apigee.security.oas.extendedvalidator; import com.google.inject.AbstractModule; +import com.google.inject.Provides; import com.google.inject.assistedinject.FactoryModuleBuilder; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; /** * Module with instructions for instantiating instances of {@link Extension} and building {@link @@ -14,5 +17,12 @@ new FactoryModuleBuilder() .implement(Extension.class, BaseExtension.class) .build(ExtensionFactory.class)); + + bind(ExtensionSchemaValidator.class).to(BaseExtensionSchemaValidator.class); + } + + @Provides + public JsonSchemaFactory jsonSchemaFactory() { + return JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); } }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java index cacee5a..98eaa6b 100644 --- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java
@@ -1,11 +1,13 @@ package com.apigee.security.oas.extendedvalidator; -import com.google.common.base.Optional; import com.google.errorprone.annotations.Var; +import java.util.Optional; /** Stores all the supported {@code x-security-*} extensions as constants. */ enum ExtensionName { - X_SECURITY_TYPE("x-security-type"); + X_SECURITY_TYPE("x-security-type"), + X_SECURITY_ALLOW("x-security-allow"), + X_SECURITY_TYPE_DEFINITIONS("x-security-type-definitions"); private final String extensionName; @@ -32,6 +34,6 @@ extensionEnum = extension; } } - return Optional.fromNullable(extensionEnum); + return Optional.ofNullable(extensionEnum); } }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionSchemaValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionSchemaValidator.java new file mode 100644 index 0000000..6d8b755 --- /dev/null +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionSchemaValidator.java
@@ -0,0 +1,4 @@ +package com.apigee.security.oas.extendedvalidator; + +/** A {@link ExtensionValidator} interface for {@link BaseExtensionSchemaValidator}. */ +interface ExtensionSchemaValidator extends ExtensionValidator {}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidator.java index d944371..9d2d3a0 100644 --- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidator.java +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidator.java
@@ -1,8 +1,11 @@ package com.apigee.security.oas.extendedvalidator; +import com.google.common.collect.ImmutableSet; +import com.networknt.schema.ValidationMessage; + /** Validates a {@link Extension} based on the type of {@code Extension}. */ public interface ExtensionValidator { /** Validates a {@link Extension}. */ - boolean validate(Extension extension); + ImmutableSet<ValidationMessage> validate(Extension extension); }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidators.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidators.java new file mode 100644 index 0000000..6090e9b --- /dev/null +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidators.java
@@ -0,0 +1,61 @@ +package com.apigee.security.oas.extendedvalidator; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.networknt.schema.CustomErrorMessageType; +import com.networknt.schema.ValidationMessage; +import java.util.Map; +import java.util.Optional; +import org.openapi4j.parser.model.OpenApiSchema; + +/** + * Utility class to prepare path string and {@link ValidationMessage} for {@link + * ExtensionValidator}. + */ +final class ExtensionValidators { + + /** + * Converts an {@link ImmutableList} {@code extensionPath} into {@link String}. + * + * <p>A named path is used for logging and passed as an parameter of {@link ValidationMessage} if + * an error occurs during validation. + */ + static String prepareNamedPath( + ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> extensionPath) { + StringBuilder namedPath = new StringBuilder(); + if (extensionPath.size() > 0) { + for (Map.Entry<Class<? extends OpenApiSchema>, Optional<String>> pathMap : extensionPath) { + Optional<String> namedPathObject = pathMap.getValue(); + namedPath.append(" -> "); + if (namedPathObject.isPresent()) { + namedPath.append(namedPathObject.get()); + } else { + namedPath.append(pathMap.getKey().getSimpleName()); + } + } + } + + return namedPath.toString(); + } + + static ValidationMessage prepareValidationMessage( + String errorCode, String errorType, String path) { + return ValidationMessage.of(errorType, CustomErrorMessageType.of(errorCode), path); + } + + /** + * Returns an {@link ImmutableSet} with {@code UNSUPPORTED_EXTENSION} error as a {@link + * ValidationMessage}. + * + * <p>Prepares a default error {@link ValidationMessage} for extensions without validators. + */ + static ImmutableSet<ValidationMessage> defaultErrors(Extension extension) { + String errorCode = "UNSUPPORTED_EXTENSION"; + String errorType = extension.getExtensionName() + " is not binded to the validator."; + String path = prepareNamedPath(extension.getExtensionPath()); + ValidationMessage message = prepareValidationMessage(errorCode, errorType, path); + return ImmutableSet.of(message); + } + + private ExtensionValidators() {} +}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ValidationException.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ValidationException.java new file mode 100644 index 0000000..bc39edd --- /dev/null +++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ValidationException.java
@@ -0,0 +1,8 @@ +package com.apigee.security.oas.extendedvalidator; + +/** Exception thrown when an error has occurred while validating an {@link Extension}. */ +public final class ValidationException extends RuntimeException { + ValidationException(String s, Exception childException) { + super(s, childException); + } +}
diff --git a/oas-core/src/main/resources/ListWithStringsSchema.json b/oas-core/src/main/resources/ListWithStringsSchema.json new file mode 100644 index 0000000..5623873 --- /dev/null +++ b/oas-core/src/main/resources/ListWithStringsSchema.json
@@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "List of strings", + "type": "array", + "items": { + "type": "string" + }, + "tests": [ + { + "id": "VALID_1", + "description": "List of strings", + "data": ["string1", "string2"], + "valid": true + }, + { + "id": "VALID_2", + "description": "Empty list", + "data": [], + "valid": true + }, + { + "id": "INVALID_1", + "description": "List of integer and strings", + "data": ["string1", 2, 3], + "valid": false + } + ] +} \ No newline at end of file
diff --git a/oas-core/src/main/resources/SecurityDefinitionsSchema.json b/oas-core/src/main/resources/SecurityDefinitionsSchema.json new file mode 100644 index 0000000..06eb2fb --- /dev/null +++ b/oas-core/src/main/resources/SecurityDefinitionsSchema.json
@@ -0,0 +1,78 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Security definitions schema", + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "inherits-from": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["type"], + "additionalProperties": false, + "tests": [ + { + "id": "VALID_1", + "description": "type and inherits-from", + "data": { + "type": "customType", + "inherits-from": ["string1", "string2"] + }, + "valid": true + }, + { + "id": "VALID_2", + "description": "only type", + "data": { + "type": "customType" + }, + "valid": true + }, + { + "id": "VALID_3", + "description": "type and empty inherits-from", + "data": { + "type": "pii", + "inherits-from": [] + }, + "valid": true + }, + { + "id": "INVALID_1", + "description": "type not of string type", + "data": { + "type": 1 + }, + "valid": false + }, + { + "id": "INVALID_2", + "description": "inherits-from not of list type", + "data": { + "type": "customType1", + "inherits-from": "customType2" + }, + "valid": false + }, + { + "id": "INVALID_3", + "description": "Required field 'type' not given", + "data": {}, + "valid": false + }, + { + "id": "INVALID_4", + "description": "Additional field present", + "data": { + "type": "customType", + "customField": "customString" + }, + "valid": false + } + ] +} \ No newline at end of file
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidatorTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidatorTest.java new file mode 100644 index 0000000..1cc69aa --- /dev/null +++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidatorTest.java
@@ -0,0 +1,175 @@ +package com.apigee.security.oas.extendedvalidator; + +import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE; +import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE_DEFINITIONS; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.unmodifiableIterable; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.ThrowableAssert.catchThrowable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.io.Resources; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.ValidationMessage; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.openapi4j.parser.model.OpenApiSchema; +import org.openapi4j.parser.model.v3.Operation; + +@RunWith(Enclosed.class) +public class BaseExtensionSchemaValidatorTest { + + private static final URL ARRAY_LIST_SECURITY_TYPES_SCHEMA_URL = + Resources.getResource("ListWithStringsSchema.json"); + private static final URL SECURITY_DEFINITIONS_SCHEMA_URL = + Resources.getResource("SecurityDefinitionsSchema.json"); + private static final Injector injector = Guice.createInjector(new ExtendedValidatorMainModule()); + private static final JsonNode emptyContent = new ObjectMapper().valueToTree("[]"); + private static final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> + extensionPath = + ImmutableList.of(new SimpleImmutableEntry<>(Operation.class, Optional.of("get"))); + + @RunWith(Parameterized.class) + public static class BaseExtensionSchemaValidatorParamsTest { + @Parameter(0) + public static String extensionName; + + @Parameter(1) + public static JsonNode content; + + @Parameter(2) + public static boolean isValid; + + private ExtensionValidator validator; + private ExtensionFactory factory; + + /** Setting up validator and factories. */ + @Before + public void setup() { + validator = injector.getInstance(ExtensionSchemaValidator.class); + factory = injector.getInstance(ExtensionFactory.class); + } + + /** + * Builds {@link Collection} of {{@link #extensionName}, {@link #isValid}, {@link #content}} + * parameters from {@code Schema URL}. + */ + public static Collection<Object[]> buildTestParameters(URL schemaUrl, String extensionName) + throws IOException { + JsonNode schema = new ObjectMapper().readTree(schemaUrl); + JsonNode schemaTests = schema.get("tests"); + ArrayList<Object[]> testParameters = new ArrayList<>(); + + if (schemaTests.isArray()) { + for (JsonNode testNode : schemaTests) { + testParameters.add( + new Object[] { + extensionName, testNode.get("data"), testNode.get("valid").asBoolean() + }); + } + } + return testParameters; + } + + /** Builds global test parameters for multiple schemas. */ + @Parameters(name = "{index}: extension: {0}, isValid: {1}, Data: {2}") + public static Collection<Object[]> data() throws IOException { + + Collection<Object[]> arrayListSchemaTestParameters = + buildTestParameters( + ARRAY_LIST_SECURITY_TYPES_SCHEMA_URL, X_SECURITY_TYPE.getExtensionName()); + Collection<Object[]> securityDefinitionSchemaTestParameters = + buildTestParameters( + SECURITY_DEFINITIONS_SCHEMA_URL, X_SECURITY_TYPE_DEFINITIONS.getExtensionName()); + + Iterable<Object[]> combinedIterables = + unmodifiableIterable( + concat(arrayListSchemaTestParameters, securityDefinitionSchemaTestParameters)); + return Lists.newArrayList(combinedIterables); + } + + @Test + public void validate_schemaParameterTests_returnsErrorSet() { + Extension extension = factory.create(extensionName, content, extensionPath); + ImmutableSet<ValidationMessage> errors = validator.validate(extension); + + assertThat(errors.isEmpty()).isEqualTo(isValid); + } + } + + @RunWith(JUnit4.class) + public static class BaseExtensionSchemaValidatorSingleTest { + + @Rule public final MockitoRule rule = MockitoJUnit.rule(); + + @Mock private JsonSchemaFactory schemaFactory; + + @InjectMocks private BaseExtensionSchemaValidator validator; + + private ExtensionFactory factory; + + /** Sets up validator and extension factory. */ + @Before + public void setup() { + factory = injector.getInstance(ExtensionFactory.class); + } + + @Test + public void validate_unsupportedExtension_returnsUnsupportedExtensionErrorMessage() { + Extension extension = factory.create("x-unsupported", emptyContent, extensionPath); + ImmutableSet<ValidationMessage> errors = validator.validate(extension); + + assertThat(errors) + .hasSize(1) + .first() + .extracting(ValidationMessage::getCode) + .isEqualTo("UNSUPPORTED_EXTENSION"); + } + + @Test + public void validate_schemaFactoryThrowsUriSyntaxException_shouldThrowValidationException() { + doAnswer( + invocationOnMock -> { + throw new URISyntaxException("", ""); + }) + .when(schemaFactory) + .getSchema(any(URI.class)); + + Extension extension = + factory.create(X_SECURITY_TYPE.getExtensionName(), emptyContent, extensionPath); + + assertThat(catchThrowable(() -> validator.validate(extension))) + .isInstanceOf(ValidationException.class) + .hasRootCauseInstanceOf(URISyntaxException.class) + .hasMessageContainingAll("Failed", "validate"); + } + } +}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionTest.java index 02d9b05..a35f6c1 100644 --- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionTest.java +++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionTest.java
@@ -9,9 +9,13 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.inject.Guice; -import java.util.Arrays; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Optional; +import org.apache.commons.lang3.tuple.Pair; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -22,7 +26,6 @@ import org.mockito.junit.MockitoRule; import org.openapi4j.parser.model.OpenApiSchema; import org.openapi4j.parser.model.v3.Operation; -import org.openapi4j.parser.model.v3.Path; @RunWith(JUnit4.class) public class BaseExtensionTest { @@ -34,44 +37,38 @@ private JsonNode node; private Extension extension; - private ImmutableSet<Class<? extends OpenApiSchema>> extensionPath; + private ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> extensionPath; @Mock private ExtensionValidator extensionValidator; /** Setting up an {@link Extension} using {@link ExtensionFactory}. */ @Before public void setup() { node = mapper.valueToTree("[]"); - extensionPath = ImmutableSet.copyOf(Arrays.asList(Path.class, Operation.class)); + extensionPath = ImmutableList.of(Pair.of(Operation.class, Optional.of("get"))); extension = factory.create(X_SECURITY_TYPE.getExtensionName(), node, extensionPath); } @Test - public void create_returnsExtension() { - assertThat(extension).isInstanceOf(Extension.class); - } + public void validate_callsExtensionValidatorsValidate() throws URISyntaxException { + when(extensionValidator.validate(any(Extension.class))).thenReturn(ImmutableSet.of()); - @Test - public void validate_callsExtensionValidator() { - when(extensionValidator.validate(any(Extension.class))).thenReturn(true); + extension.validate(extensionValidator); - boolean isValid = extension.validate(extensionValidator); - - assertThat(isValid).isTrue(); verify(extensionValidator, atLeastOnce()).validate(any(Extension.class)); } @Test - public void getExtensionName_returnsString() { + public void getExtensionName_returnsStringClassMember() { assertThat(extension.getExtensionName()).isEqualTo(X_SECURITY_TYPE.getExtensionName()); } @Test - public void getExtensionPath_returnsImmutableSetInstance() { + public void getExtensionPath_returnsImmutableListClassMember() { assertThat(extension.getExtensionPath()).isEqualTo(extensionPath); } @Test - public void getExtensionContent_returnsJsonNodeInstance() { + public void getExtensionContent_returnsJsonNodeClassMember() { assertThat(extension.getExtensionContent()).isEqualTo(node); } }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java index f5cac11..70810de 100644 --- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java +++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java
@@ -5,6 +5,7 @@ import com.google.inject.Guice; import com.google.inject.Injector; +import com.networknt.schema.JsonSchemaFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,4 +45,16 @@ assertThat(catchThrowable(() -> injector.getInstance(TraversalCoordinator.class))) .doesNotThrowAnyException(); } + + @Test + public void getInstance_extensionSchemaValidator_shouldNotFail() { + assertThat(catchThrowable(() -> injector.getInstance(ExtensionSchemaValidator.class))) + .doesNotThrowAnyException(); + } + + @Test + public void getInstance_jsonSchemaFactory_shouldNotFail() { + assertThat(catchThrowable(() -> injector.getInstance(JsonSchemaFactory.class))) + .doesNotThrowAnyException(); + } }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionFactoryTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionFactoryTest.java index 0216741..76bf6b7 100644 --- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionFactoryTest.java +++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionFactoryTest.java
@@ -5,45 +5,39 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList; import com.google.inject.Guice; import com.google.inject.Injector; -import java.util.Arrays; +import java.util.Map; +import java.util.Optional; +import org.apache.commons.lang3.tuple.Pair; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.openapi4j.parser.model.OpenApiSchema; import org.openapi4j.parser.model.v3.Operation; -import org.openapi4j.parser.model.v3.Path; @RunWith(JUnit4.class) public class ExtensionFactoryTest { private JsonNode node; - private ImmutableSet<Class<? extends OpenApiSchema>> extensionPath; + private ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> extensionPath; private ExtensionFactory factory; /** Setting up parameters and {@link ExtensionFactory} instance.. */ @Before public void setup() { node = new ObjectMapper().valueToTree("[]"); - extensionPath = ImmutableSet.copyOf(Arrays.asList(Path.class, Operation.class)); + extensionPath = ImmutableList.of(Pair.of(Operation.class, Optional.of("get"))); Injector injector = Guice.createInjector(new ExtensionModule()); factory = injector.getInstance(ExtensionFactory.class); } @Test - public void generateExtension_xSecurityExtensionName_returnsExtension() { + public void create_extensionData_returnsExtension() { Extension extension = factory.create(X_SECURITY_TYPE.getExtensionName(), node, extensionPath); assertThat(extension).isInstanceOf(Extension.class); } - - @Test - public void generateExtension_unsupportedExtensionName_returnsExtension() { - Extension extension = factory.create("x-unsupported", node, extensionPath); - - assertThat(extension).isInstanceOf(Extension.class); - } }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionNameTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionNameTest.java index 4024b9d..5570009 100644 --- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionNameTest.java +++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionNameTest.java
@@ -4,7 +4,7 @@ import static com.apigee.security.oas.extendedvalidator.ExtensionName.valueOfExtensionName; import static org.assertj.core.api.Assertions.assertThat; -import com.google.common.base.Optional; +import java.util.Optional; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -13,7 +13,7 @@ public class ExtensionNameTest { @Test - public void valueOfExtensionName_supportedExtension_returnsExtensionNameOptional() { + public void valueOfExtensionName_supportedExtension_returnsValidExtensionNameOptional() { Optional<ExtensionName> extensionNameOptional = valueOfExtensionName(X_SECURITY_TYPE.getExtensionName());
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionValidatorsTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionValidatorsTest.java new file mode 100644 index 0000000..95155f2 --- /dev/null +++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtensionValidatorsTest.java
@@ -0,0 +1,65 @@ +package com.apigee.security.oas.extendedvalidator; + +import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.defaultErrors; +import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.prepareNamedPath; +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.networknt.schema.ValidationMessage; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Map; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.openapi4j.parser.model.OpenApiSchema; +import org.openapi4j.parser.model.v3.Operation; +import org.openapi4j.parser.model.v3.Path; + +@RunWith(JUnit4.class) +public class ExtensionValidatorsTest { + + private static final ExtensionFactory factory = + Guice.createInjector(new ExtensionModule()).getInstance(ExtensionFactory.class); + + private ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path; + private Extension extension; + + /** Setting up {@link Extension} object. */ + @Before + public void setup() { + Map.Entry<Class<? extends OpenApiSchema>, Optional<String>> pathEntry = + new SimpleImmutableEntry<Class<? extends OpenApiSchema>, Optional<String>>( + Path.class, Optional.of("/users")); + Map.Entry<Class<? extends OpenApiSchema>, Optional<String>> operationEntry = + new SimpleImmutableEntry<Class<? extends OpenApiSchema>, Optional<String>>( + Operation.class, Optional.of("get")); + path = ImmutableList.of(pathEntry, operationEntry); + + JsonNode node = new ObjectMapper().valueToTree("[]"); + extension = factory.create("x-security", node, path); + } + + @Test + public void prepareNamedPath_extensionPath_returnsStringWithNamedPath() { + String namedPath = prepareNamedPath(path); + + assertThat(namedPath).contains("/users", "get"); + } + + @Test + public void defaultErrors_returnsImmutableSetWithUnsupportedExtensionMessage() { + ImmutableSet<ValidationMessage> errors = defaultErrors(extension); + + assertThat(errors) + .hasSize(1) + .first() + .extracting(ValidationMessage::getCode) + .isEqualTo("UNSUPPORTED_EXTENSION"); + } +}