From 226b0546f3fb49572a3e749fdbe7352338055f3d Mon Sep 17 00:00:00 2001
From: Henry Borasch <Henry.Borasch@sinc.de>
Date: Wed, 1 Mar 2023 10:40:35 +0100
Subject: [PATCH] added submission data validation for sender

* added loading of submission data schema
* added submission data validation to sender and subscriber flow
* added and adjusted tests and test data
* added attribute for skipping validations to config, fixed tests
---
 .../fitconnect/api/config/SchemaConfig.java   |  7 +++-
 .../api/domain/schema/SchemaResources.java    |  1 +
 .../fitko/fitconnect/api/services/Sender.java |  4 ++-
 .../validation/ValidationService.java         |  4 ++-
 .../fitko/fitconnect/client/SenderClient.java |  2 +-
 .../client/cli/CommandExecutor.java           |  2 +-
 .../client/factory/ClientFactory.java         | 15 ++++----
 .../client/sender/SubmissionBuilder.java      |  2 +-
 .../sender/model}/SubmissionPayload.java      |  2 +-
 .../client/sender/steps/BuildStep.java        |  2 +-
 .../strategies/SendNewSubmissionStrategy.java |  4 ++-
 .../client/util/ValidDataGuard.java           |  4 +--
 .../fitconnect/client/SenderClientTest.java   |  8 ++---
 .../client/cli/CommandLineClientTest.java     |  2 +-
 .../client/util/ValidDataGuardTest.java       | 26 +++++++-------
 .../fitconnect/core/SubmissionSender.java     | 10 ++++--
 .../core/schema/SchemaResourceProvider.java   | 34 +++++++++++++++----
 .../validation/DefaultValidationService.java  |  7 ++--
 .../fitconnect/core/SubmissionSenderTest.java | 10 +++---
 .../events/SecurityEventTokenServiceTest.java |  7 ++--
 .../schema/SchemaResourceProviderTest.java    |  7 ++--
 .../DefaultValidationServiceTest.java         | 10 +++---
 22 files changed, 110 insertions(+), 60 deletions(-)
 rename {api/src/main/java/dev/fitko/fitconnect/api/domain/model/submission => client/src/main/java/dev/fitko/fitconnect/client/sender/model}/SubmissionPayload.java (94%)

diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/SchemaConfig.java b/api/src/main/java/dev/fitko/fitconnect/api/config/SchemaConfig.java
index eeffbfb7d..e5dd7541f 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/config/SchemaConfig.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/SchemaConfig.java
@@ -15,7 +15,6 @@ public enum SchemaConfig {
     SET_V_1_0_1(SCHEMA_BASE_URL.schemaUri.resolve("set-payload/1.0.1/set-payload.schema.json"), "set_schema_1.0.1.json"),
     SET_V_1_0_0(SCHEMA_BASE_URL.schemaUri.resolve("set-payload/1.0.0/set-payload.schema.json"), "set_schema_1.0.0.json"),
     METADATA_V_1_0_0(SCHEMA_BASE_URL.schemaUri.resolve("metadata/1.0.0/metadata.schema.json"), "metadata_schema_1.0.0.json"),
-
     XZUFI_DESTINATION_SCHEMA(SCHEMA_BASE_URL.schemaUri.resolve("xzufi/destination.schema.json"), "destination_schema.json");
 
     private final URI schemaUri;
@@ -50,6 +49,12 @@ public enum SchemaConfig {
                 .collect(Collectors.toList());
     }
 
+    public static List<String> getSubmissionDataSchemaPaths(final String submissionDataSchemaBaseDir) {
+        return Stream.empty()
+                .map(fileName -> submissionDataSchemaBaseDir + "/" + fileName)
+                .collect(Collectors.toList());
+    }
+
     @Override
     public String toString() {
         return schemaUri.toString();
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/schema/SchemaResources.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/schema/SchemaResources.java
index 022b164a4..f64692e17 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/schema/SchemaResources.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/schema/SchemaResources.java
@@ -10,4 +10,5 @@ public class SchemaResources {
     List<String> setSchemaPaths;
     List<String> metadataSchemaPaths;
     List<String> destinationSchemaPaths;
+    List<String> submissionDataSchemaPaths;
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java b/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
index ecee117fa..a0201739d 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
@@ -12,6 +12,7 @@ import dev.fitko.fitconnect.api.domain.model.submission.*;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
 import dev.fitko.fitconnect.api.exceptions.*;
 
+import java.net.URI;
 import java.util.List;
 import java.util.UUID;
 
@@ -140,10 +141,11 @@ public interface Sender {
      * Tests if a given string is well-formed JSON syntax.
      *
      * @param json json string that is tested
+     * @param schemaUri URI of schema to validate against
      *
      * @return a {@link ValidationResult} with an optional error
      */
-    ValidationResult validateJsonFormat(SubmissionPayload json);
+    ValidationResult validateJsonFormat(String json, URI schemaUri);
 
     /**
      * Tests if a given string is well-formed XML syntax.
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java
index c5d747b3f..99057bf38 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java
@@ -4,6 +4,7 @@ import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
 
+import java.net.URI;
 import java.util.Map;
 
 /**
@@ -77,10 +78,11 @@ public interface ValidationService {
      * Tests if a given string is well-formed JSON syntax.
      *
      * @param json json string that is tested
+     * @param schemaUri URI of schema to validate against
      *
      * @return a {@link ValidationResult} with an optional error
      */
-    ValidationResult validateJsonFormat(String json);
+    ValidationResult validateJsonFormat(String json, URI schemaUri);
 
     /**
      * Tests if a given string is well-formed XML syntax.
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java b/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java
index 4970c09ef..523193e43 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java
@@ -11,7 +11,7 @@ import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.model.EncryptedSubmissionPayload;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 import dev.fitko.fitconnect.client.sender.strategies.SendEncryptedSubmissionStrategy;
 import dev.fitko.fitconnect.client.sender.strategies.SendNewSubmissionStrategy;
 import dev.fitko.fitconnect.client.util.ValidDataGuard;
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java b/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java
index c3f6b15ce..7410ce89e 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java
@@ -10,7 +10,7 @@ import dev.fitko.fitconnect.client.cli.batch.ImportRecord;
 import dev.fitko.fitconnect.client.cli.commands.*;
 import dev.fitko.fitconnect.client.cli.util.AttachmentDataType;
 import dev.fitko.fitconnect.client.sender.SubmissionBuilder;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 import dev.fitko.fitconnect.client.subscriber.ReceivedSubmission;
 import dev.fitko.fitconnect.client.subscriber.model.ReceivedAttachment;
 import dev.fitko.fitconnect.core.util.StopWatch;
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java b/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
index cfdd4ee6d..24eff7a4a 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
@@ -59,9 +59,9 @@ public final class ClientFactory {
 
     private static final String CONFIG_ENV_KEY_NAME = "FIT_CONNECT_CONFIG";
     private static final String SET_SCHEMA_DIR = "/set-schema";
-
     private static final String DESTINATION_SCHEMA_DIR = "/destination-schema";
     private static final String METADATA_SCHEMA_DIR = "/metadata-schema";
+    private static final String SUBMISSION_DATA_SCHEMA_DIR = "/submission-data-schema";
 
     private ClientFactory() {
     }
@@ -129,7 +129,7 @@ public final class ClientFactory {
     public static RoutingClient routingClient(final ApplicationConfig config) {
         LOGGER.info("Initializing routing client ...");
         final RestTemplate restTemplate = getRestTemplate(config, ApplicationConfigLoader.loadBuildInfo());
-        final SchemaProvider schemaProvider = getSchemaProvider();
+        final SchemaProvider schemaProvider = getSchemaProvider(restTemplate);
         final OAuthService authService = getSenderConfiguredAuthService(config, restTemplate);
 
         final MessageDigestService messageDigestService = getMessageDigestService();
@@ -145,7 +145,7 @@ public final class ClientFactory {
 
     private static Sender getSender(final ApplicationConfig config, final BuildInfo buildInfo) {
         final RestTemplate restTemplate = getRestTemplate(config, buildInfo);
-        final SchemaProvider schemaProvider = getSchemaProvider();
+        final SchemaProvider schemaProvider = getSchemaProvider(restTemplate);
         final MessageDigestService messageDigestService = getMessageDigestService();
 
         final CryptoService cryptoService = getCryptoService(messageDigestService);
@@ -162,7 +162,7 @@ public final class ClientFactory {
 
     private static Subscriber getSubscriber(final ApplicationConfig config, final BuildInfo buildInfo) {
         final RestTemplate restTemplate = getRestTemplate(config, buildInfo);
-        final SchemaProvider schemaProvider = getSchemaProvider();
+        final SchemaProvider schemaProvider = getSchemaProvider(restTemplate);
         final MessageDigestService messageDigestService = getMessageDigestService();
 
         final CryptoService cryptoService = getCryptoService(messageDigestService);
@@ -231,12 +231,13 @@ public final class ClientFactory {
         return new EventLogVerifier(keyService, validationService);
     }
 
-    private static SchemaProvider getSchemaProvider() {
+    private static SchemaProvider getSchemaProvider(RestTemplate restTemplate) {
         final List<String> setSchemaFiles = SchemaConfig.getSetSchemaFilePaths(SET_SCHEMA_DIR);
         final List<String> metadataSchemaFiles = SchemaConfig.getMetadataSchemaFileNames(METADATA_SCHEMA_DIR);
         final List<String> destinationSchemaFiles = SchemaConfig.getDestinationSchemaPaths(DESTINATION_SCHEMA_DIR);
-        final SchemaResources schemaResources = new SchemaResources(setSchemaFiles, metadataSchemaFiles, destinationSchemaFiles);
-        return new SchemaResourceProvider(schemaResources);
+        final List<String> submissionDataSchemaFiles = SchemaConfig.getSubmissionDataSchemaPaths(SUBMISSION_DATA_SCHEMA_DIR);
+        final SchemaResources schemaResources = new SchemaResources(setSchemaFiles, metadataSchemaFiles, destinationSchemaFiles, submissionDataSchemaFiles);
+        return new SchemaResourceProvider(restTemplate, schemaResources);
     }
 
     private static RoutingService getRoutingService(final ApplicationConfig config, final RestTemplate restTemplate) {
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/SubmissionBuilder.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/SubmissionBuilder.java
index 4298f6431..89a55fd23 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/SubmissionBuilder.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/SubmissionBuilder.java
@@ -2,7 +2,7 @@ package dev.fitko.fitconnect.client.sender;
 
 import dev.fitko.fitconnect.api.domain.model.metadata.data.MimeType;
 import dev.fitko.fitconnect.api.domain.model.submission.ServiceType;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 import dev.fitko.fitconnect.client.sender.steps.*;
 import lombok.Getter;
 import org.slf4j.Logger;
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/submission/SubmissionPayload.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/model/SubmissionPayload.java
similarity index 94%
rename from api/src/main/java/dev/fitko/fitconnect/api/domain/model/submission/SubmissionPayload.java
rename to client/src/main/java/dev/fitko/fitconnect/client/sender/model/SubmissionPayload.java
index f276501e0..fc7956049 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/submission/SubmissionPayload.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/model/SubmissionPayload.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.domain.model.submission;
+package dev.fitko.fitconnect.client.sender.model;
 
 import dev.fitko.fitconnect.api.domain.model.metadata.data.MimeType;
 import dev.fitko.fitconnect.api.domain.model.submission.ServiceType;
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/BuildStep.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/BuildStep.java
index a5e1defc9..2ecd116d6 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/BuildStep.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/BuildStep.java
@@ -1,6 +1,6 @@
 package dev.fitko.fitconnect.client.sender.steps;
 
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 
 public interface BuildStep {
 
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java
index fbe3e33e1..664109153 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java
@@ -2,6 +2,7 @@ package dev.fitko.fitconnect.client.sender.strategies;
 
 import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
+import dev.fitko.fitconnect.api.domain.model.destination.DestinationService;
 import dev.fitko.fitconnect.api.domain.model.metadata.ContentStructure;
 import dev.fitko.fitconnect.api.domain.model.metadata.Hash;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
@@ -27,7 +28,7 @@ import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
 import dev.fitko.fitconnect.api.exceptions.SubmissionNotCreatedException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.model.AttachmentPayload;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 import dev.fitko.fitconnect.core.util.StopWatch;
 import org.apache.tika.Tika;
 import org.slf4j.Logger;
@@ -40,6 +41,7 @@ import java.net.URLConnection;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java b/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java
index 0e15340da..12491f857 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java
@@ -7,7 +7,7 @@ import dev.fitko.fitconnect.api.domain.model.submission.ServiceType;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.model.EncryptedSubmissionPayload;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 
 import java.net.URI;
 import java.util.Objects;
@@ -136,7 +136,7 @@ public class ValidDataGuard {
     }
 
     private void checkJsonFormat(final SubmissionPayload submissionPayload) {
-        final ValidationResult validationResult = sender.validateJsonFormat(submissionPayload.getData());
+        final ValidationResult validationResult = sender.validateJsonFormat(submissionPayload.getData(), submissionPayload.getSchemaUri());
         if (validationResult.hasError()) {
             throw new IllegalArgumentException("Data is not in expected json format, please provide valid json: " + validationResult.getError().getMessage());
         }
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java b/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
index 3ae0ca490..a7ef9d0a2 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
@@ -23,7 +23,7 @@ import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.EncryptedSubmissionBuilder;
 import dev.fitko.fitconnect.client.sender.SubmissionBuilder;
 import dev.fitko.fitconnect.client.sender.model.EncryptedSubmissionPayload;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 import dev.fitko.fitconnect.client.testutil.LogCaptor;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -77,7 +77,7 @@ public class SenderClientTest {
         when(senderMock.sendSubmission(any())).thenReturn(expectedSubmission);
         when(senderMock.validatePublicKey(any())).thenReturn(ValidationResult.ok());
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
-        when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+        when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
         when(senderMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
 
 
@@ -182,7 +182,7 @@ public class SenderClientTest {
         when(senderMock.getDestination(any())).thenReturn(destination);
         when(senderMock.createSubmission(any())).thenReturn(announcedSubmission);
         when(senderMock.validatePublicKey(any())).thenReturn(ValidationResult.ok());
-        when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+        when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
 
         // When
@@ -513,7 +513,7 @@ public class SenderClientTest {
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
         when(senderMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
         when(senderMock.validateXmlFormat(any())).thenReturn(ValidationResult.ok());
-        when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+        when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
         when(senderMock.sendSubmission(any())).thenReturn(new Submission());
 
         return destinationId;
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/cli/CommandLineClientTest.java b/client/src/test/java/dev/fitko/fitconnect/client/cli/CommandLineClientTest.java
index ae4a9f465..61e54c21a 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/cli/CommandLineClientTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/cli/CommandLineClientTest.java
@@ -11,7 +11,7 @@ import dev.fitko.fitconnect.client.SubscriberClient;
 import dev.fitko.fitconnect.client.cli.batch.CsvImporter;
 import dev.fitko.fitconnect.client.cli.batch.ImportRecord;
 import dev.fitko.fitconnect.client.sender.model.EncryptedSubmissionPayload;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 import dev.fitko.fitconnect.client.subscriber.ReceivedSubmission;
 import dev.fitko.fitconnect.client.subscriber.model.ReceivedAttachment;
 import dev.fitko.fitconnect.client.subscriber.model.ReceivedData;
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/util/ValidDataGuardTest.java b/client/src/test/java/dev/fitko/fitconnect/client/util/ValidDataGuardTest.java
index a3e1453fe..ef356cb03 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/util/ValidDataGuardTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/util/ValidDataGuardTest.java
@@ -9,7 +9,7 @@ import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.EncryptedSubmissionBuilder;
 import dev.fitko.fitconnect.client.sender.SubmissionBuilder;
 import dev.fitko.fitconnect.client.sender.model.EncryptedSubmissionPayload;
-import dev.fitko.fitconnect.api.domain.model.submission.SubmissionPayload;
+import dev.fitko.fitconnect.client.sender.model.SubmissionPayload;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -56,7 +56,7 @@ class ValidDataGuardTest {
             destination.setServices(Set.of(service));
 
             when(senderMock.getDestination(any())).thenReturn(destination);
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final SubmissionPayload submissionPayload = SubmissionBuilder.Builder()
                     .withJsonData("\"test\": \"json\"",
@@ -107,7 +107,7 @@ class ValidDataGuardTest {
         void testMissingDestinationId() {
 
             // Given
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final SubmissionPayload submissionPayload = SubmissionBuilder.Builder()
                     .withJsonData("{}", URI.create("https://schema.fitko.de/fim/s00000000009_1.0.0.schema.json"))
@@ -126,7 +126,7 @@ class ValidDataGuardTest {
         void testInvalidServiceIdentifier() {
 
             // Given
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final SubmissionPayload submissionPayload = SubmissionBuilder.Builder()
                     .withJsonData("{\"test\" . \"data\"}",
@@ -145,7 +145,7 @@ class ValidDataGuardTest {
         void testMissingServiceIdentifier() {
 
             // Given
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final SubmissionPayload submissionPayload = SubmissionBuilder.Builder()
                     .withJsonData("{\"test\" . \"data\"}",
@@ -171,7 +171,7 @@ class ValidDataGuardTest {
             destination.setServices(Set.of(service));
 
             when(senderMock.getDestination(any())).thenReturn(destination);
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final SubmissionPayload submissionPayload = SubmissionBuilder.Builder()
                     .withJsonData("\"test\": \"json\"",
@@ -202,7 +202,7 @@ class ValidDataGuardTest {
             destination.setServices(Set.of(service));
 
             when(senderMock.getDestination(any())).thenReturn(destination);
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final SubmissionPayload submissionPayload = SubmissionBuilder.Builder()
                     .withJsonData("\"test\": \"json\"",
@@ -234,7 +234,7 @@ class ValidDataGuardTest {
             destination.setServices(Set.of(service));
 
             when(senderMock.getDestination(any())).thenReturn(destination);
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final SubmissionPayload submissionPayload = SubmissionBuilder.Builder()
                     .withJsonData("\"test\": \"json\"",
@@ -268,7 +268,7 @@ class ValidDataGuardTest {
             destination.setServices(Set.of(service));
 
             when(senderMock.getDestination(any())).thenReturn(destination);
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final EncryptedSubmissionPayload encryptedSubmissionPayload = EncryptedSubmissionBuilder.Builder()
                     .withEncryptedData("4Y0sJhadfrQnNZXeS7Pqh73FvtF")
@@ -321,7 +321,7 @@ class ValidDataGuardTest {
         void testMissingDestinationId() {
 
             // Given
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final EncryptedSubmissionPayload encryptedSubmissionPayload = EncryptedSubmissionBuilder.Builder()
                     .withEncryptedData("4Y0sJhadfrQnNZXeS7Pqh73FvtF")
@@ -341,7 +341,7 @@ class ValidDataGuardTest {
         void testInvalidServiceIdentifier() {
 
             // Given
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final EncryptedSubmissionPayload encryptedSubmissionPayload = EncryptedSubmissionBuilder.Builder()
                     .withEncryptedData("4Y0sJhadfrQnNZXeS7Pqh73FvtF")
@@ -361,7 +361,7 @@ class ValidDataGuardTest {
         void testMissingServiceIdentifier() {
 
             // Given
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final EncryptedSubmissionPayload encryptedSubmissionPayload = EncryptedSubmissionBuilder.Builder()
                     .withEncryptedData("4Y0sJhadfrQnNZXeS7Pqh73FvtF")
@@ -387,7 +387,7 @@ class ValidDataGuardTest {
             destination.setServices(Set.of(service));
 
             when(senderMock.getDestination(any())).thenReturn(destination);
-            when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+            when(senderMock.validateJsonFormat(any(), URI.create("urn:something"))).thenReturn(ValidationResult.ok());
 
             final EncryptedSubmissionPayload encryptedSubmissionPayload = EncryptedSubmissionBuilder.Builder()
                     .withEncryptedData("4Y0sJhadfrQnNZXeS7Pqh73FvtF")
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
index eb5cc289f..d58b74baf 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
@@ -6,7 +6,10 @@ import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.EventStatus;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
-import dev.fitko.fitconnect.api.domain.model.submission.*;
+import dev.fitko.fitconnect.api.domain.model.submission.CreateSubmission;
+import dev.fitko.fitconnect.api.domain.model.submission.Submission;
+import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
@@ -17,6 +20,7 @@ import dev.fitko.fitconnect.api.services.validation.ValidationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.URI;
 import java.util.List;
 import java.util.UUID;
 
@@ -112,9 +116,9 @@ public class SubmissionSender implements Sender {
     }
 
     @Override
-    public ValidationResult validateJsonFormat(final SubmissionPayload json) {
+    public ValidationResult validateJsonFormat(final String json, final URI schemaUri) {
         LOGGER.info("Validating data json format");
-        return validationService.validateJsonFormat(json);
+        return validationService.validateJsonFormat(json, schemaUri);
     }
 
     @Override
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java b/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java
index cb5cfef45..cb81e6849 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java
@@ -9,6 +9,7 @@ import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestTemplate;
 
 import java.io.BufferedReader;
 import java.io.InputStream;
@@ -32,15 +33,22 @@ public class SchemaResourceProvider implements SchemaProvider {
 
     private final Map<URI, String> destinationSchemas;
 
+    private final Map<URI, String> submissionDataSchemas;
+
+    private final RestTemplate restTemplate;
+
     private static final ObjectMapper MAPPER = new ObjectMapper();
 
-    public SchemaResourceProvider(final SchemaResources schemaResources) {
+    public SchemaResourceProvider(final RestTemplate restTemplate, final SchemaResources schemaResources) {
         setSchemas = new HashMap<>();
         metadataSchemas = new HashMap<>();
         destinationSchemas = new HashMap<>();
+        submissionDataSchemas = new HashMap<>();
+        this.restTemplate = restTemplate;
         populateSetSchemas(schemaResources.getSetSchemaPaths());
         populateMetadataSchemas(schemaResources.getMetadataSchemaPaths());
         populateDestinationSchemas(schemaResources.getDestinationSchemaPaths());
+        populateSubmissionDataSchemas(schemaResources.getSubmissionDataSchemaPaths());
     }
 
     private void populateMetadataSchemas(final List<String> metadataSchemaPaths) {
@@ -58,6 +66,11 @@ public class SchemaResourceProvider implements SchemaProvider {
         getResourceFiles(destinationSchemaPaths).forEach(this::addDestinationSchema);
     }
 
+    private void populateSubmissionDataSchemas(final List<String> submissionDataSchemaPaths) {
+        LOGGER.info("Initializing submission data schemas");
+        getResourceFiles(submissionDataSchemaPaths).forEach(this::addSubmissionDataSchema);
+    }
+
     @Override
     public boolean isAllowedSetSchema(final URI schemaUri) {
         return schemaVersionMatchesPattern(schemaUri, ALLOWED_SCHEMA_PATTERN);
@@ -99,14 +112,19 @@ public class SchemaResourceProvider implements SchemaProvider {
     @Override
     public String loadSubmissionDataSchema(final URI schemaUri) throws SchemaNotFoundException {
 
-        String schema;
-        if(schemaUri.toString().matches("http.+")) {
-            schema = "load schema from remote";
+        if (schemaUri.toString().matches("http.+")) {
+            try {
+                return restTemplate.getForEntity(schemaUri, String.class).getBody();
+            } catch (Exception exception) {
+                throw new SchemaNotFoundException("Submission data schema " + schemaUri + " is not available.", exception);
+            }
         }
-        schema = destinationSchemas.get(schemaUri);
+
+        final String schema = submissionDataSchemas.get(schemaUri);
         if (schema == null) {
-            throw new SchemaNotFoundException("Destination schema " + schemaUri.toString() + " is not available.");
+            throw new SchemaNotFoundException("Submission data schema " + schemaUri + " is not available.");
         }
+
         return schema;
     }
 
@@ -122,6 +140,10 @@ public class SchemaResourceProvider implements SchemaProvider {
         destinationSchemas.put(readIdFromSchema(schema), schema);
     }
 
+    private void addSubmissionDataSchema(final String schema) {
+        submissionDataSchemas.put(readIdFromSchema(schema), schema);
+    }
+
     private URI readIdFromSchema(final String schema) {
         try {
             return URI.create(MAPPER.readTree(schema).get("$id").asText());
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java b/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
index 37455f13c..31a70c213 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
@@ -33,7 +33,6 @@ import org.xml.sax.XMLReader;
 
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParserFactory;
-
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.InetSocketAddress;
@@ -152,9 +151,11 @@ public class DefaultValidationService implements ValidationService {
     }
 
     @Override
-    public ValidationResult validateJsonFormat(final String json) {
+    public ValidationResult validateJsonFormat(final String json, final URI schemaUri) {
+
+        String schema = schemaProvider.loadSubmissionDataSchema(schemaUri);
         try {
-            MAPPER.readTree(json);
+            returnValidationResult(SCHEMA_FACTORY_DRAFT_2007.getSchema(schema).validate(MAPPER.readTree(json)));
         } catch (final JacksonException e) {
             return ValidationResult.error(e);
         }
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
index 46a8f8134..788783120 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
@@ -37,6 +37,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
+import java.net.URI;
 import java.text.ParseException;
 import java.util.Collections;
 import java.util.Date;
@@ -54,6 +55,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -331,10 +333,10 @@ public class SubmissionSenderTest {
     void testValidJsonFormatValidation() {
 
         // Given
-        when(validationServiceMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validateJsonFormat(any(), eq(URI.create("urn:something")))).thenReturn(ValidationResult.ok());
 
         // When
-        final ValidationResult validationResult = underTest.validateJsonFormat("{}");
+        final ValidationResult validationResult = underTest.validateJsonFormat("{}", URI.create("urn:something"));
 
         //Then
         assertTrue(validationResult.isValid());
@@ -344,10 +346,10 @@ public class SubmissionSenderTest {
     void testInvalidJsonFormatValidation() {
 
         // Given
-        when(validationServiceMock.validateJsonFormat(any())).thenReturn(ValidationResult.error(new ValidationException("Invalid json")));
+        when(validationServiceMock.validateJsonFormat(any(), eq(URI.create("urn:something")))).thenReturn(ValidationResult.error(new ValidationException("Invalid json")));
 
         // When
-        final ValidationResult validationResult = underTest.validateJsonFormat("$%&/()");
+        final ValidationResult validationResult = underTest.validateJsonFormat("$%&/()", URI.create("urn:something"));
 
         //Then
         assertTrue(validationResult.hasError());
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java
index a790df463..c894ac53a 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java
@@ -27,6 +27,7 @@ import dev.fitko.fitconnect.core.schema.SchemaResourceProvider;
 import dev.fitko.fitconnect.core.validation.DefaultValidationService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.springframework.web.client.RestTemplate;
 
 import java.io.File;
 import java.io.IOException;
@@ -41,6 +42,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.mock;
 
 class SecurityEventTokenServiceTest {
 
@@ -61,8 +63,9 @@ class SecurityEventTokenServiceTest {
         final List<String> setSchemas = SchemaConfig.getSetSchemaFilePaths("/set-schema");
         final List<String> metadataSchemas = SchemaConfig.getMetadataSchemaFileNames("/metadata-schema");
         final List<String> destinationSchemas = SchemaConfig.getDestinationSchemaPaths("/destination-schema");
-        final SchemaResources schemaResources = new SchemaResources(setSchemas, metadataSchemas, destinationSchemas);
-        final SchemaProvider schemaProvider = new SchemaResourceProvider(schemaResources);
+        final List<String> submissionDataSchemas = SchemaConfig.getSubmissionDataSchemaPaths("/submission-data-schema");
+        final SchemaResources schemaResources = new SchemaResources(setSchemas, metadataSchemas, destinationSchemas, submissionDataSchemas);
+        final SchemaProvider schemaProvider = new SchemaResourceProvider(mock(RestTemplate.class), schemaResources);
 
         validationService = new DefaultValidationService(config, new HashService(), schemaProvider);
         underTest = new SecurityEventTokenService(config, validationService, signingKey);
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java b/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java
index 0b1899e94..764644acc 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java
@@ -8,6 +8,7 @@ import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.springframework.web.client.RestTemplate;
 
 import java.net.URI;
 import java.util.List;
@@ -15,6 +16,7 @@ import java.util.List;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.mock;
 
 class SchemaResourceProviderTest {
 
@@ -25,8 +27,9 @@ class SchemaResourceProviderTest {
         final List<String> setSchemas = SchemaConfig.getSetSchemaFilePaths("/set-schema");
         final List<String> metadataSchemas = SchemaConfig.getMetadataSchemaFileNames("/metadata-schema");
         final List<String> destinationSchemas = SchemaConfig.getDestinationSchemaPaths("/destination-schema");
-        final SchemaResources schemaResources = new SchemaResources(setSchemas, metadataSchemas, destinationSchemas);
-        underTest = new SchemaResourceProvider(schemaResources);
+        final List<String> submissionDataSchemas = SchemaConfig.getSubmissionDataSchemaPaths("/submission-data-schema");
+        final SchemaResources schemaResources = new SchemaResources(setSchemas, metadataSchemas, destinationSchemas, submissionDataSchemas);
+        underTest = new SchemaResourceProvider(mock(RestTemplate.class), schemaResources);
     }
 
     @Test
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
index ba7f6ec3e..ede11c83a 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
@@ -32,6 +32,7 @@ import dev.fitko.fitconnect.jwkvalidator.exceptions.JWKValidationException;
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.springframework.web.client.RestTemplate;
 
 import java.io.IOException;
 import java.net.URI;
@@ -59,8 +60,9 @@ class DefaultValidationServiceTest {
         final List<String> setSchemas = SchemaConfig.getSetSchemaFilePaths("/set-schema");
         final List<String> metadataSchemas = SchemaConfig.getMetadataSchemaFileNames("/metadata-schema");
         final List<String> destinationSchemas = SchemaConfig.getDestinationSchemaPaths("/destination-schema");
-        final SchemaResources schemaResources = new SchemaResources(setSchemas, metadataSchemas, destinationSchemas);
-        schemaProvider = new SchemaResourceProvider(schemaResources);
+        final List<String> submissionDataSchemas = SchemaConfig.getSubmissionDataSchemaPaths("/submission-data-schema");
+        final SchemaResources schemaResources = new SchemaResources(setSchemas, metadataSchemas, destinationSchemas, submissionDataSchemas);
+        schemaProvider = new SchemaResourceProvider(mock(RestTemplate.class), schemaResources);
         underTest = new DefaultValidationService(config, hashService, schemaProvider);
     }
 
@@ -412,7 +414,7 @@ class DefaultValidationServiceTest {
         final var validJson = getResource("/valid_json_data.json");
 
         // When
-        final ValidationResult validationResult = underTest.validateJsonFormat(validJson);
+        final ValidationResult validationResult = underTest.validateJsonFormat(validJson, URI.create("urn:something"));
 
         // Then
         assertTrue(validationResult.isValid());
@@ -425,7 +427,7 @@ class DefaultValidationServiceTest {
         final var invalidJson = getResource("/invalid_json_data.json");
 
         // When
-        final ValidationResult validationResult = underTest.validateJsonFormat(invalidJson);
+        final ValidationResult validationResult = underTest.validateJsonFormat(invalidJson, URI.create("urn:something"));
 
         // Then
         assertFalse(validationResult.isValid());
-- 
GitLab