diff --git a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKey.java b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKey.java index b38813076d..be011417ae 100644 --- a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKey.java +++ b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKey.java @@ -47,7 +47,8 @@ public class DiagnosisKey { * reject any diagnosis keys that do not have a rolling period of a certain fixed value. See * https://developer.apple.com/documentation/exposurenotification/setting_up_an_exposure_notification_server */ - public static final int EXPECTED_ROLLING_PERIOD = 144; + public static final int MIN_ROLLING_PERIOD = 0; + public static final int MAX_ROLLING_PERIOD = 144; private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); @@ -58,8 +59,8 @@ public class DiagnosisKey { @ValidRollingStartIntervalNumber private final int rollingStartIntervalNumber; - @Range(min = EXPECTED_ROLLING_PERIOD, max = EXPECTED_ROLLING_PERIOD, - message = "Rolling period must be " + EXPECTED_ROLLING_PERIOD + ".") + @Range(min = MIN_ROLLING_PERIOD, max = MAX_ROLLING_PERIOD, + message = "Rolling period must be between " + MIN_ROLLING_PERIOD + " and " + MAX_ROLLING_PERIOD + ".") private final int rollingPeriod; @Range(min = 0, max = 8, message = "Risk level must be between 0 and 8.") diff --git a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilder.java b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilder.java index f9acc13c88..93e473d9e6 100644 --- a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilder.java +++ b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilder.java @@ -46,7 +46,7 @@ public class DiagnosisKeyBuilder implements private byte[] keyData; private int rollingStartIntervalNumber; - private int rollingPeriod = DiagnosisKey.EXPECTED_ROLLING_PERIOD; + private int rollingPeriod = DiagnosisKey.MAX_ROLLING_PERIOD; private int transmissionRiskLevel; private Long submissionTimestamp = null; diff --git a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilders.java b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilders.java index fd1479d82f..46a5b2ac19 100644 --- a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilders.java +++ b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilders.java @@ -81,7 +81,7 @@ interface FinalBuilder { /** * Adds the specified rolling period to this builder. If not specified, the rolling period defaults to {@link - * DiagnosisKey#EXPECTED_ROLLING_PERIOD} + * DiagnosisKey#MAX_ROLLING_PERIOD} * * @param rollingPeriod Number describing how long a key is valid. It is expressed in increments of 10 minutes (e.g. * 144 for 24 hours). diff --git a/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilderTest.java b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilderTest.java index 22c128137e..d577ba6a69 100644 --- a/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilderTest.java +++ b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyBuilderTest.java @@ -51,7 +51,7 @@ void buildFromProtoBufObjWithSubmissionTimestamp() { .newBuilder() .setKeyData(ByteString.copyFrom(this.expKeyData)) .setRollingStartIntervalNumber(this.expRollingStartIntervalNumber) - .setRollingPeriod(DiagnosisKey.EXPECTED_ROLLING_PERIOD) + .setRollingPeriod(DiagnosisKey.MAX_ROLLING_PERIOD) .setTransmissionRiskLevel(this.expTransmissionRiskLevel) .build(); @@ -69,7 +69,7 @@ void buildFromProtoBufObjWithoutSubmissionTimestamp() { .newBuilder() .setKeyData(ByteString.copyFrom(this.expKeyData)) .setRollingStartIntervalNumber(this.expRollingStartIntervalNumber) - .setRollingPeriod(DiagnosisKey.EXPECTED_ROLLING_PERIOD) + .setRollingPeriod(DiagnosisKey.MAX_ROLLING_PERIOD) .setTransmissionRiskLevel(this.expTransmissionRiskLevel) .build(); @@ -106,7 +106,7 @@ void buildSuccessivelyWithRollingPeriod() { .withRollingStartIntervalNumber(this.expRollingStartIntervalNumber) .withTransmissionRiskLevel(this.expTransmissionRiskLevel) .withSubmissionTimestamp(this.expSubmissionTimestamp) - .withRollingPeriod(DiagnosisKey.EXPECTED_ROLLING_PERIOD).build(); + .withRollingPeriod(DiagnosisKey.MAX_ROLLING_PERIOD).build(); assertDiagnosisKeyEquals(actDiagnosisKey, this.expSubmissionTimestamp); } @@ -165,17 +165,18 @@ void transmissionRiskLevelDoesNotThrowForValid(int validRiskLevel) { } @ParameterizedTest - @ValueSource(ints = {-3, 143, 145}) + @ValueSource(ints = {-3, 145}) void rollingPeriodMustBeEpectedValue(int invalidRollingPeriod) { assertThat(catchThrowable(() -> keyWithRollingPeriod(invalidRollingPeriod))) .isInstanceOf(InvalidDiagnosisKeyException.class) - .hasMessage("[Rolling period must be " + DiagnosisKey.EXPECTED_ROLLING_PERIOD + .hasMessage("[Rolling period must be between "+ DiagnosisKey.MIN_ROLLING_PERIOD + " and " + DiagnosisKey.MAX_ROLLING_PERIOD + ". Invalid Value: " + invalidRollingPeriod + "]"); } - @Test - void rollingPeriodDoesNotThrowForValid() { - assertThatCode(() -> keyWithRollingPeriod(DiagnosisKey.EXPECTED_ROLLING_PERIOD)).doesNotThrowAnyException(); + @ParameterizedTest + @ValueSource(ints = {DiagnosisKey.MIN_ROLLING_PERIOD, 100, DiagnosisKey.MAX_ROLLING_PERIOD}) + void rollingPeriodDoesNotThrowForValid(int validRollingPeriod) { + assertThatCode(() -> keyWithRollingPeriod(validRollingPeriod)).doesNotThrowAnyException(); } @ParameterizedTest @@ -218,7 +219,7 @@ void submissionTimestampDoesNotThrowOnValid() { () -> buildDiagnosisKeyForSubmissionTimestamp(Instant.now().minus(Duration.ofHours(2)).getEpochSecond() / SECONDS_PER_HOUR)) .doesNotThrowAnyException(); } - + private DiagnosisKey keyWithKeyData(byte[] expKeyData) { return DiagnosisKey.builder() .withKeyData(expKeyData) @@ -260,7 +261,7 @@ private void assertDiagnosisKeyEquals(DiagnosisKey actDiagnosisKey, long expSubm assertThat(actDiagnosisKey.getSubmissionTimestamp()).isEqualTo(expSubmissionTimestamp); assertThat(actDiagnosisKey.getKeyData()).isEqualTo(this.expKeyData); assertThat(actDiagnosisKey.getRollingStartIntervalNumber()).isEqualTo(this.expRollingStartIntervalNumber); - assertThat(actDiagnosisKey.getRollingPeriod()).isEqualTo(DiagnosisKey.EXPECTED_ROLLING_PERIOD); + assertThat(actDiagnosisKey.getRollingPeriod()).isEqualTo(DiagnosisKey.MAX_ROLLING_PERIOD); assertThat(actDiagnosisKey.getTransmissionRiskLevel()).isEqualTo(this.expTransmissionRiskLevel); } } diff --git a/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyServiceMockedRepositoryTest.java b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyServiceMockedRepositoryTest.java index 281461cdeb..57f8a0b037 100644 --- a/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyServiceMockedRepositoryTest.java +++ b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/domain/DiagnosisKeyServiceMockedRepositoryTest.java @@ -85,12 +85,12 @@ private void mockInvalidKeyInDb(List keys) { private DiagnosisKey validKey(long expSubmissionTimestamp) { return new DiagnosisKey(expKeyData, expRollingStartIntervalNumber, - DiagnosisKey.EXPECTED_ROLLING_PERIOD, expTransmissionRiskLevel, expSubmissionTimestamp); + DiagnosisKey.MAX_ROLLING_PERIOD, expTransmissionRiskLevel, expSubmissionTimestamp); } private DiagnosisKey invalidKey(long expSubmissionTimestamp) { byte[] expKeyData = "17--bytelongarray".getBytes(StandardCharsets.US_ASCII); return new DiagnosisKey(expKeyData, expRollingStartIntervalNumber, - DiagnosisKey.EXPECTED_ROLLING_PERIOD, expTransmissionRiskLevel, expSubmissionTimestamp); + DiagnosisKey.MAX_ROLLING_PERIOD, expTransmissionRiskLevel, expSubmissionTimestamp); } } diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java index 7babed0f48..1ba2bfdac1 100644 --- a/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java +++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java @@ -59,6 +59,10 @@ public class SubmissionServiceConfig { private Verification verification; private Monitoring monitoring; private Client client; + @Min(0) + @Max(144) + private Integer maxRollingPeriod; + public Long getInitialFakeDelayMilliseconds() { return initialFakeDelayMilliseconds; @@ -108,6 +112,14 @@ public void setMaximumRequestSize(DataSize maximumRequestSize) { this.maximumRequestSize = maximumRequestSize; } + public Integer getMaxRollingPeriod() { + return maxRollingPeriod; + } + + public void setMaxRollingPeriod(Integer maxRollingPeriod) { + this.maxRollingPeriod = maxRollingPeriod; + } + public Integer getMaxNumberOfKeys() { return payload.getMaxNumberOfKeys(); } @@ -119,7 +131,7 @@ public void setPayload(Payload payload) { private static class Payload { @Min(7) - @Max(28) + @Max(100) private Integer maxNumberOfKeys; public Integer getMaxNumberOfKeys() { 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 b5a7547438..b5d0cfe172 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 @@ -20,8 +20,6 @@ package app.coronawarn.server.services.submission.validation; -import static app.coronawarn.server.common.persistence.domain.DiagnosisKey.EXPECTED_ROLLING_PERIOD; - 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; @@ -32,6 +30,7 @@ import java.lang.annotation.Target; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; @@ -68,9 +67,11 @@ class SubmissionPayloadValidator implements ConstraintValidator { private final int maxNumberOfKeys; + private final int maxRollingPeriod; public SubmissionPayloadValidator(SubmissionServiceConfig submissionServiceConfig) { maxNumberOfKeys = submissionServiceConfig.getMaxNumberOfKeys(); + maxRollingPeriod = submissionServiceConfig.getMaxRollingPeriod(); } /** @@ -87,11 +88,14 @@ public boolean isValid(SubmissionPayload submissionPayload, ConstraintValidatorC List exposureKeys = submissionPayload.getKeysList(); validatorContext.disableDefaultConstraintViolation(); - boolean isValid = checkKeyCollectionSize(exposureKeys, validatorContext); - isValid &= checkUniqueStartIntervalNumbers(exposureKeys, validatorContext); - isValid &= checkNoOverlapsInTimeWindow(exposureKeys, validatorContext); - - return isValid; + if (keysHaveFlexibleRollingPeriod(exposureKeys)) { + return checkStartIntervalNumberIsAtMidNight(exposureKeys, validatorContext) + && checkKeysCumulateEqualOrLessThanMaxRollingPeriodPerDay(exposureKeys, validatorContext); + } else { + return checkStartIntervalNumberIsAtMidNight(exposureKeys, validatorContext) + && checkKeyCollectionSize(exposureKeys, validatorContext) + && checkUniqueStartIntervalNumbers(exposureKeys, validatorContext); + } } private void addViolation(ConstraintValidatorContext validatorContext, String message) { @@ -124,23 +128,37 @@ private boolean checkUniqueStartIntervalNumbers(List expos return true; } - private boolean checkNoOverlapsInTimeWindow(List exposureKeys, + private boolean checkKeysCumulateEqualOrLessThanMaxRollingPeriodPerDay(List exposureKeys, ConstraintValidatorContext validatorContext) { - if (exposureKeys.size() < 2) { - return true; + + boolean isValidRollingPeriod = exposureKeys.stream().collect(Collectors + .groupingBy(TemporaryExposureKey::getRollingStartIntervalNumber, + Collectors.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); + } - Integer[] sortedStartIntervalNumbers = exposureKeys.stream() - .mapToInt(TemporaryExposureKey::getRollingStartIntervalNumber) - .sorted().boxed().toArray(Integer[]::new); + private boolean checkStartIntervalNumberIsAtMidNight(List exposureKeys, + ConstraintValidatorContext validatorContext) { + boolean isNotMidNight00Utc = exposureKeys.stream() + .anyMatch(exposureKey -> exposureKey.getRollingStartIntervalNumber() % maxRollingPeriod > 0); - for (int i = 1; i < sortedStartIntervalNumbers.length; i++) { - if ((sortedStartIntervalNumbers[i - 1] + EXPECTED_ROLLING_PERIOD) > sortedStartIntervalNumbers[i]) { - addViolation(validatorContext, String.format( - "Subsequent intervals overlap. StartIntervalNumbers: %s", sortedStartIntervalNumbers)); - return false; - } + if (isNotMidNight00Utc) { + addViolation(validatorContext, "Start Interval Number must be at midnight ( 00:00 UTC )"); + return false; } + return true; } } diff --git a/services/submission/src/main/resources/application.yaml b/services/submission/src/main/resources/application.yaml index 9b8fe59f6a..33d67e1ee2 100644 --- a/services/submission/src/main/resources/application.yaml +++ b/services/submission/src/main/resources/application.yaml @@ -25,9 +25,11 @@ services: connection-pool-size: 200 # The maximum request size accepted by the SubmissionController (e.g. 200B or 100KB). maximum-request-size: ${MAXIMUM_REQUEST_SIZE:100KB} + # The maximum rolling period accepted by the SubmissionController (e.g. 144 (24 hours)) + max-rolling-period: ${MAX_ROLLING_PERIOD:144} payload: # The maximum number of keys accepted for any submission. - max-number-of-keys: 14 + max-number-of-keys: ${MAX_NUMBER_OF_KEYS:14} verification: base-url: ${VERIFICATION_BASE_URL:http://localhost:8004} path: /version/v1/tan/verify @@ -116,4 +118,4 @@ feign: config: default: connect-timeout: 5000 - read-timeout: 5000 \ No newline at end of file + read-timeout: 5000 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 cbd427182a..c7dfddb411 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 @@ -20,11 +20,7 @@ package app.coronawarn.server.services.submission.controller; -import static app.coronawarn.server.services.submission.controller.RequestExecutor.VALID_KEY_DATA_1; -import static app.coronawarn.server.services.submission.controller.RequestExecutor.VALID_KEY_DATA_2; -import static app.coronawarn.server.services.submission.controller.RequestExecutor.VALID_KEY_DATA_3; -import static app.coronawarn.server.services.submission.controller.RequestExecutor.buildTemporaryExposureKey; -import static app.coronawarn.server.services.submission.controller.RequestExecutor.createRollingStartIntervalNumber; +import static app.coronawarn.server.services.submission.controller.RequestExecutor.*; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @@ -36,9 +32,13 @@ import app.coronawarn.server.services.submission.verification.TanVerifier; import java.util.ArrayList; import java.util.Collection; +import java.util.List; + import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -67,22 +67,21 @@ void check400ResponseStatusForMissingKeys() { } @Test - void check400ResponseStatusForTooManyKeys() { + void check400ResponseStatusForTooManyKeysWithFixedRollingPeriod() { ResponseEntity actResponse = executor.executePost(buildPayloadWithTooManyKeys()); - assertThat(actResponse.getStatusCode()).isEqualTo(BAD_REQUEST); } private Collection buildPayloadWithTooManyKeys() { ArrayList tooMany = new ArrayList<>(); for (int i = 0; i <= 20; i++) { - tooMany.add(buildTemporaryExposureKey(VALID_KEY_DATA_1, createRollingStartIntervalNumber(2) + i * DiagnosisKey.EXPECTED_ROLLING_PERIOD , 3)); + tooMany.add(buildTemporaryExposureKey(VALID_KEY_DATA_1, createRollingStartIntervalNumber(2) + i * DiagnosisKey.MAX_ROLLING_PERIOD , 3)); } return tooMany; } @Test - void check400ResponseStatusForDuplicateStartIntervalNumber() { + void check400ResponseStatusForKeysWithFixedRollingPeriodAndDuplicateStartIntervals() { int rollingStartIntervalNumber = createRollingStartIntervalNumber(2); var keysWithDuplicateStartIntervalNumber = Lists.list( buildTemporaryExposureKey(VALID_KEY_DATA_1, rollingStartIntervalNumber, 1), @@ -94,10 +93,10 @@ void check400ResponseStatusForDuplicateStartIntervalNumber() { } @Test - void check200ResponseStatusForGapsInTimeIntervals() { + void check200ResponseStatusForGapsInTimeIntervalsOfKeysWithFixedRollingPeriod() { int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); - int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.EXPECTED_ROLLING_PERIOD; - int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + 2 * DiagnosisKey.EXPECTED_ROLLING_PERIOD; + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.MAX_ROLLING_PERIOD; + int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + 3 * DiagnosisKey.MAX_ROLLING_PERIOD; var keysWithGapsInStartIntervalNumber = Lists.list( buildTemporaryExposureKey(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1), buildTemporaryExposureKey(VALID_KEY_DATA_3, rollingStartIntervalNumber3, 3), @@ -109,30 +108,112 @@ void check200ResponseStatusForGapsInTimeIntervals() { } @Test - void check400ResponseStatusForOverlappingTimeIntervals() { + void check200ResponseStatusForGapsInTimeIntervalsOfKeysWithFlexibleRollingPeriod() { int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); - int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + (DiagnosisKey.EXPECTED_ROLLING_PERIOD / 2); - var keysWithOverlappingStartIntervalNumber = Lists.list( - buildTemporaryExposureKey(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1), - buildTemporaryExposureKey(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2)); + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.MAX_ROLLING_PERIOD; + int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + 3 * DiagnosisKey.MAX_ROLLING_PERIOD; + var keysWithGapsInStartIntervalNumber = Lists.list( + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1, 54), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1, 90), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_3, rollingStartIntervalNumber3, 3, 133), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2, 144)); + + ResponseEntity actResponse = executor.executePost(keysWithGapsInStartIntervalNumber); - ResponseEntity actResponse = executor.executePost(keysWithOverlappingStartIntervalNumber); + assertThat(actResponse.getStatusCode()).isEqualTo(OK); + } + @ParameterizedTest + @MethodSource("app.coronawarn.server.services.submission.controller.TEKDatasetGeneration#getOverlappingTestDatasets") + void check400ResponseStatusForOverlappingTimeIntervalsI(List dataset) { + ResponseEntity actResponse = executor.executePost(dataset); assertThat(actResponse.getStatusCode()).isEqualTo(BAD_REQUEST); } + @ParameterizedTest + @MethodSource("app.coronawarn.server.services.submission.controller.TEKDatasetGeneration#getRollingPeriodDatasets") + void check200ResponseStatusForValidSubmissionPayload(List dataset) { + ResponseEntity actResponse = executor.executePost(dataset); + assertThat(actResponse.getStatusCode()).isEqualTo(OK); + } + + /** + * This test generates a payload with keys for the past 30 days. It verifies that validation passes even + * though keys older than application.yml/retention-period would not be stored. + */ @Test - void check200ResponseStatusForValidSubmissionPayload() { - int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); - int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.EXPECTED_ROLLING_PERIOD; - int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + DiagnosisKey.EXPECTED_ROLLING_PERIOD; - var keysWithValidStartIntervalNumber = Lists.list( - buildTemporaryExposureKey(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1), - buildTemporaryExposureKey(VALID_KEY_DATA_3, rollingStartIntervalNumber3, 3), - buildTemporaryExposureKey(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2)); + void check200ResponseStatusForMoreThan14KeysWithValidFlexibleRollingPeriod() { + ResponseEntity actResponse = executor.executePost(buildPayloadWithMoreThan14KeysAndFlexibleRollingPeriod()); + assertThat(actResponse.getStatusCode()).isEqualTo(OK); + } + + private Collection buildPayloadWithMoreThan14KeysAndFlexibleRollingPeriod() { + ArrayList flexibleRollingPeriodKeys = new ArrayList<>(); + /* Generate keys with fixed rolling period (144) for the past 20 days */ + for (int i = 0 ; i < 20; i++) { + flexibleRollingPeriodKeys.add(buildTemporaryExposureKey(VALID_KEY_DATA_1, + createRollingStartIntervalNumber(2) - i * DiagnosisKey.MAX_ROLLING_PERIOD, 3)); + } + /* Generate another 10 keys with flexible rolling period (<144) */ + for (int i = 20 ; i < 30; i++) { + flexibleRollingPeriodKeys.add(buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, + createRollingStartIntervalNumber(2) - i * DiagnosisKey.MAX_ROLLING_PERIOD, 3, 133)); + + } + return flexibleRollingPeriodKeys; + } - ResponseEntity actResponse = executor.executePost(keysWithValidStartIntervalNumber); + @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()); assertThat(actResponse.getStatusCode()).isEqualTo(OK); } + + private Collection buildPayloadWithTwoKeysOneFlexibleAndOneDefaultOnDifferentDays() { + ArrayList flexibleRollingPeriodKeys = new ArrayList<>(); + + flexibleRollingPeriodKeys.add(buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, + createRollingStartIntervalNumber(2), 3, 100)); + flexibleRollingPeriodKeys.add(buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, + createRollingStartIntervalNumber(3), 3, 144)); + + + return flexibleRollingPeriodKeys; + } + + @Test + void check200ResponseStatusWhenKeysCumulateToMaxRollingPeriodInSameDay() { + ResponseEntity actResponse = executor.executePost(buildPayloadWithTwoKeysWithFlexibleRollingPeriod()); + + assertThat(actResponse.getStatusCode()).isEqualTo(OK); + } + + private Collection buildPayloadWithTwoKeysWithFlexibleRollingPeriod() { + ArrayList flexibleRollingPeriodKeys = new ArrayList<>(); + + flexibleRollingPeriodKeys.add(buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, + createRollingStartIntervalNumber(2), 3, 100)); + flexibleRollingPeriodKeys.add(buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, + createRollingStartIntervalNumber(2), 3, 44)); + + + return flexibleRollingPeriodKeys; + } } diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java index 32ae7308c8..74fd280f6d 100644 --- a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java +++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java @@ -92,6 +92,15 @@ public static TemporaryExposureKey buildTemporaryExposureKey( .setTransmissionRiskLevel(transmissionRiskLevel).build(); } + public static TemporaryExposureKey buildTemporaryExposureKeyWithFlexibleRollingPeriod( + String keyData, int rollingStartIntervalNumber, int transmissionRiskLevel, int rollingPeriod) { + return TemporaryExposureKey.newBuilder() + .setKeyData(ByteString.copyFromUtf8(keyData)) + .setRollingStartIntervalNumber(rollingStartIntervalNumber) + .setTransmissionRiskLevel(transmissionRiskLevel) + .setRollingPeriod(rollingPeriod).build(); + } + public static int createRollingStartIntervalNumber(Integer daysAgo) { return Math.toIntExact(LocalDate .ofInstant(Instant.now(), UTC) @@ -100,6 +109,6 @@ public static int createRollingStartIntervalNumber(Integer daysAgo) { } public static Collection buildPayloadWithOneKey() { - return Collections.singleton(buildTemporaryExposureKey(VALID_KEY_DATA_1, 1, 3)); + return Collections.singleton(buildTemporaryExposureKey(VALID_KEY_DATA_1, createRollingStartIntervalNumber(1), 3)); } } 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 66fc94e627..33b3dc0b8f 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 @@ -254,8 +254,8 @@ private SubmissionPayload buildPayloadWithTooLargePadding() { private Collection buildMultipleKeys() { int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(config.getRetentionDays() - 1); - int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.EXPECTED_ROLLING_PERIOD; - int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + DiagnosisKey.EXPECTED_ROLLING_PERIOD; + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.MAX_ROLLING_PERIOD; + int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + DiagnosisKey.MAX_ROLLING_PERIOD; return Stream.of( buildTemporaryExposureKey(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 3), buildTemporaryExposureKey(VALID_KEY_DATA_2, rollingStartIntervalNumber3, 6), @@ -267,7 +267,7 @@ private TemporaryExposureKey createOutdatedKey() { return TemporaryExposureKey.newBuilder() .setKeyData(ByteString.copyFromUtf8(VALID_KEY_DATA_2)) .setRollingStartIntervalNumber(createRollingStartIntervalNumber(config.getRetentionDays())) - .setRollingPeriod(DiagnosisKey.EXPECTED_ROLLING_PERIOD) + .setRollingPeriod(DiagnosisKey.MAX_ROLLING_PERIOD) .setTransmissionRiskLevel(5).build(); } diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/TEKDatasetGeneration.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/TEKDatasetGeneration.java new file mode 100644 index 0000000000..7be4a19b9e --- /dev/null +++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/TEKDatasetGeneration.java @@ -0,0 +1,113 @@ +/*- + * ---license-start + * Corona-Warn-App + * --- + * Copyright (C) 2020 SAP SE and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package app.coronawarn.server.services.submission.controller; + +import app.coronawarn.server.common.persistence.domain.DiagnosisKey; +import app.coronawarn.server.common.protocols.external.exposurenotification.TemporaryExposureKey; +import org.assertj.core.util.Lists; +import org.junit.jupiter.params.provider.Arguments; +import java.util.List; +import java.util.stream.Stream; + +import static app.coronawarn.server.services.submission.controller.RequestExecutor.*; +import static app.coronawarn.server.services.submission.controller.RequestExecutor.VALID_KEY_DATA_2; + +public class TEKDatasetGeneration { + + private static Stream getOverlappingTestDatasets() { + return Stream.of( + getOverlappingDatasetWithFixedPeriods(), + getOverlappingDatasetWithFlexiblePeriods(), + getMixedOverlappingDataset() + ).map(Arguments::of); + } + + private static List getOverlappingDatasetWithFixedPeriods(){ + int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + (DiagnosisKey.MAX_ROLLING_PERIOD / 2); + return Lists.list( + buildTemporaryExposureKey(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1), + buildTemporaryExposureKey(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2)); + + } + + private static List getOverlappingDatasetWithFlexiblePeriods(){ + int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + (DiagnosisKey.MAX_ROLLING_PERIOD / 2); + return Lists.list( + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1, 54), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1, 90), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2, 133)); + + } + + private static List getMixedOverlappingDataset(){ + int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + (DiagnosisKey.MAX_ROLLING_PERIOD / 2); + return Lists.list( + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1, 54), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1, 1, 90), + buildTemporaryExposureKey(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2)); + + } + + private static Stream getRollingPeriodDatasets() { + return Stream.of( + getFixedRollingPeriodDataset(), + getFlexibleRollingPeriodDataset(), + getMixedRollingPeriodDataset() + ).map(Arguments::of); + } + + private static List getFixedRollingPeriodDataset(){ + int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.MAX_ROLLING_PERIOD; + int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + DiagnosisKey.MAX_ROLLING_PERIOD; + return Lists.list( + buildTemporaryExposureKey(VALID_KEY_DATA_3, rollingStartIntervalNumber1, 3), + buildTemporaryExposureKey(VALID_KEY_DATA_3, rollingStartIntervalNumber3, 3), + buildTemporaryExposureKey(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2)); + } + + private static List getFlexibleRollingPeriodDataset(){ + int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.MAX_ROLLING_PERIOD; + int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + DiagnosisKey.MAX_ROLLING_PERIOD; + return Lists.list( + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1,3, 54), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1,3, 90), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_3, rollingStartIntervalNumber3,3, 133), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_3, rollingStartIntervalNumber3,2, 11), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_2, rollingStartIntervalNumber2,2, 100)); + } + + private static List getMixedRollingPeriodDataset(){ + int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(6); + int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.MAX_ROLLING_PERIOD; + int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + DiagnosisKey.MAX_ROLLING_PERIOD; + return Lists.list( + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1,3, 54), + buildTemporaryExposureKeyWithFlexibleRollingPeriod(VALID_KEY_DATA_1, rollingStartIntervalNumber1,3, 90), + buildTemporaryExposureKey(VALID_KEY_DATA_3, rollingStartIntervalNumber3, 3), + buildTemporaryExposureKey(VALID_KEY_DATA_2, rollingStartIntervalNumber2, 2)); + } + +} diff --git a/services/submission/src/test/resources/application.yaml b/services/submission/src/test/resources/application.yaml index 908e1c9208..53992cd0a9 100644 --- a/services/submission/src/test/resources/application.yaml +++ b/services/submission/src/test/resources/application.yaml @@ -26,6 +26,7 @@ services: random-key-padding-multiplier: 10 connection-pool-size: 200 maximum-request-size: 100KB + max-rolling-period: 144 payload: max-number-of-keys: 14 verification: