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