From ccbfe413378ec4b7d48c96840067e0923980d549 Mon Sep 17 00:00:00 2001 From: Christian Kirschnick Date: Mon, 22 Jun 2020 13:33:07 +0200 Subject: [PATCH] Release 1.0.8 (#613) * Introduce new version 1.0.8 * Disable UserDetailsServiceAutoConfiguration (#594) * Cherry-Pick: Add random key padding (#609) * Cherry-Pick: Minor code prettification (#612) Co-authored-by: Johannes Eschrig Co-authored-by: Pit Humke --- .mvn/maven.config | 2 +- .../submission/ServerApplication.java | 3 +- .../config/SubmissionServiceConfig.java | 15 +++++ .../controller/SubmissionController.java | 28 ++++++++- .../src/main/resources/application.yaml | 6 ++ .../controller/SubmissionControllerTest.java | 61 ++++++++++++------- .../src/test/resources/application.yaml | 1 + 7 files changed, 92 insertions(+), 24 deletions(-) diff --git a/.mvn/maven.config b/.mvn/maven.config index 7518e74f44..f7ddc695fb 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1,4 +1,4 @@ --Drevision=1.0.7 +-Drevision=1.0.8 -Dlicense.projectName=Corona-Warn-App -Dlicense.inceptionYear=2020 -Dlicense.licenseName=apache_v2 diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java index 85a63e9ecd..d16a2a2357 100644 --- a/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java +++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java @@ -25,6 +25,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; @@ -32,7 +33,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; -@SpringBootApplication +@SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class}) @EnableJpaRepositories(basePackages = "app.coronawarn.server.common.persistence") @EntityScan(basePackages = "app.coronawarn.server.common.persistence") @ComponentScan({"app.coronawarn.server.common.persistence", 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 0ca6d6992a..d1ea2b034c 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 @@ -20,11 +20,15 @@ package app.coronawarn.server.services.submission.config; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; @Component @ConfigurationProperties(prefix = "services.submission") +@Validated public class SubmissionServiceConfig { // Exponential moving average of the last N real request durations (in ms), where @@ -32,6 +36,9 @@ public class SubmissionServiceConfig { private Double initialFakeDelayMilliseconds; private Double fakeDelayMovingAverageSamples; private Integer retentionDays; + @Min(1) + @Max(100) + private Integer randomKeyPaddingMultiplier; private Integer connectionPoolSize; private Payload payload; private Verification verification; @@ -61,6 +68,14 @@ public void setRetentionDays(Integer retentionDays) { this.retentionDays = retentionDays; } + public Integer getRandomKeyPaddingMultiplier() { + return randomKeyPaddingMultiplier; + } + + public void setRandomKeyPaddingMultiplier(Integer randomKeyPaddingMultiplier) { + this.randomKeyPaddingMultiplier = randomKeyPaddingMultiplier; + } + public Integer getConnectionPoolSize() { return connectionPoolSize; } diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java index 23a4a7ca45..6fa2672610 100644 --- a/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java +++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java @@ -29,11 +29,13 @@ import app.coronawarn.server.services.submission.validation.ValidSubmissionPayload; import app.coronawarn.server.services.submission.verification.TanVerifier; import io.micrometer.core.annotation.Timed; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; import org.apache.commons.math3.distribution.PoissonDistribution; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +67,7 @@ public class SubmissionController { private final TanVerifier tanVerifier; private final Double fakeDelayMovingAverageSamples; private final Integer retentionDays; + private final Integer randomKeyPaddingMultiplier; private Double fakeDelay; SubmissionController(DiagnosisKeyService diagnosisKeyService, TanVerifier tanVerifier, @@ -75,6 +78,7 @@ public class SubmissionController { fakeDelay = submissionServiceConfig.getInitialFakeDelayMilliseconds(); fakeDelayMovingAverageSamples = submissionServiceConfig.getFakeDelayMovingAverageSamples(); retentionDays = submissionServiceConfig.getRetentionDays(); + randomKeyPaddingMultiplier = submissionServiceConfig.getRandomKeyPaddingMultiplier(); submissionControllerMonitor.initializeGauges(this); } @@ -166,7 +170,29 @@ public void persistDiagnosisKeysPayload(SubmissionPayload protoBufDiagnosisKeys) } } - diagnosisKeyService.saveDiagnosisKeys(diagnosisKeys); + diagnosisKeyService.saveDiagnosisKeys(padDiagnosisKeys(diagnosisKeys)); + } + + private List padDiagnosisKeys(List diagnosisKeys) { + List paddedDiagnosisKeys = new ArrayList<>(); + diagnosisKeys.forEach(diagnosisKey -> { + paddedDiagnosisKeys.add(diagnosisKey); + IntStream.range(1, randomKeyPaddingMultiplier) + .mapToObj(index -> DiagnosisKey.builder() + .withKeyData(generateRandomKeyData()) + .withRollingStartIntervalNumber(diagnosisKey.getRollingStartIntervalNumber()) + .withTransmissionRiskLevel(diagnosisKey.getTransmissionRiskLevel()) + .withRollingPeriod(diagnosisKey.getRollingPeriod()) + .build()) + .forEach(paddedDiagnosisKeys::add); + }); + return paddedDiagnosisKeys; + } + + private static byte[] generateRandomKeyData() { + byte[] randomKeyData = new byte[16]; + new SecureRandom().nextBytes(randomKeyData); + return randomKeyData; } private void updateFakeDelay(long realRequestDuration) { diff --git a/services/submission/src/main/resources/application.yaml b/services/submission/src/main/resources/application.yaml index b1907a673f..a58b571786 100644 --- a/services/submission/src/main/resources/application.yaml +++ b/services/submission/src/main/resources/application.yaml @@ -12,6 +12,12 @@ services: # The number of samples for the calculation of the moving average for fake request delays. fake-delay-moving-average-samples: 10 retention-days: 14 + # The number of keys to save to the DB for every real submitted key. + # Example: If the 'random-key-padding-multiplier' is set to 10, and 5 keys are being submitted, + # then the 5 real submitted keys will be saved to the DB, plus an additional 45 keys with + # random 'key_data'. All properties, besides the 'key_data', of the additional keys will be + # identical to the real key. + random-key-padding-multiplier: ${RANDOM_KEY_PADDING_MULTIPLIER:1} connection-pool-size: 200 payload: max-number-of-keys: 14 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 7b2d09cdf5..34b4f2ddb1 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 @@ -122,27 +122,27 @@ void singleKeyWithOutdatedRollingStartIntervalNumberDoesNotGetSaved() { @Test void keysWithOutdatedRollingStartIntervalNumberDoNotGetSaved() { - Collection keys = buildPayloadWithMultipleKeys(); + Collection submittedKeys = buildPayloadWithMultipleKeys(); TemporaryExposureKey outdatedKey = createOutdatedKey(); - keys.add(outdatedKey); + submittedKeys.add(outdatedKey); ArgumentCaptor> argument = ArgumentCaptor.forClass(Collection.class); - executor.executeRequest(keys, buildOkHeaders()); + executor.executeRequest(submittedKeys, buildOkHeaders()); verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(argument.capture()); - keys.remove(outdatedKey); - assertElementsCorrespondToEachOther(keys, argument.getValue()); + submittedKeys.remove(outdatedKey); + assertElementsCorrespondToEachOther(submittedKeys, argument.getValue()); } @Test - void checkSaveOperationCallForValidParameters() { - Collection keys = buildPayloadWithMultipleKeys(); + void checkSaveOperationCallAndFakeDelayUpdateForValidParameters() { + Collection submittedKeys = buildPayloadWithMultipleKeys(); ArgumentCaptor> argument = ArgumentCaptor.forClass(Collection.class); - executor.executeRequest(keys, buildOkHeaders()); + executor.executeRequest(submittedKeys, buildOkHeaders()); verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(argument.capture()); - assertElementsCorrespondToEachOther(keys, argument.getValue()); + assertElementsCorrespondToEachOther(submittedKeys, argument.getValue()); } @ParameterizedTest @@ -181,7 +181,7 @@ 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(elem -> Arguments.of(elem)); + .map(Arguments::of); } @Test @@ -237,18 +237,37 @@ private static Collection buildPayloadWithInvalidKey() { buildTemporaryExposureKey(VALID_KEY_DATA_1, createRollingStartIntervalNumber(2), 999)) .collect(Collectors.toCollection(ArrayList::new)); } - private void assertElementsCorrespondToEachOther - (Collection submittedKeys, Collection keyEntities) { - Set expKeys = submittedKeys.stream() - .map(aSubmittedKey -> DiagnosisKey.builder().fromProtoBuf(aSubmittedKey).build()) + + private void assertElementsCorrespondToEachOther(Collection submittedTemporaryExposureKeys, + Collection savedDiagnosisKeys) { + + Set submittedDiagnosisKeys = submittedTemporaryExposureKeys.stream() + .map(submittedDiagnosisKey -> DiagnosisKey.builder().fromProtoBuf(submittedDiagnosisKey).build()) .collect(Collectors.toSet()); - assertThat(keyEntities.size()) - .withFailMessage("Number of submitted keys and generated key entities don't match.") - .isEqualTo(expKeys.size()); - keyEntities.forEach(anActKey -> assertThat(expKeys) - .withFailMessage("Key entity does not correspond to a submitted key.") - .contains(anActKey) - ); + assertThat(savedDiagnosisKeys).hasSize(submittedDiagnosisKeys.size() * config.getRandomKeyPaddingMultiplier()); + assertThat(savedDiagnosisKeys).containsAll(submittedDiagnosisKeys); + + submittedDiagnosisKeys.forEach(submittedDiagnosisKey -> { + List savedKeysForSingleSubmittedKey = savedDiagnosisKeys.stream() + .filter(savedDiagnosisKey -> savedDiagnosisKey.getRollingPeriod() == + submittedDiagnosisKey.getRollingPeriod()) + .filter(savedDiagnosisKey -> savedDiagnosisKey.getTransmissionRiskLevel() == + submittedDiagnosisKey.getTransmissionRiskLevel()) + .filter(savedDiagnosisKey -> savedDiagnosisKey.getRollingStartIntervalNumber() == + submittedDiagnosisKey.getRollingStartIntervalNumber()) + .collect(Collectors.toList()); + + assertThat(savedKeysForSingleSubmittedKey).hasSize(config.getRandomKeyPaddingMultiplier()); + assertThat(savedKeysForSingleSubmittedKey.stream().filter(savedKey -> + Arrays.equals(savedKey.getKeyData(), submittedDiagnosisKey.getKeyData()))).hasSize(1); + assertThat(savedKeysForSingleSubmittedKey).allMatch( + savedKey -> savedKey.getRollingPeriod() == submittedDiagnosisKey.getRollingPeriod()); + assertThat(savedKeysForSingleSubmittedKey).allMatch( + savedKey -> savedKey.getRollingStartIntervalNumber() == submittedDiagnosisKey + .getRollingStartIntervalNumber()); + assertThat(savedKeysForSingleSubmittedKey).allMatch( + savedKey -> savedKey.getTransmissionRiskLevel() == submittedDiagnosisKey.getTransmissionRiskLevel()); + }); } } diff --git a/services/submission/src/test/resources/application.yaml b/services/submission/src/test/resources/application.yaml index 0f7d87385a..c4b8409067 100644 --- a/services/submission/src/test/resources/application.yaml +++ b/services/submission/src/test/resources/application.yaml @@ -25,6 +25,7 @@ services: initial-fake-delay-milliseconds: 1 fake-delay-moving-average-samples: 1 retention-days: 14 + random-key-padding-multiplier: 10 connection-pool-size: 200 payload: max-number-of-keys: 14