diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/destination/Destination.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/destination/Destination.java new file mode 100644 index 0000000000000000000000000000000000000000..21f58696efd1b89283e25d232d8136374df4cb8e --- /dev/null +++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/destination/Destination.java @@ -0,0 +1,11 @@ +package de.fitko.fitconnect.api.domain.model.destination; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class Destination { + + @JsonProperty("encryptionKid") + UUID encryptionKid; +} diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ContentStructure.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ContentStructure.java index 8e06ae6c6d144d63ab4ab5cebe4bc1179cfc5019..1fbebed0cd06063788cd3eb553ccbc4d6854e17d 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ContentStructure.java +++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ContentStructure.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; +import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -19,6 +20,7 @@ import java.util.Map; }) @Getter @Setter +@Builder public class ContentStructure { @JsonProperty("data") diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Attachment.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Attachment.java index 8147f76647a4629f9dfc44787fcdf26e4e774f33..c88f7b1ac64774e86aaedee57f2767889b0b3abb 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Attachment.java +++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Attachment.java @@ -6,8 +6,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Hash__1; import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Signature__1; +import lombok.Builder; import lombok.Getter; import lombok.Setter; +import lombok.Value; import java.util.Map; import java.util.UUID; @@ -24,6 +26,7 @@ import java.util.UUID; }) @Getter @Setter +@Builder public class Attachment { @JsonProperty("hash") diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Submission.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionRequest.java similarity index 95% rename from api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Submission.java rename to api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionRequest.java index c2c8da391c7c1e80d66ac4b8449d80b9da70d77f..0b691c7ba65675947f2b5548ed4df7dcfbba27ec 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Submission.java +++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionRequest.java @@ -14,7 +14,7 @@ import java.util.UUID; @Setter @Value @Builder -public class Submission { +public class SubmissionRequest { @JsonProperty("destinationId") UUID destinationId; diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionResponse.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..d15106e5304873e1a0847526b87727de6ee7cf2e --- /dev/null +++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionResponse.java @@ -0,0 +1,19 @@ +package de.fitko.fitconnect.api.domain.model.submission; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; + +import java.util.UUID; + +@Value +public class SubmissionResponse { + + @JsonProperty("destinationId") + private final UUID destinationId; + + @JsonProperty("submissionId") + private final UUID submissionId; + + @JsonProperty("caseId") + private final UUID caseId; +} diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/validation/ValidationResult.java b/api/src/main/java/de/fitko/fitconnect/api/domain/validation/ValidationResult.java index d1f8f89b2314d5bfa220f1857a82afef550524ab..0293bfbeddd683860d1d44d37dd93b1b2d93d04e 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/domain/validation/ValidationResult.java +++ b/api/src/main/java/de/fitko/fitconnect/api/domain/validation/ValidationResult.java @@ -34,6 +34,10 @@ public class ValidationResult { return this.isValid; } + public boolean hasErrors() { + return !isValid || !errors.isEmpty(); + } + public List<Exception> getErrors() { return this.errors; } diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/AttachmentUploadError.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/AttachmentUploadError.java index 3843c9eb0cec27fd49c61d0a53980e4c5a33f9de..819a018fb5752f750856e102e13cad22197905eb 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/AttachmentUploadError.java +++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/AttachmentUploadError.java @@ -1,10 +1,10 @@ package de.fitko.fitconnect.api.exceptions.client; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; /** - * Error during upload of the {@link Submission}s {@link Attachment}s + * Error during upload of the {@link SubmissionRequest}s {@link Attachment}s */ public class AttachmentUploadError extends RuntimeException { diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionCreationError.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionCreationError.java index 2074888d42301c0b2f58726d5bd30bb1573bc154..c83e00b78d666f7ae6ce318be3720c5f46d14a08 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionCreationError.java +++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionCreationError.java @@ -1,9 +1,9 @@ package de.fitko.fitconnect.api.exceptions.client; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; /** - * Error during the creation process of a {@link Submission} + * Error during the creation process of a {@link SubmissionRequest} */ public class SubmissionCreationError extends RuntimeException { diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionSendError.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionSendError.java index 6d0ea812cc00fd1f2186e7cc5e1bc69eecd36f17..028ce74bd2dd36b1f11dc07d85b90fec5f955363 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionSendError.java +++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/client/SubmissionSendError.java @@ -1,9 +1,9 @@ package de.fitko.fitconnect.api.exceptions.client; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; /** - * Error during the creation process of a {@link Submission} + * Error during the creation process of a {@link SubmissionRequest} */ public class SubmissionSendError extends RuntimeException { diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/Sender.java b/api/src/main/java/de/fitko/fitconnect/api/services/Sender.java index d9ba7a1e37a16144da449811bfff787674df1110..f6db1e33166b7da719e48806474c45cd0ee687dc 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/services/Sender.java +++ b/api/src/main/java/de/fitko/fitconnect/api/services/Sender.java @@ -5,17 +5,20 @@ import de.fitko.fitconnect.api.domain.auth.OAuthToken; import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionResponse; import de.fitko.fitconnect.api.domain.validation.ValidationResult; +import java.io.File; import java.util.List; import java.util.Optional; +import java.util.UUID; /** * A technical system that creates a submission via the FIT-Connect Submission API. * <p> * The Sender acts as a common interface wrapping all client functionality to authenticate <p> - * and create a valid {@link Submission} including encrypted {@link Data} and {@link Attachment}s + * and create a valid {@link SubmissionRequest} including encrypted {@link Data} and {@link Attachment}s * * @see <a href="https://docs.fitko.de/fit-connect/docs/sending/overview">Sending Submissions</a> */ @@ -68,19 +71,37 @@ public interface Sender { Optional<Metadata> createMetadata(final Data data, final List<Attachment> attachments); /** - * Creates and announces a new {@link Submission} + * Creates and uploads a {@link Metadata} object that contains {@link Data}, {@link Attachment} and their hashes so a subscriber + * can verify the integrity. + * + * @param attachments a list of binary {@link Attachment}s + * @return a valid {@link Metadata} object with hashed {@link Data} and {@link Attachment}s, is empty if an error occurred + */ + Optional<Metadata> createMetadata(final List<Attachment> attachments); + + /** + * Creates and announces a new {@link SubmissionRequest} * * @param submission with a destinationId, a list of attachmentIds and a serviceType * @return the created submission */ - Optional<Submission> createSubmission(Submission submission); + Optional<SubmissionResponse> createSubmission(SubmissionRequest submission); + /** - * Posts the announced {@link Submission} + * Posts the announced {@link SubmissionRequest} + * * @param submissionId identifier of the announced submission - * @param metadata the encrypted metadata + * @param encryptedMetadata the encrypted metadata * * @return the submission */ - Optional<Submission> sendSubmission(String submissionId, Metadata metadata); + Optional<SubmissionResponse> sendSubmission(UUID submissionId, Metadata encryptedMetadata); + + + void uploadAttachments(UUID submissionId, List<Attachment> encryptedAttachments); + + Optional<Attachment> createAttachment(File file); + + RSAKey getEncryptionKeyForDestination(UUID destinationId); } diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/Subscriber.java b/api/src/main/java/de/fitko/fitconnect/api/services/Subscriber.java index 9dd45d25a7e554832cab27e7fa54de51f6e4984a..d2d175dc40e3cb7859893380f75d0504ca1c7517 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/services/Subscriber.java +++ b/api/src/main/java/de/fitko/fitconnect/api/services/Subscriber.java @@ -6,7 +6,7 @@ import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; import de.fitko.fitconnect.api.domain.model.event.SecurityEventToken; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import de.fitko.fitconnect.api.domain.validation.ValidationResult; import java.util.List; @@ -66,7 +66,7 @@ public interface Subscriber { ValidationResult validateMetadataHashValues(final Metadata metadata); /** - * Polls for available {@link Submission}s on the given destinationId + * Polls for available {@link SubmissionRequest}s on the given destinationId * * @param destinationId restricts the query to a specific destination * @param limit number of submissions in result (max. is 500) @@ -74,17 +74,17 @@ public interface Subscriber { * * @return list of found submissions */ - List<Submission> pollAvailableSubmissions(String destinationId, int limit, int offset); + List<SubmissionRequest> pollAvailableSubmissions(String destinationId, int limit, int offset); /** - * Gets a specific {@link Submission}. + * Gets a specific {@link SubmissionRequest}. * - * @param submissionId the unique identifier of a {@link Submission} + * @param submissionId the unique identifier of a {@link SubmissionRequest} * * @return the optional submission, is empty if none was found * */ - Optional<Submission> getSubmission(String submissionId); + Optional<SubmissionRequest> getSubmission(String submissionId); Optional<SecurityEventToken> createSecurityEventToken(Data data, Attachment attachment, RSAKey privateKey); diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/auth/OAuthService.java b/api/src/main/java/de/fitko/fitconnect/api/services/auth/OAuthService.java index fba2424f370a0a4d58f2bcf415e1cf79f489239b..c6683988ee5d7b2d25b6784ee2d03954d597dc99 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/services/auth/OAuthService.java +++ b/api/src/main/java/de/fitko/fitconnect/api/services/auth/OAuthService.java @@ -1,12 +1,12 @@ package de.fitko.fitconnect.api.services.auth; import de.fitko.fitconnect.api.domain.auth.OAuthToken; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import de.fitko.fitconnect.api.exceptions.internal.AuthenticationException; /** * A Service that provides an interface to authenticate against the Fit-Connect API in order - * to send a {@link Submission}. + * to send a {@link SubmissionRequest}. * * @see <a href="https://docs.fitko.de/fit-connect/docs/getting-started/authentication">Fit-Connect documentation on authentication</a> * diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/crypto/CryptoService.java b/api/src/main/java/de/fitko/fitconnect/api/services/crypto/CryptoService.java index 465fdbb77da11d7655bcf679883954895549a23f..9a493990b7b217585cc667136a1ddfed1dbc5927 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/services/crypto/CryptoService.java +++ b/api/src/main/java/de/fitko/fitconnect/api/services/crypto/CryptoService.java @@ -3,12 +3,12 @@ package de.fitko.fitconnect.api.services.crypto; import com.nimbusds.jose.jwk.RSAKey; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import de.fitko.fitconnect.api.exceptions.internal.DecryptionException; import de.fitko.fitconnect.api.exceptions.internal.EncryptionException; /** - * A service that allows to encrypt and decrypt {@link Data} and {@link Attachment}s of a {@link Submission} via JWE. + * A service that allows to encrypt and decrypt {@link Data} and {@link Attachment}s of a {@link SubmissionRequest} via JWE. * * @see <a href="https://datatracker.ietf.org/doc/html/rfc7516">JSON-Web-Encryption</a> */ diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/metadata/MetadataService.java b/api/src/main/java/de/fitko/fitconnect/api/services/metadata/MetadataService.java index e43dbca1bfbd34dd141530762f272ffa5ccd9771..2071fdcf411078b524314742b431824ca5f4ac64 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/services/metadata/MetadataService.java +++ b/api/src/main/java/de/fitko/fitconnect/api/services/metadata/MetadataService.java @@ -3,12 +3,12 @@ package de.fitko.fitconnect.api.services.metadata; import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import java.util.List; /** - * Provides the generation of correct and valid {@link Metadata} for a {@link Submission} + * Provides the generation of correct and valid {@link Metadata} for a {@link SubmissionRequest} * * @see * <a href="https://docs.fitko.de/fit-connect/docs/metadataoverview">Metadata</a> and diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/validation/MetadataValidator.java b/api/src/main/java/de/fitko/fitconnect/api/services/validation/MetadataValidator.java index 22ecc04545091b81c649f8987e3c62474832b377..e788b315320a8fb9a32279131061e18f9f12459b 100644 --- a/api/src/main/java/de/fitko/fitconnect/api/services/validation/MetadataValidator.java +++ b/api/src/main/java/de/fitko/fitconnect/api/services/validation/MetadataValidator.java @@ -3,11 +3,11 @@ package de.fitko.fitconnect.api.services.validation; import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import de.fitko.fitconnect.api.domain.validation.ValidationResult; /** - * A validator that ensure the integrity of the transferred {@link Metadata} of a {@link Submission}. + * A validator that ensure the integrity of the transferred {@link Metadata} of a {@link SubmissionRequest}. * Both schema-validation and hash value validation is provided. * * @see <a href="https://docs.fitko.de/fit-connect/docs/sending/metadata#integrity">Metadata Integrity</a> diff --git a/client/src/main/java/de/fitko/fitconnect/TestRunner.java b/client/src/main/java/de/fitko/fitconnect/TestRunner.java index ec74ce06dc6354afdc10b453de99ad756f42e410..d2f148e550148a0d25e3545e30bda020d57ec77f 100644 --- a/client/src/main/java/de/fitko/fitconnect/TestRunner.java +++ b/client/src/main/java/de/fitko/fitconnect/TestRunner.java @@ -1,44 +1,50 @@ package de.fitko.fitconnect; -import de.fitko.fitconnect.api.domain.model.metadata.Metadata; -import de.fitko.fitconnect.api.domain.model.metadata.data.Data; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import de.fitko.fitconnect.client.SenderClient; import de.fitko.fitconnect.client.SubscriberClient; import java.util.Collections; import java.util.List; -import java.util.Optional; +import java.util.UUID; public class TestRunner { public static void main(String[] args) { + senderSample(); + } - var clientId = "781f6213-0f0f-4a79-9372-e7187ffda98b"; - var secret = "PnzR8Vbmhpv_VwTkT34wponqXWK8WBm-LADlryYdV4o"; + private static void senderSample() { - senderSample(clientId, secret); - subscriberSample(clientId, secret); - } + // Without data + SenderClient.build() + .withDestination(UUID.randomUUID()) + .withAttachments(Collections.emptyList()) + .submit(); - private static void senderSample(String clientId, String secret) { - final Optional<Submission> sentSubmission = SenderClient.builder() - .authenticate(clientId, secret) - .createSubmission(Submission.builder().build()) - .uploadAttachments(Collections.emptyList()) - .sendSubmission(Metadata.builder().build(), Data.builder().build()); + // With data + SenderClient.build() + .withDestination(UUID.randomUUID()) + .withAttachments(Collections.emptyList()) + .withData("some json or xml") + .submit(); } - private static void subscriberSample(String clientId, String secret) { + private static void subscriberSample() { + + var clientId = "781f6213-0f0f-4a79-9372-e7187ffda98b"; + var secret = "PnzR8Vbmhpv_VwTkT34wponqXWK8WBm-LADlryYdV4o"; + + // #1 authenticate var authenticatedClient = SubscriberClient.builder().authenticate(clientId, secret); // #2 poll for available submissions - List<Submission> availableSubmissions = authenticatedClient.getAvailableSubmissions("someDestinationId"); + List<SubmissionRequest> availableSubmissions = authenticatedClient.getAvailableSubmissions("someDestinationId"); // #3 get a submission and its data var confirmedSubmission = authenticatedClient - .requestSubmission(Submission.builder().build()) + .requestSubmission(SubmissionRequest.builder().build()) .requestMetadata() .requestAttachments() .confirmSubmission(); diff --git a/client/src/main/java/de/fitko/fitconnect/client/Client.java b/client/src/main/java/de/fitko/fitconnect/client/Client.java index d066e86376e1ccb03eac4231233755d8bb6a37a7..dca737f162b6db2ba57056def4f6752af7001c49 100644 --- a/client/src/main/java/de/fitko/fitconnect/client/Client.java +++ b/client/src/main/java/de/fitko/fitconnect/client/Client.java @@ -2,6 +2,8 @@ package de.fitko.fitconnect.client; import com.google.inject.Guice; import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.name.Names; import de.fitko.fitconnect.dependency.SdkModule; public abstract class Client { @@ -11,4 +13,8 @@ public abstract class Client { static <T> T getService(Class<T> clazz){ return injector.getInstance(clazz); } + + static String getProperty(String propertyName){ + return injector.getInstance(Key.get(String.class, Names.named(propertyName))); + } } diff --git a/client/src/main/java/de/fitko/fitconnect/client/SenderClient.java b/client/src/main/java/de/fitko/fitconnect/client/SenderClient.java index ebadcbcf3f0e622a402e8efe4eab4ce389ad2e4c..d76d5608139d65c42aceb3c364dea17f674b03b3 100644 --- a/client/src/main/java/de/fitko/fitconnect/client/SenderClient.java +++ b/client/src/main/java/de/fitko/fitconnect/client/SenderClient.java @@ -1,113 +1,190 @@ package de.fitko.fitconnect.client; +import com.nimbusds.jose.jwk.RSAKey; import de.fitko.fitconnect.api.domain.auth.OAuthToken; import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; -import de.fitko.fitconnect.api.domain.model.submission.Submission; -import de.fitko.fitconnect.api.exceptions.client.AttachmentUploadError; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionResponse; +import de.fitko.fitconnect.api.domain.validation.ValidationResult; import de.fitko.fitconnect.api.exceptions.client.ClientNotAuthenticated; import de.fitko.fitconnect.api.services.Sender; +import java.io.File; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; /** - * A fluent client for announcing and handing in a {@link Submission} + * A fluent client for announcing and handing in a {@link SubmissionRequest} */ public class SenderClient extends Client { - private SenderClient() {} + private SenderClient() { + } - public static Authenticate builder() { - return new ClientBuilder(getService(Sender.class)); + public static WithDestination build() { + final Sender sender = getService(Sender.class); + final String clientId = getProperty("clientId"); + final String secret = getProperty("clientSecret"); + return new ClientBuilder(sender, clientId, secret); } - public interface Authenticate { + public interface WithDestination { + /** - * Authenticates the client against the FitConnect-Auth-API and retrieves an {@link OAuthToken}. - * - * @param clientId a unique client identifier - * @param secret the applications secret key - * @param scope 1 or more client scopes that determine if a submission is accepted by the client - * @return the step for creating a new submission if the authentication was successful + * Configures the client for the given destination and load the public key * - * @throws ClientNotAuthenticated if the OAuth token could not be retrieved + * @param destinationId the clients destination + * @return the upload step for attachments */ - CreateSubmission authenticate(String clientId, String secret, String... scope) throws ClientNotAuthenticated; + WithAttachments withDestination(UUID destinationId); } - public interface AttachmentUpload { + public interface WithAttachments { + /** - * Encrypts and Uploads all {@link Attachment}s to the FitConnect-API - * - * @param attachments a list of attachments that are uploaded - * @return the step for sending the submission if the creating step was successful + * Sends the submission with a list of attachments * - * @throws ClientNotAuthenticated if the client is not authenticated - * @throws AttachmentUploadError if the upload failed + * @param attachments that are sent with the submission + * @return the step where additional data can be added to the submission */ - SendSubmission uploadAttachments(List<Attachment> attachments); + WithData withAttachments(List<File> attachments); } - public interface CreateSubmission { - /** - * Creates and announces a new {@link Submission} - * - * @param submission the new {@link Submission} that should be created on server-side - * @return the step for upload the attachments if the creation was successful - */ - AttachmentUpload createSubmission(Submission submission); + public interface WithData { + + Submit withData(String data); + + Submit withData(byte[] data); + + SubmissionResponse submit(); } - public interface SendSubmission { - /** - * Encrypts and uploads the {@link Metadata} and {@link Data} and hands in the {@link Submission} at - * the defined destination-id. - * - * @param metaData the submissions {@link Metadata} - * @param data the submissions {@link Data} - * - * @return the finally uploaded and sent submission, empty if an error occurred - */ - Optional<Submission> sendSubmission(Metadata metaData, Data data); + public interface Submit { + SubmissionResponse submit(); } - public static class ClientBuilder implements Authenticate, AttachmentUpload, CreateSubmission, SendSubmission { + public static class ClientBuilder implements WithDestination, WithAttachments, WithData, Submit { private Sender sender; - private Optional<OAuthToken> token; + private final String clientId; + private final String secret; - private ClientBuilder(Sender sender) { + private UUID destinationId; + private Data data; + private List<Attachment> attachments; + private RSAKey encryptionKey; + private UUID submissionId; + + public ClientBuilder(Sender sender, String clientId, String secret) { this.sender = sender; + this.clientId = clientId; + this.secret = secret; + authenticate(); } @Override - public CreateSubmission authenticate(String clientId, String secret, String... scope) { - this.token = sender.retrieveOAuthToken(clientId, secret, scope); - checkIfAuthenticated(); + public WithAttachments withDestination(UUID destinationId) { + final RSAKey encryptionKey = sender.getEncryptionKeyForDestination(destinationId); + final ValidationResult validationResult = sender.validatePublicKey(encryptionKey); + if(validationResult.hasErrors()){ + throw new RuntimeException("Public encryption key is not valid"); + } + this.encryptionKey = encryptionKey; return this; } @Override - public SendSubmission uploadAttachments(List<Attachment> attachments) { + public Submit withData(String data) { + return withData(data.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public Submit withData(byte[] data) { + var unencryptedData = Data.builder().build(); + final Optional<Data> encryptedData = sender.encryptSubmissionData(encryptionKey, unencryptedData); + if(encryptedData.isEmpty()){ + throw new RuntimeException("Data could not be encrypted"); + } + this.data = encryptedData.get(); return this; } @Override - public AttachmentUpload createSubmission(Submission submission) { + public WithData withAttachments(List<File> attachmentFiles) { + final List<Attachment> attachments = readFilesToAttachments(attachmentFiles); + final SubmissionResponse createdSubmission = createSubmission(destinationId, attachments); + this.submissionId = createdSubmission.getSubmissionId(); + final List<Attachment> encryptedAttachments = encryptAttachments(encryptionKey, attachments); + sender.uploadAttachments(submissionId, encryptedAttachments); return this; } @Override - public Optional<Submission> sendSubmission(Metadata metaData, Data data) { - throw new UnsupportedOperationException("not yet implemented"); + public SubmissionResponse submit() { + final Optional<Metadata> metadata = getMetadata(); + if(metadata.isEmpty()){ + throw new RuntimeException("Metadata could not be created"); + } + return sender.sendSubmission(submissionId, metadata.get()).orElseThrow(); + } + + private Optional<Metadata> getMetadata() { + return this.data == null ? sender.createMetadata(attachments) : sender.createMetadata(data, attachments); + } + + private List<Attachment> encryptAttachments(RSAKey encryptionKey, List<Attachment> attachments) { + return attachments.stream().map(attachment -> encryptAttachment(encryptionKey, attachment)).collect(Collectors.toList()); + } + + private Attachment encryptAttachment(RSAKey encryptionKey, Attachment attachment) { + final Optional<Attachment> encryptedAttachment = sender.encryptAttachment(encryptionKey, attachment); + if (encryptedAttachment.isEmpty()) { + throw new RuntimeException("Attachment could not be encrypted"); + } + return encryptedAttachment.get(); + } + + private SubmissionResponse createSubmission(UUID destinationId, List<Attachment> attachments) { + final SubmissionRequest request = SubmissionRequest.builder() + .destinationId(destinationId) + .announcedAttachments(asListOfAttachmentsIds(attachments)) + .build(); + + final Optional<SubmissionResponse> response = sender.createSubmission(request); + if (response.isEmpty()) { + throw new RuntimeException("Could not create submission"); + } + return response.get(); + } + + private List<UUID> asListOfAttachmentsIds(List<Attachment> attachments) { + return attachments.stream().map(attachment -> attachment.getAttachmentId()).collect(Collectors.toList()); + } + + private void authenticate() { + final Optional<OAuthToken> oAuthToken = sender.retrieveOAuthToken(clientId, secret); + if (oAuthToken.isEmpty()) { + throw new ClientNotAuthenticated("Client is not authenticated, please authenticate first"); + } + } + + private List<Attachment> readFilesToAttachments(List<File> attachmentFiles) { + return attachmentFiles.stream() + .map(this::readFileToAttachment) + .collect(Collectors.toList()); } - private void checkIfAuthenticated(){ - if(this.token == null || this.token.isEmpty()){ - throw new ClientNotAuthenticated("Not authenticated, please authenticate first"); + private Attachment readFileToAttachment(File file) { + final Optional<Attachment> attachment = sender.createAttachment(file); + if(attachment.isEmpty()){ + throw new RuntimeException("Attachment could not be created"); } + return attachment.get(); } } } \ No newline at end of file diff --git a/client/src/main/java/de/fitko/fitconnect/client/SubscriberClient.java b/client/src/main/java/de/fitko/fitconnect/client/SubscriberClient.java index 7faaa4d010aa7250d6b113378d11ee3f13fc8d5f..62e74c2a9f91e10b162e2717a3f92837c357eeec 100644 --- a/client/src/main/java/de/fitko/fitconnect/client/SubscriberClient.java +++ b/client/src/main/java/de/fitko/fitconnect/client/SubscriberClient.java @@ -3,7 +3,7 @@ package de.fitko.fitconnect.client; import de.fitko.fitconnect.api.domain.auth.OAuthToken; import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import de.fitko.fitconnect.api.services.Subscriber; import java.util.Collections; @@ -28,8 +28,8 @@ public class SubscriberClient extends Client { public interface PollSubmissions { - List<Submission> getAvailableSubmissions(String destinationId); - RequestMetadata requestSubmission(Submission submission); + List<SubmissionRequest> getAvailableSubmissions(String destinationId); + RequestMetadata requestSubmission(SubmissionRequest submission); } public interface RequestMetadata { @@ -45,7 +45,7 @@ public class SubscriberClient extends Client { } public interface AccessData { - Submission getSubmission(); + SubmissionRequest getSubmission(); List<Attachment> getAttachments(); Metadata getMetadata(); } @@ -55,7 +55,7 @@ public class SubscriberClient extends Client { private final Subscriber subscriber; - private Optional<Submission> submission = Optional.empty(); + private Optional<SubmissionRequest> submission = Optional.empty(); private Optional<Metadata> metadata = Optional.empty(); private List<Attachment> attachments = Collections.emptyList(); private Optional<OAuthToken> token; @@ -71,12 +71,12 @@ public class SubscriberClient extends Client { } @Override - public List<Submission> getAvailableSubmissions(String destinationId) { + public List<SubmissionRequest> getAvailableSubmissions(String destinationId) { throw new UnsupportedOperationException("not yet implemented"); } @Override - public RequestMetadata requestSubmission(Submission submission) { + public RequestMetadata requestSubmission(SubmissionRequest submission) { throw new UnsupportedOperationException("not yet implemented"); } @@ -97,7 +97,7 @@ public class SubscriberClient extends Client { } @Override - public Submission getSubmission() { + public SubmissionRequest getSubmission() { return this.submission.orElseThrow(); } diff --git a/client/src/main/java/de/fitko/fitconnect/cmd/CommandLineRunner.java b/client/src/main/java/de/fitko/fitconnect/cmd/CommandLineRunner.java index fae9f0351ea151ab3edd74dbfa3c2cbefe3e1d9d..b95103262b9428d273362e11826d23b86e8cdecb 100644 --- a/client/src/main/java/de/fitko/fitconnect/cmd/CommandLineRunner.java +++ b/client/src/main/java/de/fitko/fitconnect/cmd/CommandLineRunner.java @@ -1,9 +1,6 @@ package de.fitko.fitconnect.cmd; import com.beust.jcommander.JCommander; -import de.fitko.fitconnect.client.SenderClient; - -import java.util.Optional; public class CommandLineRunner { @@ -20,8 +17,7 @@ public class CommandLineRunner { var secret = cmdArgs.secret; // sample high -level- api calls to send a submission - SenderClient.builder() - .authenticate(clientId, secret); + //SenderClient.builder().authenticate(clientId, secret); } } diff --git a/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSender.java b/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSender.java index ed9e91748a69de8353db33ee764382b345557cf8..13ca5ab2493852ca9606c72aa5dcf8a1a685ad9a 100644 --- a/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSender.java +++ b/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSender.java @@ -5,8 +5,11 @@ import com.nimbusds.jose.jwk.RSAKey; import de.fitko.fitconnect.api.domain.auth.OAuthToken; import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; +import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Hash__1; +import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Type; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionResponse; import de.fitko.fitconnect.api.domain.validation.ValidationResult; import de.fitko.fitconnect.api.exceptions.internal.AuthenticationException; import de.fitko.fitconnect.api.services.Sender; @@ -14,11 +17,15 @@ import de.fitko.fitconnect.api.services.auth.OAuthService; import de.fitko.fitconnect.api.services.crypto.CryptoService; import de.fitko.fitconnect.api.services.metadata.MetadataService; import de.fitko.fitconnect.api.services.validation.CertificateValidator; +import de.fitko.fitconnect.impl.crypto.HashUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; import java.util.List; import java.util.Optional; +import java.util.UUID; public class SubmissionSender implements Sender { @@ -29,6 +36,8 @@ public class SubmissionSender implements Sender { private final CryptoService cryptoService; private final MetadataService metadataService; + private HashUtil hashUtil = new HashUtil(); + @Inject public SubmissionSender(final OAuthService authService, final CryptoService encryptionService, @@ -74,12 +83,46 @@ public class SubmissionSender implements Sender { } @Override - public Optional<Submission> createSubmission(Submission submission) { + public Optional<Metadata> createMetadata(List<Attachment> attachments) { + throw new UnsupportedOperationException("not yet implemented"); + } + + @Override + public Optional<SubmissionResponse> createSubmission(SubmissionRequest submission) { + throw new UnsupportedOperationException("not yet implemented"); + } + + @Override + public Optional<SubmissionResponse> sendSubmission(UUID submissionId, Metadata encryptedMetadata) { + throw new UnsupportedOperationException("not yet implemented"); + } + + @Override + public void uploadAttachments(UUID submissionId, List<Attachment> encryptedAttachments) { throw new UnsupportedOperationException("not yet implemented"); } @Override - public Optional<Submission> sendSubmission(String submissionId, Metadata metadata) { + public Optional<Attachment> createAttachment(File file) { + try { + String hashedFileContent = hashUtil.createHashFromFile(file); + Hash__1 hash = new Hash__1(); + hash.setType(Type.SHA_512); + hash.setContent(hashedFileContent); + return Optional.ofNullable(Attachment.builder() + .attachmentId(UUID.randomUUID()) + .filename(file.getName()) + .hash(hash) + .build()); + } catch (IOException e) { + logger.error("Attachment could not be read", e); + return Optional.empty(); + } + } + + @Override + public RSAKey getEncryptionKeyForDestination(UUID destinationId) { throw new UnsupportedOperationException("not yet implemented"); } + } diff --git a/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSubscriber.java b/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSubscriber.java index 2faf8217edef9151072004dc7e32ac0f744d5733..0a672df5a04b4f7123312aae6aea005bc0fadcad 100644 --- a/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSubscriber.java +++ b/impl/src/main/java/de/fitko/fitconnect/impl/SubmissionSubscriber.java @@ -7,7 +7,7 @@ import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment; import de.fitko.fitconnect.api.domain.model.metadata.data.Data; import de.fitko.fitconnect.api.domain.model.event.SecurityEventToken; -import de.fitko.fitconnect.api.domain.model.submission.Submission; +import de.fitko.fitconnect.api.domain.model.submission.SubmissionRequest; import de.fitko.fitconnect.api.domain.validation.ValidationResult; import de.fitko.fitconnect.api.exceptions.internal.AuthenticationException; import de.fitko.fitconnect.api.services.Subscriber; @@ -69,12 +69,12 @@ public class SubmissionSubscriber implements Subscriber { } @Override - public List<Submission> pollAvailableSubmissions(String destinationId, int limit, int offset) { + public List<SubmissionRequest> pollAvailableSubmissions(String destinationId, int limit, int offset) { throw new UnsupportedOperationException("not yet implemented"); } @Override - public Optional<Submission> getSubmission(String submissionId) { + public Optional<SubmissionRequest> getSubmission(String submissionId) { throw new UnsupportedOperationException("not yet implemented"); } diff --git a/impl/src/main/java/de/fitko/fitconnect/impl/auth/DefaultOAuthService.java b/impl/src/main/java/de/fitko/fitconnect/impl/auth/DefaultOAuthService.java index 754a71de438f944d4b29ef7d739e044c26ee8b12..b73ac916d8d03779410e728407ebc5550eda78c5 100644 --- a/impl/src/main/java/de/fitko/fitconnect/impl/auth/DefaultOAuthService.java +++ b/impl/src/main/java/de/fitko/fitconnect/impl/auth/DefaultOAuthService.java @@ -1,5 +1,7 @@ package de.fitko.fitconnect.impl.auth; +import com.google.inject.Inject; +import com.google.inject.name.Named; import de.fitko.fitconnect.api.domain.auth.OAuthToken; import de.fitko.fitconnect.api.exceptions.internal.AuthenticationException; import de.fitko.fitconnect.api.services.auth.OAuthService; @@ -11,7 +13,6 @@ import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -23,7 +24,8 @@ public class DefaultOAuthService implements OAuthService { private final RestTemplate restTemplate; private final String tokenUrl; - public DefaultOAuthService(final RestTemplate restTemplate, final String tokenUrl) { + @Inject + public DefaultOAuthService(final RestTemplate restTemplate, @Named("authTokenUrl") String tokenUrl) { this.restTemplate = restTemplate; this.tokenUrl = tokenUrl; } diff --git a/impl/src/main/java/de/fitko/fitconnect/impl/validation/MetadataSubmissionValidator.java b/impl/src/main/java/de/fitko/fitconnect/impl/validation/MetadataSubmissionValidator.java index f4bfcaa5b4b481fcaa459acf016da478da0efc70..c07dd4697b3065cfe38ebf0245ab331739273c7f 100644 --- a/impl/src/main/java/de/fitko/fitconnect/impl/validation/MetadataSubmissionValidator.java +++ b/impl/src/main/java/de/fitko/fitconnect/impl/validation/MetadataSubmissionValidator.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import de.fitko.fitconnect.api.domain.model.metadata.Metadata; import de.fitko.fitconnect.api.services.validation.MetadataValidator; import de.fitko.fitconnect.api.domain.validation.ValidationResult; -import de.fitko.fitconnect.impl.crypto.MetadataVerifier; +import de.fitko.fitconnect.impl.crypto.HashUtil; import org.everit.json.schema.Schema; import org.everit.json.schema.ValidationException; import org.everit.json.schema.loader.SchemaLoader; @@ -15,7 +15,7 @@ import org.json.JSONTokener; public class MetadataSubmissionValidator implements MetadataValidator { public static final String DEFAULT_SCHEMA_PATH = "schemas/metadata.schema.json"; - private static final MetadataVerifier verifier = new MetadataVerifier(); + private static final HashUtil verifier = new HashUtil(); private final ObjectMapper mapper = new ObjectMapper(); @Override