Skip to content
This repository was archived by the owner on May 16, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.everit.json.schema.Schema;
import org.everit.json.schema.ValidationException;
import org.everit.json.schema.loader.SchemaClient;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -161,31 +155,6 @@ public static byte[] cborEncode(Object object) throws JsonProcessingException {
return cborMapper.writeValueAsBytes(object);
}

/**
* Validates an object (JSON) based on a provided schema containing validation rules.
*
* @param validateObject - object to be validated
* @param schemaValidationJson - validation schema
* @throws JsonProcessingException - if object to be validated fails on JSON processing
* @throws ValidationException - if the validation of the object based on validation schema fails.
*/
public static void validateJsonSchema(Object validateObject, InputStream schemaValidationJson,
final SchemaClient schemaClient)
throws JsonProcessingException, ValidationException {
ObjectMapper objectMapper = new ObjectMapper();
JSONObject jsonSchema = new JSONObject(new JSONTokener(schemaValidationJson));
String businessRuleJson = objectMapper.writeValueAsString(validateObject);

JSONObject jsonSubject = new JSONObject(businessRuleJson);
Schema schema = schemaClient == null ? SchemaLoader.load(jsonSchema) : SchemaLoader.load(jsonSchema, schemaClient);
schema.validate(jsonSubject);
}

public static void validateJsonSchema(Object validateObject, InputStream schemaValidationJson)
throws JsonProcessingException, ValidationException {
validateJsonSchema(validateObject, schemaValidationJson, null);
}

private SerializationUtils() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@
import static app.coronawarn.server.common.shared.util.SerializationUtils.deserializeJson;
import static app.coronawarn.server.common.shared.util.SerializationUtils.jsonExtractCosePayload;
import static app.coronawarn.server.common.shared.util.SerializationUtils.stringifyObject;
import static app.coronawarn.server.common.shared.util.SerializationUtils.validateJsonSchema;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import org.everit.json.schema.ValidationException;
import org.json.JSONException;
import org.junit.jupiter.api.Test;


class SerializationUtilsTest {

public static final String TEST_ATTRIBUTE = "testAttribute";
Expand Down Expand Up @@ -75,25 +70,6 @@ void testStringifyObjectAndExpectException() {
.isThrownBy(() -> stringifyObject(testObject));
}

@Test
void shouldPassValidationSchema() throws JSONException, JsonProcessingException {
TestObject subject = new TestObject();
subject.setTestAttribute(VALIDATION_SCHEMA_OK);

InputStream validationSchema = getClass().getClassLoader().getResourceAsStream(VALIDATION_SCHEMA_JSON);
validateJsonSchema(subject, validationSchema);
}

@Test
void shouldNotPassValidationSchema() {
TestObject subject = new TestObject();
subject.setTestAttribute(VALIDATION_SCHEMA_NOT_OKAY);

InputStream validationSchema = getClass().getClassLoader().getResourceAsStream("validation_schema.json");
assertThatExceptionOfType(ValidationException.class)
.isThrownBy(() -> validateJsonSchema(subject, validationSchema));
}

@Test
void shouldCborEncode() throws IOException {
TestObject subject = new TestObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,30 @@ public Retryer retryer() {
return new Retryer.Default(retryPeriod, maxRetryPeriod, maxAttempts);
}

/**
* Feign decoder for validation of json files against a given schema.
* @param messageConverters Message converters to use.
* @param customizers Customizers to use.
* @param resourceLoader A resource loader.
* @param jsonValidationService A validation service that checks for json schema violations.
* @return The decoder.
*/
@Bean
public Decoder jsonSchemaDecoder(ObjectFactory<HttpMessageConverters> messageConverters,
ObjectProvider<HttpMessageConverterCustomizer> customizers, ResourceLoader resourceLoader) {
Decoder jsonSchemaDecoder = new JsonSchemaDecoder(messageConverters, customizers, resourceLoader);
ObjectProvider<HttpMessageConverterCustomizer> customizers, ResourceLoader resourceLoader,
JsonValidationService jsonValidationService) {
Decoder jsonSchemaDecoder = new JsonSchemaDecoder(messageConverters, customizers, resourceLoader,
jsonValidationService);
return new ResponseEntityDecoder(jsonSchemaDecoder);
}

/**
* A service to validate json against a schema.
* @param resourceLoader A resource loader.
* @return The validation service.
*/
@Bean
public JsonValidationService jsonValidationService(ResourceLoader resourceLoader) {
return new JsonValidationService(resourceLoader);
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
package app.coronawarn.server.services.distribution.dgc.client;

import static app.coronawarn.server.services.distribution.dgc.client.JsonSchemaMappingLookup.JSON_SCHEMA_PATH;
import static java.nio.charset.StandardCharsets.UTF_8;

import app.coronawarn.server.common.shared.util.ResourceSchemaClient;
import feign.FeignException;
import feign.Response;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.stream.Collectors;
import org.everit.json.schema.Schema;
import org.everit.json.schema.ValidationException;
import org.everit.json.schema.loader.SchemaClient;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectFactory;
Expand All @@ -32,8 +17,9 @@
public class JsonSchemaDecoder extends SpringDecoder {

private static final Logger logger = LoggerFactory.getLogger(JsonSchemaDecoder.class);
private ResourceLoader resourceLoader;
private JsonSchemaMappingLookup jsonSchemaMappingLookup;
private final ResourceLoader resourceLoader;
private final JsonSchemaMappingLookup jsonSchemaMappingLookup;
private final JsonValidationService jsonValidationService;

/**
* Constructor to load the resource loader and lookup helper class.
Expand All @@ -43,10 +29,12 @@ public class JsonSchemaDecoder extends SpringDecoder {
* @param resourceLoader The resource loader used to load the json schema.
*/
public JsonSchemaDecoder(final ObjectFactory<HttpMessageConverters> messageConverters,
final ObjectProvider<HttpMessageConverterCustomizer> customizers, final ResourceLoader resourceLoader) {
final ObjectProvider<HttpMessageConverterCustomizer> customizers, final ResourceLoader resourceLoader,
JsonValidationService jsonValidationService) {
super(messageConverters, customizers);
this.resourceLoader = resourceLoader;
jsonSchemaMappingLookup = new JsonSchemaMappingLookup();
this.jsonValidationService = jsonValidationService;
}

@Override
Expand All @@ -59,7 +47,7 @@ public Object decode(final Response response, final Type type) throws IOExceptio
return super.decode(response, type);
}
try (final InputStream schemaInputStream = resourceLoader.getResource(schemaPathToUse).getInputStream()) {
validateJsonAgainstSchema(payloadJsonInputStream, schemaInputStream);
jsonValidationService.validateJsonAgainstSchema(payloadJsonInputStream, schemaInputStream);
}
return super.decode(response, type);
}
Expand All @@ -73,37 +61,4 @@ public Object decode(final Response response, final Type type) throws IOExceptio
public String getSchemaPathForRequestEndpoint(final String requestUrl) {
return jsonSchemaMappingLookup.getSchemaPath(requestUrl);
}

/**
* Validation logic to compare a json file to a schema.
*
* @param jsonPayloadInputStream The json payload.
* @param schemaAsStream The json schema.
* @throws IOException Thrown when json could not be loaded from file.
*/
public void validateJsonAgainstSchema(final InputStream jsonPayloadInputStream, final InputStream schemaAsStream)
throws IOException {
try {
final JSONObject jsonSchema = new JSONObject(new JSONTokener(schemaAsStream));
final SchemaClient schemaClient = new ResourceSchemaClient(resourceLoader, JSON_SCHEMA_PATH);
final Schema schema = SchemaLoader.load(jsonSchema, schemaClient);
// have to check manually if json is an array or an object. Limitation of the org.json library.

// workaround for the actual json in the stream -- if we give the InputStream to the JSONTokener directly below,
// it is saying that we don't have valid json (it says it should start with { or with [, which id DOES!)
final String jsonPayloadString = new BufferedReader(
new InputStreamReader(jsonPayloadInputStream, UTF_8)).lines().collect(Collectors.joining());
try {
final JSONObject parsedObject = new JSONObject(new JSONTokener(jsonPayloadString));
schema.validate(parsedObject);
} catch (final JSONException e) {
logger.debug(e.getMessage(), e);
final JSONArray parsedArray = new JSONArray(new JSONTokener(jsonPayloadString));
parsedArray.forEach(schema::validate);
}
} catch (ValidationException | JSONException e) {
logger.error("Json schema validation failed", e);
throw e;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package app.coronawarn.server.services.distribution.dgc.client;

import static app.coronawarn.server.services.distribution.dgc.client.JsonSchemaMappingLookup.JSON_SCHEMA_PATH;
import static java.nio.charset.StandardCharsets.UTF_8;

import app.coronawarn.server.common.shared.util.ResourceSchemaClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.everit.json.schema.Schema;
import org.everit.json.schema.ValidationException;
import org.everit.json.schema.loader.SchemaClient;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ResourceLoader;

public class JsonValidationService {

private static final Logger logger = LoggerFactory.getLogger(JsonValidationService.class);
private final ResourceLoader resourceLoader;

public JsonValidationService(final ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}

/**
* Validation logic to compare a json file to a schema.
*
* @param jsonPayloadInputStream The json payload.
* @param schemaAsStream The json schema.
* @throws IOException Thrown when json could not be loaded from file.
*/
public void validateJsonAgainstSchema(final InputStream jsonPayloadInputStream, final InputStream schemaAsStream)
throws IOException, ValidationException, JSONException {
try {
final JSONObject jsonSchema = new JSONObject(new JSONTokener(schemaAsStream));
final SchemaClient schemaClient = new ResourceSchemaClient(resourceLoader, JSON_SCHEMA_PATH);
final Schema schema = SchemaLoader.load(jsonSchema, schemaClient);
try {
final JSONObject parsedObject = new JSONObject(new JSONTokener(new BufferedReader(
new InputStreamReader(jsonPayloadInputStream, UTF_8))));
schema.validate(parsedObject);
} catch (final JSONException e) {
logger.debug(e.getMessage(), e);
final JSONArray parsedArray = new JSONArray(new JSONTokener(new BufferedReader(
new InputStreamReader(jsonPayloadInputStream, UTF_8))));
parsedArray.forEach(schema::validate);
}
} catch (final ValidationException e) {
logger.error("JSON schema violations: {}", e.getAllMessages());
throw e;
}
}
}
Loading