Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • fit-connect/sdk-java
1 result
Show changes
Showing
with 1391 additions and 203 deletions
......@@ -47,12 +47,6 @@ public class SubmissionSender implements Sender {
this.keyService = keyService;
}
@Override
public ValidationResult validatePublicKey(final RSAKey publicKey) {
LOGGER.info("Validating public key integrity");
return validationService.validateEncryptionPublicKey(publicKey);
}
@Override
public ValidationResult validateMetadata(final Metadata metadata) {
LOGGER.info("Validating metadata");
......
......@@ -2,12 +2,19 @@ package dev.fitko.fitconnect.core;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.SignedJWT;
import dev.fitko.fitconnect.api.domain.model.destination.Destination;
import dev.fitko.fitconnect.api.domain.model.event.Event;
import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
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.metadata.attachment.AttachmentForValidation;
import dev.fitko.fitconnect.api.domain.model.submission.Submission;
import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
import dev.fitko.fitconnect.api.exceptions.DecryptionException;
import dev.fitko.fitconnect.api.exceptions.EventCreationException;
import dev.fitko.fitconnect.api.exceptions.RestApiException;
import dev.fitko.fitconnect.api.services.Subscriber;
import dev.fitko.fitconnect.api.services.crypto.CryptoService;
import dev.fitko.fitconnect.api.services.events.EventLogService;
......@@ -44,7 +51,7 @@ public class SubmissionSubscriber implements Subscriber {
}
@Override
public byte[] decryptStringContent(final RSAKey privateKey, final String encryptedContent) {
public byte[] decryptStringContent(final RSAKey privateKey, final String encryptedContent) throws DecryptionException {
LOGGER.info("Decrypting string");
return cryptoService.decryptToBytes(privateKey, encryptedContent);
}
......@@ -62,32 +69,50 @@ public class SubmissionSubscriber implements Subscriber {
}
@Override
public Submission getSubmission(final UUID submissionId) {
public Submission getSubmission(final UUID submissionId) throws RestApiException {
LOGGER.info("Loading submission {}", submissionId);
return submissionService.getSubmission(submissionId);
}
@Override
public String fetchAttachment(final UUID submissionId, final UUID attachmentId) {
public String fetchAttachment(final UUID submissionId, final UUID attachmentId) throws RestApiException {
LOGGER.info("Loading attachment {} for submission {}", attachmentId, submissionId);
return submissionService.getAttachment(submissionId, attachmentId);
}
@Override
public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) {
public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws RestApiException {
LOGGER.info("Loading event log for destination {}", destinationId);
return eventLogService.getEventLog(caseId, destinationId);
}
@Override
public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) {
LOGGER.info("Loading authentication tags of {} event for submission {}", event, submission.getSubmissionId());
return eventLogService.getAuthenticationTagsForEvent(event, submission);
}
@Override
public ValidationResult validateMetadata(final Metadata metadata, final Submission submission, final AuthenticationTags authenticationTags) {
LOGGER.info("Validating metadata");
final Destination destination = submissionService.getDestination(submission.getDestinationId());
return validationService.validateMetadata(metadata, submission, destination, authenticationTags);
}
@Override
public ValidationResult validateData(final byte[] data, final Submission submission, final Metadata metadata, final AuthenticationTags authenticationTags) {
LOGGER.info("Validating data");
return validationService.validateData(data, submission, metadata, authenticationTags);
}
@Override
public ValidationResult validateMetadata(final Metadata metadata) {
LOGGER.info("Validating metadata schema");
return validationService.validateMetadataSchema(metadata);
public ValidationResult validateAttachments(final List<AttachmentForValidation> attachmentsForValidation, final AuthenticationTags authenticationTags) {
LOGGER.info("Validating attachments");
return validationService.validateAttachments(attachmentsForValidation, authenticationTags);
}
@Override
public ValidationResult validateHashIntegrity(final String originalHash, final byte[] data) {
LOGGER.info("Validating data hash integrity");
LOGGER.info("Validating hash integrity");
return validationService.validateHashIntegrity(originalHash, data);
}
......@@ -98,7 +123,7 @@ public class SubmissionSubscriber implements Subscriber {
}
@Override
public void acceptSubmission(final EventPayload eventPayload) {
public void acceptSubmission(final EventPayload eventPayload) throws RestApiException, EventCreationException {
LOGGER.info("Accepting submission");
final SignedJWT confirmedSubmissionEvent = securityEventService.createAcceptSubmissionEvent(eventPayload);
eventLogService.sendEvent(eventPayload.getCaseId(), confirmedSubmissionEvent.serialize());
......@@ -106,7 +131,7 @@ public class SubmissionSubscriber implements Subscriber {
}
@Override
public void rejectSubmission(final EventPayload eventPayload) {
public void rejectSubmission(final EventPayload eventPayload) throws RestApiException, EventCreationException {
LOGGER.info("Rejecting submission");
final SignedJWT rejectSubmissionEvent = securityEventService.createRejectSubmissionEvent(eventPayload);
eventLogService.sendEvent(eventPayload.getCaseId(), rejectSubmissionEvent.serialize());
......
......@@ -2,7 +2,13 @@ package dev.fitko.fitconnect.core.crypto;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.*;
import com.nimbusds.jose.CompressionAlgorithm;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSAEncrypter;
import com.nimbusds.jose.jwk.RSAKey;
......@@ -78,7 +84,7 @@ public class JWECryptoService implements CryptoService {
jwe.decrypt(new RSADecrypter(privateKey));
LOGGER.info("Decrypting {} bytes took {} ", encData.getBytes().length, StopWatch.stopWithFormattedTime(start));
return jwe.getPayload();
} catch (final ParseException | JOSEException e) {
} catch (final ParseException | IllegalStateException | JOSEException e) {
throw new DecryptionException(e.getMessage(), e);
}
}
......
......@@ -2,18 +2,20 @@ package dev.fitko.fitconnect.core.events;
import com.nimbusds.jwt.SignedJWT;
import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.domain.model.event.Event;
import dev.fitko.fitconnect.api.domain.model.event.EventLog;
import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
import dev.fitko.fitconnect.api.domain.model.submission.Submission;
import dev.fitko.fitconnect.api.domain.validation.ValidationContext;
import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
import dev.fitko.fitconnect.api.exceptions.AuthenticationTagsEmptyException;
import dev.fitko.fitconnect.api.exceptions.EventLogException;
import dev.fitko.fitconnect.api.exceptions.RestApiException;
import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.api.services.events.EventLogService;
import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
import dev.fitko.fitconnect.core.util.EventLogUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
......@@ -28,6 +30,13 @@ import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static dev.fitko.fitconnect.core.util.EventLogUtil.eventEqualsSubmissionId;
import static dev.fitko.fitconnect.core.util.EventLogUtil.eventToEntry;
import static dev.fitko.fitconnect.core.util.EventLogUtil.getAuthenticationTags;
import static dev.fitko.fitconnect.core.util.EventLogUtil.getJWTSFromEvents;
import static dev.fitko.fitconnect.core.util.EventLogUtil.getJwtsFromEvents;
import static dev.fitko.fitconnect.core.util.EventLogUtil.mapEventLogToEntries;
public class EventLogApiService implements EventLogService {
private static final Logger LOGGER = LoggerFactory.getLogger(EventLogApiService.class);
......@@ -48,37 +57,59 @@ public class EventLogApiService implements EventLogService {
}
@Override
public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) {
public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws EventLogException {
final EventLog eventLog = loadEventLog(caseId);
final List<SignedJWT> events = EventLogUtil.getJWTSFromEvents(eventLog.getEventLogs());
final List<SignedJWT> events = getJWTSFromEvents(eventLog.getEventLogs());
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
validateLog(eventLogVerifier.validateEventLogs(ctx, events));
return EventLogUtil.mapEventLogToEntries(eventLog);
evaluateLogValidation(eventLogVerifier.validateEventLogs(ctx, events));
return mapEventLogToEntries(eventLog);
}
@Override
public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) throws EventLogException {
final EventLog eventLog = loadEventLog(submission.getCaseId());
final List<SignedJWT> submitEvents = getJwtsFromEvents(event, submission, eventLog);
if (submitEvents.size() != 1) {
throw new SubmitEventNotFoundException("Event log does not contain exactly one submit event");
}
final SignedJWT submitEvent = submitEvents.stream().findFirst().get();
final AuthenticationTags authenticationTags = getAuthenticationTags(submitEvent);
if(authenticationTags.getMetadata() == null && authenticationTags.getData() == null){
throw new AuthenticationTagsEmptyException("Authentication tags are empty");
}
verifyEventWithAuthTags(submission, submitEvent, authenticationTags);
return authenticationTags;
}
@Override
public SubmissionStatus getLastedEvent(final UUID destinationId, final UUID caseId, final UUID submissionId, final AuthenticationTags authenticationTags) throws RestApiException, EventLogException {
public SubmissionStatus getLastedEvent(final UUID destinationId, final UUID caseId, final UUID submissionId, final AuthenticationTags authenticationTags) throws EventLogException {
final EventLog eventLog = loadEventLog(caseId);
final SignedJWT latestEvent = getLatestEventForSubmission(submissionId, eventLog);
final SignedJWT latestEvent = getLatestEventForSubmission(submissionId, eventLog.getEventLogs());
if (latestEvent == null) {
LOGGER.info("No events found for submission {}", submissionId);
return new SubmissionStatus();
}
final var ctx = new ValidationContext(destinationId, caseId, authenticationTags);
validateLog(eventLogVerifier.validateEventLogs(ctx, List.of(latestEvent)));
final EventLogEntry eventLogEntry = EventLogUtil.eventToEntry(latestEvent);
evaluateLogValidation(eventLogVerifier.validateEventLogs(ctx, List.of(latestEvent)));
final EventLogEntry eventLogEntry = eventToEntry(latestEvent);
return new SubmissionStatus(eventLogEntry.getEvent().getState(), eventLogEntry.getProblems());
}
@Override
public void sendEvent(final UUID caseId, final String signedAndSerializedSET) {
public void sendEvent(final UUID caseId, final String signedAndSerializedSET) throws EventLogException {
final String url = config.getEventsEndpoint();
final HttpHeaders headers = getHttpHeaders("application/jose");
final HttpEntity<String> entity = new HttpEntity<>(signedAndSerializedSET, headers);
try {
restTemplate.exchange(url, HttpMethod.POST, entity, Void.class, caseId);
} catch (final RestClientException e) {
throw new RestApiException("Sending event failed", e);
throw new EventLogException("Sending event failed", e);
}
}
......@@ -89,7 +120,7 @@ public class EventLogApiService implements EventLogService {
try {
return restTemplate.exchange(url, HttpMethod.GET, entity, EventLog.class, caseId).getBody();
} catch (final RestClientException e) {
throw new RestApiException("EventLog query failed", e);
throw new EventLogException("EventLog query failed", e);
}
}
......@@ -109,21 +140,33 @@ public class EventLogApiService implements EventLogService {
return headers;
}
private SignedJWT getLatestEventForSubmission(final UUID submissionId, final EventLog eventLog) {
return EventLogUtil.getJWTSFromEvents(eventLog.getEventLogs()).stream()
.filter(EventLogUtil.eventEqualsSubmissionId(submissionId))
private SignedJWT getLatestEventForSubmission(final UUID submissionId, final List<String> events) {
return getJWTSFromEvents(events).stream()
.filter(eventEqualsSubmissionId(submissionId))
.reduce((first, second) -> second)
.orElse(null);
}
private void validateLog(final List<ValidationResult> results) {
if (!results.isEmpty()) {
final String errorMessages = results.stream()
private void verifyEventWithAuthTags(final Submission submission, final SignedJWT submitEvent, final AuthenticationTags authenticationTags) {
final var ctx = new ValidationContext(submission.getDestinationId(), submission.getCaseId(), authenticationTags);
final List<ValidationResult> validationResults = eventLogVerifier.validateEventLogs(ctx, List.of(submitEvent));
if(!validationResults.isEmpty()){
final String errorMessages = validationResults.stream()
.map(ValidationResult::getError)
.map(Throwable::getMessage)
.distinct()
.map(Exception::getMessage)
.collect(Collectors.joining("\n"));
throw new EventLogException("Event log contains invalid entries: \n" + errorMessages);
throw new EventLogException(errorMessages);
}
}
private void evaluateLogValidation(final List<ValidationResult> results) {
results.stream()
.filter(ValidationResult::hasError)
.findFirst()
.ifPresent((result) -> {
throw new EventLogException(result.getError().getMessage(), result.getError());
});
}
}
......@@ -5,6 +5,7 @@ import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.KeyOperation;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
......@@ -99,12 +100,12 @@ public class EventLogVerifier implements EventLogVerificationService {
if (header.getAlgorithm() == null) {
ctx.addError("The provided alg in the SET header must not be null.");
} else {
ctx.addResult(header.getAlgorithm() == JWSAlgorithm.PS512, "The provided alg in the SET header is not allowed.");
ctx.addErrorIfTestFailed(header.getAlgorithm() == JWSAlgorithm.PS512, "The provided alg in the SET header is not allowed.");
}
if (header.getType() == null) {
ctx.addError("The provided typ in the SET header must not be null.");
} else {
ctx.addResult(header.getType().toString().equals(HEADER_TYPE), "The provided typ in the SET header is not " + HEADER_TYPE);
ctx.addErrorIfTestFailed(header.getType().toString().equals(HEADER_TYPE), "The provided typ in the SET header is not " + HEADER_TYPE);
}
}
......@@ -122,16 +123,16 @@ public class EventLogVerifier implements EventLogVerificationService {
}
private void validatePayload(final ValidationContext ctx, final JWTClaimsSet claims) throws ParseException {
ctx.addResult(claims.getClaim(ISSUER) != null, "The claim iss is missing in the payload of the SET.");
ctx.addResult(claims.getClaim(ISSUED_AT) != null, "The claim iat is missing in the payload of the SET.");
ctx.addResult(claims.getClaim(JWT_ID) != null, "The claim jti is missing in the payload of the SET.");
ctx.addResult(claims.getClaim(SUBJECT) != null, "The claim sub is missing in the payload of the SET.");
ctx.addResult(claims.getClaim(CLAIM_TXN) != null, "The claim txn is missing in the payload of the SET.");
ctx.addResult(claims.getClaim(CLAIM_EVENTS) != null, "The claim events is missing in the payload of the SET.");
ctx.addResult(claims.getJSONObjectClaim(CLAIM_EVENTS).keySet().size() == 1, "The events claims has must be exactly one event.");
ctx.addResult(claims.getStringClaim("sub").matches("(submission|case|reply):" + UUID_V4_PATTERN), "The provided subject does not match the allowed pattern.");
ctx.addResult(claims.getStringClaim(CLAIM_TXN).matches("case:" + UUID_V4_PATTERN), "The provided txn does not match the allowed pattern.");
getEventClaim(claims).ifPresentOrElse(event -> ctx.addResult(Event.fromSchemaUri(event) != null, "The provided event is not a valid event supported by this instance."),
ctx.addErrorIfTestFailed(claims.getClaim(ISSUER) != null, "The claim iss is missing in the payload of the SET.");
ctx.addErrorIfTestFailed(claims.getClaim(ISSUED_AT) != null, "The claim iat is missing in the payload of the SET.");
ctx.addErrorIfTestFailed(claims.getClaim(JWT_ID) != null, "The claim jti is missing in the payload of the SET.");
ctx.addErrorIfTestFailed(claims.getClaim(SUBJECT) != null, "The claim sub is missing in the payload of the SET.");
ctx.addErrorIfTestFailed(claims.getClaim(CLAIM_TXN) != null, "The claim txn is missing in the payload of the SET.");
ctx.addErrorIfTestFailed(claims.getClaim(CLAIM_EVENTS) != null, "The claim events is missing in the payload of the SET.");
ctx.addErrorIfTestFailed(claims.getJSONObjectClaim(CLAIM_EVENTS).keySet().size() == 1, "The events claims has must be exactly one event.");
ctx.addErrorIfTestFailed(claims.getStringClaim("sub").matches("(submission|case|reply):" + UUID_V4_PATTERN), "The provided subject does not match the allowed pattern.");
ctx.addErrorIfTestFailed(claims.getStringClaim(CLAIM_TXN).matches("case:" + UUID_V4_PATTERN), "The provided txn does not match the allowed pattern.");
getEventClaim(claims).ifPresentOrElse(event -> ctx.addErrorIfTestFailed(Event.fromSchemaUri(event) != null, "The provided event is not a valid event supported by this instance."),
() -> ctx.addError("No events in JWT"));
}
......@@ -139,7 +140,7 @@ public class EventLogVerifier implements EventLogVerificationService {
if (signatureKey == null) {
ctx.addError("The signature key cannot be validated, it must not be null.");
} else {
ctx.addResult(validationService.validateSignaturePublicKey(signatureKey));
ctx.addResult(validationService.validatePublicKey(signatureKey, KeyOperation.VERIFY));
}
}
......@@ -148,7 +149,7 @@ public class EventLogVerifier implements EventLogVerificationService {
ctx.addError("The signature cannot validated, signature key is unavailable.");
} else {
final JWSVerifier jwsVerifier = new RSASSAVerifier(signatureKey);
ctx.addResult(signedJWT.verify(jwsVerifier), "The signature of the token could not be verified with the specified key.");
ctx.addErrorIfTestFailed(signedJWT.verify(jwsVerifier), "The signature of the token could not be verified with the specified key.");
}
}
......@@ -177,15 +178,15 @@ public class EventLogVerifier implements EventLogVerificationService {
ctx.addError("The event '" + event.getSchemaUri() + "' has to be created by the destination ('iss' claim must be an UUID)");
}
if (destinationId != null) {
ctx.addResult(destinationId.equals(ctx.getDestinationId()), "The destination of the submission is not the issuer ('iss' claim must match submission.destinationId)");
ctx.addErrorIfTestFailed(destinationId.equals(ctx.getDestinationId()), "The destination of the submission is not the issuer ('iss' claim must match submission.destinationId)");
}
}
private void validateAuthenticationTags(final ValidationContext ctx, final JWTClaimsSet payload) throws ParseException {
final AuthenticationTags eventAuthTags = getAuthenticationTags(payload);
final AuthenticationTags submissionAuthTags = ctx.getAuthenticationTags();
ctx.addResult(eventAuthTags != null, "AuthenticationTags of event must not be null.");
ctx.addResult(submissionAuthTags != null, "AuthenticationTags of submission must not be null.");
ctx.addErrorIfTestFailed(eventAuthTags != null, "AuthenticationTags of event must not be null.");
ctx.addErrorIfTestFailed(submissionAuthTags != null, "AuthenticationTags of submission must not be null.");
if (eventAuthTags != null && submissionAuthTags != null) {
validateDataAuthTags(ctx, eventAuthTags.getData(), submissionAuthTags.getData());
validateMetadataAuthTags(ctx, eventAuthTags.getMetadata(), submissionAuthTags.getMetadata());
......@@ -195,20 +196,20 @@ public class EventLogVerifier implements EventLogVerificationService {
private void validateAttachmentsAuthTags(final ValidationContext ctx, final Map<UUID, String> eventAttachmentTags, final Map<UUID, String> submissionAttachmentTags) {
if (eventAttachmentTags != null && submissionAttachmentTags != null) {
ctx.addResult(eventAttachmentTags.size() == submissionAttachmentTags.size(), "The events quantity of attachments does not match the submission.");
ctx.addErrorIfTestFailed(eventAttachmentTags.size() == submissionAttachmentTags.size(), "The events quantity of attachments does not match the submission.");
eventAttachmentTags.forEach((key, eventTag) -> {
final String submissionTag = submissionAttachmentTags.get(key);
ctx.addResult(eventTag.equals(submissionTag), "The authentication-tag for the attachment " + key + " does not match the submission.");
ctx.addErrorIfTestFailed(eventTag.equals(submissionTag), "The authentication-tag for the attachment " + key + " does not match the submission.");
});
}
}
private void validateDataAuthTags(final ValidationContext ctx, final String eventDataAuthTag, final String submissionDataAuthTag) {
ctx.addResult(eventDataAuthTag.equals(submissionDataAuthTag), "The events data authentication-tag does not match the submission.");
ctx.addErrorIfTestFailed(eventDataAuthTag.equals(submissionDataAuthTag), "The events data authentication-tag does not match the submission.");
}
private void validateMetadataAuthTags(final ValidationContext ctx, final String eventMetadataAuthTag, final String submissionMetadataAuthTag) {
ctx.addResult(eventMetadataAuthTag.equals(submissionMetadataAuthTag), "The events metadata authentication-tag does not match the submission.");
ctx.addErrorIfTestFailed(eventMetadataAuthTag.equals(submissionMetadataAuthTag), "The events metadata authentication-tag does not match the submission.");
}
private static boolean eventContainsAuthTags(final JWTClaimsSet payload) throws ParseException {
......
......@@ -21,6 +21,7 @@ import dev.fitko.fitconnect.api.services.events.SecurityEventService;
import dev.fitko.fitconnect.api.services.validation.ValidationService;
import java.text.ParseException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
......@@ -123,6 +124,9 @@ public class SecurityEventTokenService implements SecurityEventService {
}
private Map<UUID, String> buildAttachmentAuthenticationTags(final Map<UUID, String> encryptedAttachments) {
if(encryptedAttachments == null || encryptedAttachments.isEmpty()){
return Collections.emptyMap();
}
return encryptedAttachments
.entrySet()
.stream()
......
......@@ -2,6 +2,7 @@ package dev.fitko.fitconnect.core.keys;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.jwk.KeyOperation;
import com.nimbusds.jose.jwk.RSAKey;
import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.domain.model.destination.Destination;
......@@ -107,12 +108,12 @@ public class PublicKeyService implements KeyService {
}
private void validateEncryptionKey(final RSAKey rsaKey) {
final ValidationResult result = validationService.validateEncryptionPublicKey(rsaKey);
final ValidationResult result = validationService.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
validateResult(result, "Invalid public encryption key");
}
private void validateSignatureKey(final RSAKey rsaKey) {
final ValidationResult result = validationService.validateSignaturePublicKey(rsaKey);
final ValidationResult result = validationService.validatePublicKey(rsaKey, KeyOperation.VERIFY);
validateResult(result, "Public signature key is not valid");
}
......
......@@ -19,7 +19,9 @@ import lombok.Setter;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
......@@ -42,7 +44,7 @@ public class SubmissionApiService implements SubmissionService {
}
@Override
public Destination getDestination(final UUID destinationID) {
public Destination getDestination(final UUID destinationID) throws RestApiException {
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getDestinationsEndpoint())
.method(HttpMethod.GET)
......@@ -55,7 +57,7 @@ public class SubmissionApiService implements SubmissionService {
}
@Override
public SubmissionForPickup announceSubmission(final CreateSubmission submission) {
public SubmissionForPickup announceSubmission(final CreateSubmission submission) throws RestApiException {
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getSubmissionsEndpoint())
.method(HttpMethod.POST)
......@@ -68,7 +70,7 @@ public class SubmissionApiService implements SubmissionService {
}
@Override
public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) {
public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) throws RestApiException {
final Map<String, Object> params = new HashMap<>();
params.put("submissionId", submissionId);
params.put("attachmentId", attachmentId);
......@@ -84,7 +86,7 @@ public class SubmissionApiService implements SubmissionService {
}
@Override
public String getAttachment(final UUID submissionId, final UUID attachmentId) {
public String getAttachment(final UUID submissionId, final UUID attachmentId) throws RestApiException {
final Map<String, Object> params = new HashMap<>();
params.put("submissionId", submissionId);
params.put("attachmentId", attachmentId);
......@@ -100,7 +102,7 @@ public class SubmissionApiService implements SubmissionService {
}
@Override
public Submission sendSubmission(final SubmitSubmission submission) {
public Submission sendSubmission(final SubmitSubmission submission) throws RestApiException {
final Map<String, Object> params = Map.of("submissionId", submission.getSubmissionId());
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getSubmissionEndpoint())
......@@ -114,7 +116,7 @@ public class SubmissionApiService implements SubmissionService {
}
@Override
public SubmissionsForPickup pollAvailableSubmissions(final int offset, final int limit) {
public SubmissionsForPickup pollAvailableSubmissions(final int offset, final int limit) throws RestApiException {
final String urlWithQueryParams = UriComponentsBuilder.fromHttpUrl(config.getSubmissionsEndpoint())
.queryParam("limit", limit)
.queryParam("offset", offset)
......@@ -149,7 +151,7 @@ public class SubmissionApiService implements SubmissionService {
}
@Override
public Submission getSubmission(final UUID submissionId) {
public Submission getSubmission(final UUID submissionId) throws RestApiException {
final Map<String, Object> params = Map.of("submissionId", submissionId);
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getSubmissionEndpoint())
......@@ -169,12 +171,19 @@ public class SubmissionApiService implements SubmissionService {
final Class<T> responseType = requestSettings.responseType;
final Map<String, ?> params = requestSettings.params;
try {
return restTemplate.exchange(url, method, entity, responseType, params).getBody();
return evaluateStatusCode(restTemplate.exchange(url, method, entity, responseType, params));
} catch (final RestClientException e) {
throw new RestApiException(e.getMessage(), e);
}
}
private <T> T evaluateStatusCode(final ResponseEntity<T> response) {
if(response.getStatusCode().equals(HttpStatus.NOT_FOUND)){
throw new RestApiException(response.getStatusCode(), "requested entity could not be found");
}
return response.getBody();
}
private HttpEntity<String> getHttpEntity(final HttpHeaders headers) {
return getHttpEntity(null, headers);
}
......
......@@ -4,19 +4,31 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import dev.fitko.fitconnect.api.domain.model.event.*;
import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.domain.model.event.Event;
import dev.fitko.fitconnect.api.domain.model.event.EventClaimFields;
import dev.fitko.fitconnect.api.domain.model.event.EventIssuer;
import dev.fitko.fitconnect.api.domain.model.event.EventLog;
import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
import dev.fitko.fitconnect.api.domain.model.event.TypeAndUUID;
import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
import dev.fitko.fitconnect.api.domain.model.submission.Submission;
import dev.fitko.fitconnect.api.exceptions.EventCreationException;
import dev.fitko.fitconnect.api.exceptions.EventLogException;
import java.text.ParseException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.*;
import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.DESTINATION;
import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.SUBMISSION_SERVICE;
import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.UNDEFINED;
public final class EventLogUtil {
......@@ -79,8 +91,8 @@ public final class EventLogUtil {
final String eventName = eventEntry.getKey();
final Map<String, Object> eventPayload = eventEntry.getValue();
final List<Problem> problems = new ArrayList<>();
if (eventPayload.containsKey("problems")) {
problems.addAll((List<Problem>) eventPayload.get("problems"));
if (eventPayload.containsKey(EventClaimFields.PROBLEMS)) {
problems.addAll((List<Problem>) eventPayload.get(EventClaimFields.PROBLEMS));
}
return EventLogEntry.builder()
.event(Event.fromSchemaUri(eventName))
......@@ -88,7 +100,7 @@ public final class EventLogUtil {
.issueTime(claimsSet.getIssueTime())
.eventId(getEventId(claimsSet))
.caseId(getTransactionClaim(claimsSet))
.submissionId(getSubmissionId(claimsSet))
.submissionId(getSubmissionIdFromJWT(claimsSet))
.problems(problems)
.build();
}
......@@ -107,10 +119,14 @@ public final class EventLogUtil {
}
}
private static UUID getSubmissionId(final JWTClaimsSet claimsSet) {
public static UUID getSubmissionIdFromJWT(final JWTClaimsSet claimsSet) {
return TypeAndUUID.fromString(claimsSet.getSubject()).getUuid();
}
public static UUID getSubmissionIdFromJWT(final SignedJWT signedJWT) {
return TypeAndUUID.fromString(getClaimsSet(signedJWT).getSubject()).getUuid();
}
private static UUID getEventId(final JWTClaimsSet claimsSet) {
return UUID.fromString(claimsSet.getJWTID());
}
......@@ -139,18 +155,38 @@ public final class EventLogUtil {
}
}
public static AuthenticationTags getAuthenticationTags(final SignedJWT jwt) {
return getAuthenticationTags(getClaimsSet(jwt));
}
public static AuthenticationTags getAuthenticationTags(final JWTClaimsSet claims) {
try {
final Map<String, Object> eventsClaim = (Map) claims.getClaim(EventClaimFields.CLAIM_EVENTS);
final Map.Entry<String, Map> eventEntry = (Map.Entry) eventsClaim.entrySet().iterator().next();
final Map<String, Object> events = eventEntry.getValue();
final String authTags = MAPPER.writeValueAsString(events.get("authenticationTags"));
final String authTags = MAPPER.writeValueAsString(events.get(EventClaimFields.AUTHENTICATION_TAGS));
return MAPPER.readValue(authTags, AuthenticationTags.class);
} catch (final JsonProcessingException e) {
throw new EventCreationException(e.getMessage(), e);
}
}
public static String getAuthenticationTagFromEncryptedData(final String encryptedData) {
try {
final String[] parts = encryptedData.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN);
return parts[parts.length - 1];
} catch (final Exception e) {
return null;
}
}
public static List<SignedJWT> getJwtsFromEvents(final Event event, final Submission submission, final EventLog eventLog) {
return getJWTSFromEvents(eventLog.getEventLogs()).stream()
.filter(jwt -> getSubmissionIdFromJWT(jwt).equals(submission.getSubmissionId()))
.filter(jwt -> getEventFromJWT(jwt).equals(event))
.collect(Collectors.toList());
}
public static Predicate<SignedJWT> eventEqualsSubmissionId(final UUID submissionId) {
return jwt -> {
try {
......
......@@ -83,25 +83,6 @@ public class SubmissionSenderTest {
);
}
@Test
void validatePublicKey() throws JOSEException {
//Given
final RSAKey publicKey = new RSAKeyGenerator(2048)
.keyUse(KeyUse.ENCRYPTION)
.keyID(UUID.randomUUID().toString())
.generate()
.toPublicJWK();
when(validationServiceMock.validateEncryptionPublicKey(any())).thenReturn(ValidationResult.ok());
// When
final ValidationResult validationResult = underTest.validatePublicKey(publicKey);
// Then
assertTrue(validationResult.isValid());
}
@Test
void encryptBytes() throws JOSEException {
......
......@@ -7,8 +7,14 @@ import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import com.nimbusds.jwt.SignedJWT;
import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataHashMismatch;
import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.MetadataSchemaViolation;
import dev.fitko.fitconnect.api.domain.model.event.problems.submission.AttachmentsMismatch;
import dev.fitko.fitconnect.api.domain.model.metadata.Hash;
import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
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.SubmissionsForPickup;
......@@ -33,6 +39,7 @@ import java.util.UUID;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
......@@ -40,6 +47,7 @@ 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.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
......@@ -167,10 +175,10 @@ class SubmissionSubscriberTest {
void validateMetadata() {
// Given
when(validationServiceMock.validateMetadataSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateMetadata(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
// When
final ValidationResult validationResult = underTest.validateMetadata(new Metadata());
final ValidationResult validationResult = underTest.validateMetadata(new Metadata(), new Submission(), new AuthenticationTags());
// Then
assertTrue(validationResult.isValid());
......@@ -180,14 +188,28 @@ class SubmissionSubscriberTest {
void invalidMetadata() {
// Given
when(validationServiceMock.validateMetadataSchema(any())).thenReturn(ValidationResult.error(new Exception("Failed parsing metadata")));
when(validationServiceMock.validateMetadata(any(), any(), any(), any())).thenReturn(ValidationResult.problem(new MetadataSchemaViolation()));
// When
final ValidationResult validationResult = underTest.validateMetadata(new Metadata());
final ValidationResult validationResult = underTest.validateMetadata(new Metadata(), new Submission(), new AuthenticationTags());
// Then
assertTrue(validationResult.hasError());
assertThat(validationResult.getError().getMessage(), containsString("Failed parsing metadata"));
assertTrue(validationResult.hasProblems());
assertThat(validationResult.getProblems().get(0), instanceOf(MetadataSchemaViolation.class));
}
@Test
void invalidData() {
// Given
when(validationServiceMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.problem(new DataHashMismatch()));
// When
final ValidationResult validationResult = underTest.validateData("foo".getBytes(), new Submission(), new Metadata(), new AuthenticationTags());
// Then
assertTrue(validationResult.hasProblems());
assertThat(validationResult.getProblems().get(0), instanceOf(DataHashMismatch.class));
}
@Test
......@@ -300,6 +322,29 @@ class SubmissionSubscriberTest {
assertThrows(EventLogException.class, () -> underTest.getEventLog(destinationId, caseId));
}
@Test
void testAttachmentValidationFailed() {
// Given
final Hash hash = new Hash();
hash.setContent("hashValue");
final ApiAttachment attachmentMetadata = new ApiAttachment();
attachmentMetadata.setAttachmentId(UUID.randomUUID());
attachmentMetadata.setHash(hash);
final AttachmentForValidation attachmentForValidation = new AttachmentForValidation(attachmentMetadata, null, null);
when(validationServiceMock.validateAttachments(anyList(), any())).thenReturn(ValidationResult.problem(new AttachmentsMismatch()));
// When
final ValidationResult validationResult = underTest.validateAttachments(List.of(attachmentForValidation), new AuthenticationTags());
// Then
assertTrue(validationResult.hasProblems());
assertThat(validationResult.getProblems().get(0), instanceOf(AttachmentsMismatch.class));
}
private String getResourceAsString(final String filename) throws IOException {
return new String(SubmissionSenderTest.class.getResourceAsStream(filename).readAllBytes());
......
......@@ -75,7 +75,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -113,7 +113,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId, authenticationTags);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -142,7 +142,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId, null);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -178,7 +178,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId, authTags);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -216,7 +216,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -254,7 +254,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.error(new SchemaNotFoundException("Schema not found")));
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -289,7 +289,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -330,7 +330,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -371,7 +371,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -410,7 +410,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -450,7 +450,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getSubmissionServicePublicKey(any())).thenReturn(rsaKey);
// When
......@@ -488,7 +488,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -526,7 +526,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -563,7 +563,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -602,7 +602,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
// When
......@@ -639,7 +639,7 @@ class EventLogVerifierTest {
final ValidationContext ctx = new ValidationContext(destinationId, caseId);
when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(null);
// When
......
......@@ -23,7 +23,7 @@ class ValidationContextTest {
final ValidationContext underTest = new ValidationContext(UUID.randomUUID(), UUID.randomUUID());
// When
underTest.addResult(1+2 == 3, "1+2 does not equal 3");
underTest.addErrorIfTestFailed(1+2 == 3, "1+2 does not equal 3");
final List<ValidationResult> results = underTest.getValidationResults();
// Then
......@@ -37,7 +37,7 @@ class ValidationContextTest {
final ValidationContext underTest = new ValidationContext(UUID.randomUUID(), UUID.randomUUID());
// When
underTest.addResult(1+2 == 2, "1+2 does not equal 3");
underTest.addErrorIfTestFailed(1+2 == 2, "1+2 does not equal 3");
final List<ValidationResult> results = underTest.getValidationResults();
// Then
......@@ -71,7 +71,7 @@ class ValidationContextTest {
final List<ValidationResult> results = underTest.getValidationResults();
// Then
assertTrue(results.size() == 1);
assertEquals(1, results.size());
assertTrue(results.get(0).hasError());
assertThat(results.get(0).getError().getMessage(), is("Test failed"));
}
......@@ -87,7 +87,7 @@ class ValidationContextTest {
final List<ValidationResult> results = underTest.getValidationResults();
// Then
assertTrue(results.size() == 1);
assertEquals(1, results.size());
assertTrue(results.get(0).hasError());
assertThat(results.get(0).getError().getMessage(), is("Test failed"));
}
......
......@@ -91,7 +91,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
when(authServiceMock.getCurrentToken()).thenReturn(authToken);
when(submissionServiceMock.getDestination(any())).thenReturn(destination);
when(validationServiceMock.validateEncryptionPublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
// When
final RSAKey rsaKey = underTest.getPublicEncryptionKey(destination.getDestinationId());
......@@ -134,7 +134,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
when(authServiceMock.getCurrentToken()).thenReturn(authToken);
when(submissionServiceMock.getDestination(any())).thenReturn(destination);
when(validationServiceMock.validateEncryptionPublicKey(any())).thenReturn(ValidationResult.error(new Exception("Public key is insecure !")));
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.error(new Exception("Public key is insecure !")));
// When
final InvalidKeyException exception = assertThrows(InvalidKeyException.class, () -> keyService.getPublicEncryptionKey(destination.getDestinationId()));
......@@ -165,7 +165,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
when(authServiceMock.getCurrentToken()).thenReturn(authToken);
when(submissionServiceMock.getDestination(any())).thenReturn(destination);
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
// When
final RSAKey retrievedSignatureKey = underTest.getPublicSignatureKey(destination.getDestinationId(), "123");
......@@ -195,7 +195,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
.withStatus(200)));
when(authServiceMock.getCurrentToken()).thenReturn(authToken);
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
// When
final RSAKey retrievedSignatureKey = underTest.getSubmissionServicePublicKey("123");
......@@ -225,7 +225,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
.withStatus(200)));
when(authServiceMock.getCurrentToken()).thenReturn(authToken);
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
// When
final RSAKey retrievedSignatureKey = underTest.getPortalPublicKey("123");
......@@ -255,7 +255,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
.withStatus(200)));
when(authServiceMock.getCurrentToken()).thenReturn(authToken);
when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
// When
final RSAKey retrievedSignatureKey = underTest.getWellKnownKeysForSubmissionUrl("http://localhost:" + wireMockServer.port() + "/custom/path", "123");
......
......@@ -5,6 +5,7 @@ import dev.fitko.fitconnect.api.domain.model.event.Event;
import dev.fitko.fitconnect.api.domain.model.event.EventIssuer;
import dev.fitko.fitconnect.api.domain.model.event.EventLog;
import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataEncryptionIssue;
import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
import dev.fitko.fitconnect.api.exceptions.EventLogException;
import org.junit.jupiter.api.Test;
......
......@@ -8,7 +8,6 @@ import dev.fitko.fitconnect.api.config.SubscriberConfig;
import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
import dev.fitko.fitconnect.client.SubscriberClient;
import dev.fitko.fitconnect.client.factory.ClientFactory;
import org.junit.jupiter.api.BeforeAll;
import java.util.List;
import java.util.Map;
......@@ -23,8 +22,7 @@ public class IntegrationTestBase {
private static final String selfServicePortalUrl = "https://portal.auth-testing.fit-connect.fitko.dev";
private static final String submissionBaseUrl = "https://submission-api-testing.fit-connect.fitko.dev";
@BeforeAll
static void cleanupSubmissionByRejecting() {
public static void cleanupTestSubmissions() {
final UUID destinationId = UUID.fromString(System.getenv("TEST_DESTINATION_ID"));
final Problem problem = new Problem(SCHEMA_URL + "technical-error", "cleanup", "submission-cleanup", "other");
......@@ -32,7 +30,7 @@ public class IntegrationTestBase {
subscriberClient.getAvailableSubmissionsForDestination(destinationId).forEach((s -> {
try {
subscriberClient.requestSubmission(s.getSubmissionId()).rejectSubmission(List.of(problem));
subscriberClient.rejectSubmission(s, List.of(problem));
} catch (final Exception e) {
//continue
}
......
......@@ -19,7 +19,6 @@ import dev.fitko.fitconnect.api.services.crypto.CryptoService;
import dev.fitko.fitconnect.client.SenderClient;
import dev.fitko.fitconnect.client.factory.ClientFactory;
import dev.fitko.fitconnect.client.sender.model.Attachment;
import dev.fitko.fitconnect.client.sender.model.AttachmentPayload;
import dev.fitko.fitconnect.client.sender.model.EncryptedAttachment;
import dev.fitko.fitconnect.client.sender.model.SendableEncryptedSubmission;
import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
......@@ -31,6 +30,7 @@ import org.apache.tika.mime.MimeTypes;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
......@@ -51,6 +51,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
@EnableIfEnvironmentVariablesAreSet
public class SenderClientIT extends IntegrationTestBase {
@BeforeEach
public void cleanup(){
cleanupTestSubmissions();
}
@Test
void testSendAndConfirmCycle() {
......@@ -64,7 +69,7 @@ public class SenderClientIT extends IntegrationTestBase {
.addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "text/plain"))
.addAttachment(Attachment.fromByteArray("attachment data".getBytes(), "text/plain"))
.addAttachment(Attachment.fromString("attachment data", "text/plain"))
.setReplyChannel(ReplyChannel.fromEmail("test@mail.org"))
.setReplyChannel(ReplyChannel.fromDeMail("test@mail.org"))
.build();
final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
......@@ -82,7 +87,7 @@ public class SenderClientIT extends IntegrationTestBase {
assertThat(receivedSubmission.getDataSchemaUri(), is(URI.create("https://schema.fitko.de/fim/s00000000009_1.0.0.schema.json")));
assertThat(receivedSubmission.getDataMimeType(), is("application/json"));
assertThat(receivedSubmission.getAttachments(), hasSize(3));
assertThat(receivedSubmission.getMetadata().getReplyChannel(), is(ReplyChannel.fromEmail("test@mail.org")));
assertThat(receivedSubmission.getMetadata().getReplyChannel(), is(ReplyChannel.fromDeMail("test@mail.org")));
assertThat(new String(receivedSubmission.getAttachments().get(0).getDataAsBytes()), is("Test attachment"));
}
......@@ -120,25 +125,18 @@ public class SenderClientIT extends IntegrationTestBase {
data.setHash(dataHash);
data.setSubmissionSchema(submissionSchema);
final var attachmentPayload = AttachmentPayload.builder()
.encryptedData(encryptedAttachment)
.mimeType(MimeTypes.PLAIN_TEXT)
.attachmentId(UUID.randomUUID())
.hashedData(cryptoService.hashBytes(attachmentData.getBytes(StandardCharsets.UTF_8)))
.build();
final var publicServiceType = new PublicServiceType();
publicServiceType.setName("Test Service");
publicServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
final var attachment = new ApiAttachment();
attachment.setAttachmentId(attachmentPayload.getAttachmentId());
attachment.setAttachmentId(UUID.randomUUID());
attachment.setPurpose(Purpose.ATTACHMENT);
attachment.setFilename(attachmentPayload.getFileName());
attachment.setMimeType(attachmentPayload.getMimeType());
attachment.setFilename(attachmentFile.getName());
attachment.setMimeType(MimeTypes.PLAIN_TEXT);
final var attachmentHash = new Hash();
attachmentHash.setContent(attachmentPayload.getHashedData());
attachmentHash.setContent(cryptoService.hashBytes(attachmentData.getBytes(StandardCharsets.UTF_8)));
attachmentHash.setSignatureType(SignatureType.SHA_512);
attachment.setHash(attachmentHash);
......@@ -159,7 +157,7 @@ public class SenderClientIT extends IntegrationTestBase {
.setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
.setEncryptedMetadata(encryptedMetadata)
.setEncryptedData(encryptedData)
.addEncryptedAttachment(new EncryptedAttachment(attachmentPayload.getAttachmentId(), attachmentPayload.getEncryptedData()))
.addEncryptedAttachment(new EncryptedAttachment(attachment.getAttachmentId(), encryptedAttachment))
.build();
final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
......