diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/diagnosiskeys/ProdDiagnosisKeyBundler.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/diagnosiskeys/ProdDiagnosisKeyBundler.java index ddc0960861..987fa5ce46 100644 --- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/diagnosiskeys/ProdDiagnosisKeyBundler.java +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/diagnosiskeys/ProdDiagnosisKeyBundler.java @@ -133,6 +133,13 @@ private static Optional getEarliestDistributableTimestamp( return distributableDiagnosisKeys.keySet().stream().min(LocalDateTime::compareTo); } + /** + * Calculates the earliest point in time at which the specified {@link DiagnosisKey} can be distributed, while + * respecting the expiry policy and the submission timestamp. Before keys are allowed to be distributed, they must be + * expired for a configured amount of time. + * + * @return {@link LocalDateTime} at which the specified {@link DiagnosisKey} can be distributed. + */ private LocalDateTime getDistributionDateTimeByExpiryPolicy(DiagnosisKey diagnosisKey) { return sharingPoliciesChecker.getEarliestTimeForSharingKey(diagnosisKey, ExpirationPolicy.of(expiryPolicyMinutes, ChronoUnit.MINUTES)); diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/validation/ValidSubmissionPayload.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/validation/ValidSubmissionPayload.java index 56f7bc4e7a..34b44f5af0 100644 --- a/services/submission/src/main/java/app/coronawarn/server/services/submission/validation/ValidSubmissionPayload.java +++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/validation/ValidSubmissionPayload.java @@ -3,8 +3,6 @@ package app.coronawarn.server.services.submission.validation; import static java.util.function.Predicate.not; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.summingInt; import static java.util.stream.Collectors.toList; import app.coronawarn.server.common.protocols.external.exposurenotification.TemporaryExposureKey; @@ -15,7 +13,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -80,7 +77,6 @@ public boolean isValid(SubmissionPayload submissionPayload, ConstraintValidatorC if (keysHaveFlexibleRollingPeriod(exposureKeys)) { return checkStartIntervalNumberIsAtMidNight(exposureKeys, validatorContext) - && checkKeysCumulateEqualOrLessThanMaxRollingPeriodPerDay(exposureKeys, validatorContext) && checkOriginCountryIsValid(submissionPayload, validatorContext) && checkVisitedCountriesAreValid(submissionPayload, validatorContext) && checkRequiredFieldsNotMissing(exposureKeys, validatorContext) @@ -89,7 +85,6 @@ && checkTransmissionRiskLevelIsAcceptable(exposureKeys, validatorContext) } else { return checkStartIntervalNumberIsAtMidNight(exposureKeys, validatorContext) && checkKeyCollectionSize(exposureKeys, validatorContext) - && checkUniqueStartIntervalNumbers(exposureKeys, validatorContext) && checkOriginCountryIsValid(submissionPayload, validatorContext) && checkVisitedCountriesAreValid(submissionPayload, validatorContext) && checkRequiredFieldsNotMissing(exposureKeys, validatorContext) @@ -112,38 +107,6 @@ private boolean checkKeyCollectionSize(List exposureKeys, return true; } - private boolean checkUniqueStartIntervalNumbers(List exposureKeys, - ConstraintValidatorContext validatorContext) { - Integer[] startIntervalNumbers = exposureKeys.stream() - .mapToInt(TemporaryExposureKey::getRollingStartIntervalNumber).boxed().toArray(Integer[]::new); - long distinctSize = Arrays.stream(startIntervalNumbers) - .distinct() - .count(); - - if (distinctSize < exposureKeys.size()) { - addViolation(validatorContext, String.format( - "Duplicate StartIntervalNumber found. StartIntervalNumbers: %s", startIntervalNumbers)); - return false; - } - return true; - } - - private boolean checkKeysCumulateEqualOrLessThanMaxRollingPeriodPerDay(List exposureKeys, - ConstraintValidatorContext validatorContext) { - - boolean isValidRollingPeriod = exposureKeys.stream() - .collect(groupingBy(TemporaryExposureKey::getRollingStartIntervalNumber, - summingInt(TemporaryExposureKey::getRollingPeriod))) - .values().stream() - .anyMatch(sum -> sum <= maxRollingPeriod); - - if (!isValidRollingPeriod) { - addViolation(validatorContext, "The sum of the rolling periods exceeds 144 per day"); - return false; - } - return true; - } - private boolean keysHaveFlexibleRollingPeriod(List exposureKeys) { return exposureKeys.stream() .anyMatch(temporaryExposureKey -> temporaryExposureKey.getRollingPeriod() < maxRollingPeriod); diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/PayloadValidationTest.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/PayloadValidationTest.java index d2b24e5596..3201fab14b 100644 --- a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/PayloadValidationTest.java +++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/PayloadValidationTest.java @@ -37,7 +37,7 @@ class PayloadValidationTest { @BeforeEach public void setUpMocks() { - when(this.tanVerifier.verifyTan(anyString())).thenReturn(true); + when(tanVerifier.verifyTan(anyString())).thenReturn(true); } @Autowired @@ -127,7 +127,7 @@ void check400ResponseStatusForMissingTrlAndDsos() { } @Test - void check400ResponseStatusForKeysWithFixedRollingPeriodAndDuplicateStartIntervals() { + void check200ResponseStatusForKeysWithFixedRollingPeriodAndDuplicateStartIntervals() { int rollingStartIntervalNumber = createRollingStartIntervalNumber(2); var keysWithDuplicateStartIntervalNumber = Lists.list( buildTemporaryExposureKey(VALID_KEY_DATA_1, rollingStartIntervalNumber, 1, @@ -136,7 +136,7 @@ void check400ResponseStatusForKeysWithFixedRollingPeriodAndDuplicateStartInterva ResponseEntity actResponse = executor.executePost(keysWithDuplicateStartIntervalNumber); - assertThat(actResponse.getStatusCode()).isEqualTo(BAD_REQUEST); + assertThat(actResponse.getStatusCode()).isEqualTo(OK); } @Test @@ -210,22 +210,6 @@ private Collection buildPayloadWithMoreThan14KeysAndFlexib return flexibleRollingPeriodKeys; } - @Test - void check400ResponseStatusWhenTwoKeysCumulateMoreThanMaxRollingPeriodInSameDay() { - ResponseEntity actResponse = executor.executePost(buildPayloadWithKeysThatCumulateMoreThanMaxRollingPeriodPerDay()); - assertThat(actResponse.getStatusCode()).isEqualTo(BAD_REQUEST); - } - - private Collection buildPayloadWithKeysThatCumulateMoreThanMaxRollingPeriodPerDay() { - ArrayList temporaryExposureKeys = new ArrayList<>(); - temporaryExposureKeys.add(buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, - createRollingStartIntervalNumber(2), 3, 100)); - temporaryExposureKeys.add(buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, - createRollingStartIntervalNumber(2), 3, 144)); - - return temporaryExposureKeys; - } - @Test void check200ResponseStatusWithTwoKeysOneFlexibleAndOneDefaultOnDifferentDays() { ResponseEntity actResponse = executor.executePost(buildPayloadWithTwoKeysOneFlexibleAndOneDefaultOnDifferentDays()); @@ -245,13 +229,6 @@ private Collection buildPayloadWithTwoKeysOneFlexibleAndOn return flexibleRollingPeriodKeys; } - @Test - void check200ResponseStatusWhenKeysCumulateToMaxRollingPeriodInSameDay() { - ResponseEntity actResponse = executor.executePost(buildPayloadWithTwoKeysWithFlexibleRollingPeriod()); - - assertThat(actResponse.getStatusCode()).isEqualTo(OK); - } - private Collection buildPayloadWithTwoKeysWithFlexibleRollingPeriod() { ArrayList flexibleRollingPeriodKeys = new ArrayList<>(); diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java index 6e3517349f..c99c9a76ab 100644 --- a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java +++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java @@ -2,19 +2,20 @@ package app.coronawarn.server.services.submission.controller; +import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.VALID_KEY_DATA_1; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.VALID_KEY_DATA_2; +import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayload; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildMultipleKeys; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildMultipleKeysWithoutDSOS; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildMultipleKeysWithoutDSOSAndTRL; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildMultipleKeysWithoutTRL; -import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayload; -import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithInvalidKey; -import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithInvalidOriginCountry; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithOneKey; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithPadding; -import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithTooLargePadding; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithVisitedCountries; +import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithTooLargePadding; import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.createRollingStartIntervalNumber; +import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildPayloadWithInvalidOriginCountry; +import static app.coronawarn.server.services.submission.controller.SubmissionPayloadMockData.buildTemporaryExposureKey; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -33,6 +34,7 @@ import app.coronawarn.server.common.persistence.domain.DiagnosisKey; import app.coronawarn.server.common.persistence.service.DiagnosisKeyService; +import app.coronawarn.server.common.protocols.external.exposurenotification.ReportType; import app.coronawarn.server.common.protocols.external.exposurenotification.TemporaryExposureKey; import app.coronawarn.server.common.protocols.internal.SubmissionPayload; import app.coronawarn.server.services.submission.config.SubmissionServiceConfig; @@ -88,21 +90,6 @@ class SubmissionControllerTest { @Autowired private SubmissionServiceConfig config; - private static Stream createIncompleteHeaders() { - return Stream.of( - Arguments.of(HttpHeaderBuilder.builder().build()), - Arguments.of(HttpHeaderBuilder.builder().contentTypeProtoBuf().build()), - Arguments.of(HttpHeaderBuilder.builder().contentTypeProtoBuf().withoutCwaFake().build()), - Arguments.of(HttpHeaderBuilder.builder().contentTypeProtoBuf().cwaAuth().build())); - } - - private static Stream createDeniedHttpMethods() { - return Arrays.stream(HttpMethod.values()) - .filter(method -> method != HttpMethod.POST) - .filter(method -> method != HttpMethod.PATCH) /* not supported by Rest Template */ - .map(Arguments::of); - } - @BeforeEach public void setUpMocks() { when(tanVerifier.verifyTan(anyString())).thenReturn(true); @@ -214,8 +201,8 @@ void checkDSOSIsPersistedForKeysWithTRLOnly() { } /** - * The test verifies that even if the payload does not provide keys with TRL, the information - * is still derived from the DSOS field and correctly persisted. + * The test verifies that even if the payload does not provide keys with TRL, the information is still derived from + * the DSOS field and correctly persisted. * *
  • DSOS - days since onset of symptoms *
  • TRL - transmission risk level @@ -441,4 +428,26 @@ private DiagnosisKey findDiagnosisKeyMatch(TemporaryExposureKey temporaryExposur diagnosisKey -> temporaryExposureKey.getKeyData().equals(ByteString.copyFrom(diagnosisKey.getKeyData()))) .findFirst().orElseThrow(); } + + public static SubmissionPayload buildPayloadWithInvalidKey() { + TemporaryExposureKey invalidKey = + buildTemporaryExposureKey(VALID_KEY_DATA_1, createRollingStartIntervalNumber(2), 999, + ReportType.CONFIRMED_CLINICAL_DIAGNOSIS, 1); + return buildPayload(invalidKey); + } + + private static Stream createIncompleteHeaders() { + return Stream.of( + Arguments.of(HttpHeaderBuilder.builder().build()), + Arguments.of(HttpHeaderBuilder.builder().contentTypeProtoBuf().build()), + Arguments.of(HttpHeaderBuilder.builder().contentTypeProtoBuf().withoutCwaFake().build()), + Arguments.of(HttpHeaderBuilder.builder().contentTypeProtoBuf().cwaAuth().build())); + } + + private static Stream createDeniedHttpMethods() { + return Arrays.stream(HttpMethod.values()) + .filter(method -> method != HttpMethod.POST) + .filter(method -> method != HttpMethod.PATCH) /* not supported by Rest Template */ + .map(Arguments::of); + } }