From d02db125c807eb5b0ccc029b4e5aac1e857a5ced Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Mon, 22 Jun 2020 08:59:39 +0200 Subject: [PATCH 01/27] add actuator basic auth --- .../sdk/ws/config/ActuatorSecurity.java | 57 +++++++++++++++++++ .../sdk/ws/config/WSCloudBaseConfig.java | 2 + 2 files changed, 59 insertions(+) create mode 100644 dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java new file mode 100644 index 00000000..06b65491 --- /dev/null +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java @@ -0,0 +1,57 @@ +package org.dpppt.backend.sdk.ws.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthEndpoint; +import org.springframework.boot.actuate.info.InfoEndpoint; +import org.springframework.boot.actuate.logging.LoggersEndpoint; +import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@Order(Ordered.HIGHEST_PRECEDENCE + 9) +@Profile(value = "actuator-security") +@EnableWebSecurity +public class ActuatorSecurity extends WebSecurityConfigurerAdapter { + + private static final String PROMETHEUS_ROLE = "PROMETHEUS"; + + @Value("${authcodegeneration.monitor.prometheus.user}") + private String user; + @Value("${authcodegeneration.monitor.prometheus.password}") + private String password; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.requestMatcher(org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.toAnyEndpoint()). + authorizeRequests(). + requestMatchers(org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.to(HealthEndpoint.class)).permitAll(). + requestMatchers(org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.to(InfoEndpoint.class)).permitAll(). + requestMatchers(org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.to(LoggersEndpoint.class)).hasRole(PROMETHEUS_ROLE). + requestMatchers(org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.to(PrometheusScrapeEndpoint.class)).hasRole(PROMETHEUS_ROLE). + anyRequest().denyAll(). + and(). + httpBasic(); + + http.csrf().ignoringAntMatchers("/actuator/loggers/**"); + } + + @Autowired + protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication().withUser(user).password(passwordEncoder().encode(password)).roles(PROMETHEUS_ROLE); + } + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/WSCloudBaseConfig.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/WSCloudBaseConfig.java index 275d95d4..dc54d88e 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/WSCloudBaseConfig.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/WSCloudBaseConfig.java @@ -20,11 +20,13 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; @Configuration public abstract class WSCloudBaseConfig extends WSBaseConfig { @Autowired + @Lazy private DataSource dataSource; abstract String getPublicKey(); From 2b2b2d3f4f304139c53dd669284a97f91feadee4 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Mon, 22 Jun 2020 09:01:29 +0200 Subject: [PATCH 02/27] use ws as root node --- .../org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java index 06b65491..26e95e14 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java @@ -26,9 +26,9 @@ public class ActuatorSecurity extends WebSecurityConfigurerAdapter { private static final String PROMETHEUS_ROLE = "PROMETHEUS"; - @Value("${authcodegeneration.monitor.prometheus.user}") + @Value("${ws.monitor.prometheus.user}") private String user; - @Value("${authcodegeneration.monitor.prometheus.password}") + @Value("${ws.monitor.prometheus.password}") private String password; @Override From ff3ae864e465dc598d7923401bbb7d00d02d0cb4 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Mon, 22 Jun 2020 09:18:54 +0200 Subject: [PATCH 03/27] test actuator security --- .../sdk/ws/controller/GaenControllerTest.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java index 3f068e03..c2cccb08 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java @@ -59,14 +59,20 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MvcResult; import org.springframework.transaction.annotation.Transactional; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; +@ActiveProfiles({"actuator-security"}) @SpringBootTest(properties = { "ws.app.jwt.publickey=classpath://generated_pub.pem", - "logging.level.org.springframework.security=DEBUG", "ws.exposedlist.batchlength=7200000", "ws.gaen.randomkeysenabled=true" }) + "logging.level.org.springframework.security=DEBUG", "ws.exposedlist.batchlength=7200000", "ws.gaen.randomkeysenabled=true", + "ws.monitor.prometheus.user=prometheus", + "ws.monitor.prometheus.password=prometheus", + "management.endpoints.enabled-by-default=true", + "management.endpoints.web.exposure.include=*"}) public class GaenControllerTest extends BaseControllerTest { @Autowired ProtoSignature signer; @@ -86,6 +92,19 @@ public void testHello() throws Exception { assertNotNull(response); assertEquals("Hello from DP3T WS", response.getContentAsString()); } + @Test + public void testActuatorSecurity() throws Exception { + var response = mockMvc.perform(get("/actuator/health")).andExpect(status().is2xxSuccessful()).andReturn() + .getResponse(); + response = mockMvc.perform(get("/actuator/loggers")).andExpect(status().is(401)).andReturn() + .getResponse(); + response = mockMvc.perform(get("/actuator/loggers").header("Authorization", "Basic cHJvbWV0aGV1czpwcm9tZXRoZXVz")).andExpect(status().isOk()).andReturn() + .getResponse(); + response = mockMvc.perform(get("/actuator/prometheus")).andExpect(status().is(401)).andReturn() + .getResponse(); + response = mockMvc.perform(get("/actuator/prometheus").header("Authorization", "Basic cHJvbWV0aGV1czpwcm9tZXRoZXVz")).andExpect(status().isOk()).andReturn() + .getResponse(); + } @Test public void testMultipleKeyUpload() throws Exception { From 5a802b5115304814e935c4ae650416b87105f08c Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 23 Jun 2020 11:43:14 +0200 Subject: [PATCH 04/27] encapsulate all keys in one config --- .../sdk/ws/config/ActuatorSecurity.java | 42 +++++++++++++++++-- .../configbeans/ActuatorSecurityConfig.java | 19 +++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/configbeans/ActuatorSecurityConfig.java diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java index 26e95e14..fbb8e041 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java @@ -1,16 +1,20 @@ package org.dpppt.backend.sdk.ws.config; +import org.dpppt.backend.sdk.ws.config.configbeans.ActuatorSecurityConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; import org.springframework.boot.actuate.logging.LoggersEndpoint; import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -28,9 +32,39 @@ public class ActuatorSecurity extends WebSecurityConfigurerAdapter { @Value("${ws.monitor.prometheus.user}") private String user; - @Value("${ws.monitor.prometheus.password}") - private String password; + @Autowired Environment environment; + // region Actuator Passwords + //---------------------------------------------------------------------------------------------------------------------------------- + @Bean + @Profile("cloud-dev") + ActuatorSecurityConfig passwordCloudDev() { + return new ActuatorSecurityConfig(user, environment.getProperty("vcap.services.ha_prometheus_dev.credentials.password")); + } + @Bean + @Profile("cloud-test") + ActuatorSecurityConfig passwordCloudTest() { + return new ActuatorSecurityConfig(user, environment.getProperty("vcap.services.ha_prometheus_test.credentials.password")); + } + @Bean + @Profile("cloud-abn") + ActuatorSecurityConfig passwordCloudAbn() { + return new ActuatorSecurityConfig(user, environment.getProperty("vcap.services.ha_prometheus_abn.credentials.password")); + } + @Bean + @Profile("cloud-prod") + ActuatorSecurityConfig passwordProdAbn() { + return new ActuatorSecurityConfig(user, environment.getProperty("vcap.services.ha_prometheus_prod.credentials.password")); + } + + @Bean + @ConditionalOnMissingBean + ActuatorSecurityConfig passwordDefault() { + return new ActuatorSecurityConfig(user, environment.getProperty("ws.monitor.prometheus.password")); + } + //---------------------------------------------------------------------------------------------------------------------------------- + //endregion + @Override protected void configure(HttpSecurity http) throws Exception { http.requestMatcher(org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.toAnyEndpoint()). @@ -47,8 +81,8 @@ protected void configure(HttpSecurity http) throws Exception { } @Autowired - protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser(user).password(passwordEncoder().encode(password)).roles(PROMETHEUS_ROLE); + protected void configureGlobal(AuthenticationManagerBuilder auth, ActuatorSecurityConfig securityConfig) throws Exception { + auth.inMemoryAuthentication().withUser(securityConfig.getUsername()).password(passwordEncoder().encode(securityConfig.getPassword())).roles(PROMETHEUS_ROLE); } @Bean public PasswordEncoder passwordEncoder() { diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/configbeans/ActuatorSecurityConfig.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/configbeans/ActuatorSecurityConfig.java new file mode 100644 index 00000000..d29580b5 --- /dev/null +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/configbeans/ActuatorSecurityConfig.java @@ -0,0 +1,19 @@ +package org.dpppt.backend.sdk.ws.config.configbeans; + +public class ActuatorSecurityConfig { + private final String username; + private final String password; + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public ActuatorSecurityConfig(String username, String password) { + this.username = username; + this.password = password; + } +} \ No newline at end of file From 24a094adc4f450b8c875f59c631a293a98245ae5 Mon Sep 17 00:00:00 2001 From: alig Date: Tue, 23 Jun 2020 11:53:11 +0200 Subject: [PATCH 05/27] Cleanup Imports. --- .../java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java | 1 - .../org/dpppt/backend/sdk/ws/filter/ResponseWrapperFilter.java | 1 - 2 files changed, 2 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java index fbb8e041..8eb6a2ad 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/config/ActuatorSecurity.java @@ -8,7 +8,6 @@ import org.springframework.boot.actuate.logging.LoggersEndpoint; import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/filter/ResponseWrapperFilter.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/filter/ResponseWrapperFilter.java index a53e2e10..0f113737 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/filter/ResponseWrapperFilter.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/filter/ResponseWrapperFilter.java @@ -15,7 +15,6 @@ import java.security.PublicKey; import java.security.Security; import java.util.List; -import java.util.Map; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; From c75c3d9f444fb788e91fdf439fa607334742aa4c Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 23 Jun 2020 12:02:31 +0200 Subject: [PATCH 06/27] changes: - remove etag - add JWT validation info - adjust documentation for handling fake requests --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 865b9d22..571a4749 100644 --- a/README.md +++ b/README.md @@ -83,19 +83,23 @@ By default, the EN controller (`GaenController`) uses the base url `/v1/gaen/`. The web-service provides three endpoints, which map the Exposure Notification specifications. -- /v1/gaen/exposed: `POST` Send a list of infected keys. If a JWT is used to authorize the request, send a JWT back to authorize the call of the following day. If the request as a whole is a "fake"-request, all keys need to be fake, otherwise a 400 HttpStatus code is returned. +- /v1/gaen/exposed: `POST` Send a list of infected keys. If a JWT is used to authorize the request, send a JWT back to authorize the call of the following day. If the request as a whole is a "fake"-request, meaning the JWT has the "fake" claim set to "1", no keys are inserted. - /v1/gaen/exposednextday: `POST` Since the EN framework won't give the key of the current day, the upload has to be split into two. This endpoint receives the key of the last day. This endpoint eventually will be removed, as soon as the EN framework is adjusted. If JWT are activated, this endpoint needs the JWT received from a successfull request to `/v1/gaen/exposed`. - /v1/gaen/exposed/\?publishedafter=\: `GET` Returns a list of keys, which were used at `timestamp`. Note that `timestamp` needs to be epoch milliseconds. Since the behaviour of Android and iOS aren't the same, the optional `publishedAfter` parameter is added. If set only keys, which were received *after* `publishAfter` are returned. This request returns `ZIP` file containing `export.bin` and `export.sig`, where the keys and the signature are stored, as need by the EN framework. The class for signing and serializing is `ProtoSignature`. - Further endpoints are added to the controller for debugging purposes. Those requests can e.g. be blocked by a WAF rule or similiar. -## ETag -To profit from caching e.g. when all requests are routed through a CDN, an ETag is set. The value of the ETag is the content hash of the protobuf key list. The ETag will change whenever new keys are added. +## JWT Validation +In order to prevent replay attacks, a JWT is only valid once. After one usage the "jit" claim is stored temporarily (longer than the validity). +The JWTs are validated based on the following criterias: + + - JWT needs an expiresAt claim which is valid + - The expiresAt claim should not be longer than the set value in the config + - The JWT needs a "jit" claim and the said "jit" should not have been used + -Note: Since we use ECDSA signatures, the signature would change each time. The same is true for the hash of the HttpPayload. ## Content Signing In order to provide authenticity, the HttpPayload is signed. The signature is added as a `Signature` HttpHeader to each 200 or 204 response. The signature consists of a JWT containing the content hash as a claim. Further, if `protected-headers` are added in the controller (and activated through the respective key in the properties file), those headers are signed as well, thus providing the possibility of signing certain request parameters. From 7691f69f432072357df4eab2eeefdf5557a235bd Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 23 Jun 2020 13:20:15 +0200 Subject: [PATCH 07/27] remove unused fake flag --- .../org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java index 0c31307a..59e98756 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java @@ -7,7 +7,6 @@ public class GaenSecondDay { @NotNull @Valid private GaenKey delayedKey; - private Integer fake; public GaenKey getDelayedKey() { @@ -18,12 +17,4 @@ public void setDelayedKey(GaenKey delayedKey) { this.delayedKey = delayedKey; } - public Integer getFake() { - return this.fake; - } - - public void setFake(Integer fake) { - this.fake = fake; - } - } \ No newline at end of file From 0ee23baa8a02468e8a246dbf0d36e3afba1915f3 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 23 Jun 2020 13:34:10 +0200 Subject: [PATCH 08/27] changes: strip-jar, set timestamp to last commi --- dpppt-backend-sdk/pom.xml | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/dpppt-backend-sdk/pom.xml b/dpppt-backend-sdk/pom.xml index dbe046f6..477db1fb 100644 --- a/dpppt-backend-sdk/pom.xml +++ b/dpppt-backend-sdk/pom.xml @@ -242,6 +242,66 @@ + + + pl.project13.maven + git-commit-id-plugin + + + retrieve-git-info + prepare-package + + revision + + + + + true + true + false + yyyyMMddHHmmss + UTC + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${git.commit.id} + ${git.commit.time} + true + + + + + + + io.github.zlika + reproducible-build-maven-plugin + 0.12 + + + strip-jaxb + + strip-jaxb + + + + strip-jar + + strip-jar + + + ${git.commit.time} + + + + + + From 24b0bba0036be4bec0ab7d6514771416a54d1db4 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 23 Jun 2020 13:39:05 +0200 Subject: [PATCH 09/27] changes: calculate shasum and include it in the artifacts --- .github/workflows/maven.yml | 5 ++++- .github/workflows/tagged_release.yaml | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index f9fb20cc..0fdb0633 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -59,6 +59,9 @@ jobs: run: mvn deploy -Dmaven.test.skip=true --file dpppt-backend-sdk/pom.xml -s $GITHUB_WORKSPACE/settings.xml env: GITHUB_TOKEN: ${{ github.token }} + - name: shasum + run: shasum -a 256 dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.jar > dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.sha256 + shell: bash - name: Create Snapshot Release uses: ncipollo/release-action@v1 if: github.ref == 'refs/heads/develop' @@ -66,7 +69,7 @@ jobs: with: name: Snapshot Release ${{ github.ref }} tag: SNAPSHOT - artifacts: "dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.jar,dpppt-backend-sdk/dpppt-backend-sdk-ws/generated/swagger/swagger.yaml" + artifacts: "dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.jar,dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.sha256,dpppt-backend-sdk/dpppt-backend-sdk-ws/generated/swagger/swagger.yaml" body: | Changes in this Release - Snapshot release diff --git a/.github/workflows/tagged_release.yaml b/.github/workflows/tagged_release.yaml index 5ea1136f..c3c9eda4 100644 --- a/.github/workflows/tagged_release.yaml +++ b/.github/workflows/tagged_release.yaml @@ -33,6 +33,9 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} TESTCONTAINERS_RYUK_DISABLED: true + - name: shasum + run: shasum -a 256 dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.jar > dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.sha256 + shell: bash - name: "Create new release" uses: "marvinpinto/action-automatic-releases@latest" with: @@ -40,4 +43,6 @@ jobs: prerelease: false files: | dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.jar + dpppt-backend-sdk/dpppt-backend-sdk-ws/target/dpppt-backend-sdk-ws.sha256 dpppt-backend-sdk/dpppt-backend-sdk-ws/generated/swagger/swagger.yaml + From 92e8e3f8949ac3cd0643c8ae84ce4dd6d793a8fb Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 23 Jun 2020 13:43:30 +0200 Subject: [PATCH 10/27] add a section about reproducible builds --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 865b9d22..cd4653d6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ Please read the [Contribution Guide](CONTRIBUTING.md) before submitting any pull ## Introduction This documentation describes the backend used for the SwissCovid application. It is focused on providing information for the requests used for the Exposure Notification framework. Although, code of the old format is still provided, no documentation or support is available except the code itself. +## Reproducible Builds +In order to have reproducible builds the [io.github.zlika](https://github.com/zlika/reproducible-build-maven-plugin) maven plugin is used. It replaces all timestamp with the timestamp of the last commit, and orders the entries in the JAR alphabetically. The github action then computes the sha256sum of the resulting JAR and adds the output as an build artifact. + ## Dependencies * Spring Boot 2.2.6 * Java 11 (or higher) From b51c16652b287e980828d2236312009c5a3c4e0c Mon Sep 17 00:00:00 2001 From: Frank Werner-Krippendorf Date: Thu, 25 Jun 2020 11:39:34 +0200 Subject: [PATCH 11/27] README.md, appended instructions on how to configure GitHub Packages as clarified in DP-3T/dp3t-sdk-backend#165 --- README.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e301726a..cf968808 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,26 @@ In order to have reproducible builds the [io.github.zlika](https://github.com/zl * Spring Boot 2.2.6 * Java 11 (or higher) * Logback -* [Springboot-Swagger-3](https://github.com/Ubique-OSS/springboot-swagger3) (Github Package, plugin dependency) +* [Springboot-Swagger-3](https://github.com/Ubique-OSS/springboot-swagger3) + +### GitHub Packages +Parts of the Maven build depends on artifacts, that are hosted on [GitHub Packages](https://help.github.com/en/packages). To gain access, a personal access token is required: + +* Issue a personal access token from https://github.com/settings/tokens and be sure to assign the `read:packages` scope to it. +* Append the server configuration to your local Maven settings.xml, in most cases `~/.m2/settings.xml`. In the servers tag, add a child server tag with the id `github`, replacing `GITHUB_USERNAME` with your GitHub username, and `PERSONAL_ACCESS_TOKEN` with your personal access token. + ```xml + ... + + + github + GITHUB_USERNAME + PERSONAL_ACCESS_TOKEN + + + ... + ``` + +Please consult the [official documentation](https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-apache-maven-for-use-with-github-packages) for further maven specific instructions. ### Database For development purposes an hsqldb can be used to run the webservice locally. For production systems, it is recommended to connect to a PostgreSQL dabatase (cluster if possible). There are two tables storing keys, one for the DP3T vendor independent format, one for the Google/Apple approach format. Further, to protect against JWT replay attacks a third table stores the JTI temporarily. The schemas are as following : From 61f702e950786ae6401b1568d28fb1909dc86bc0 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Mon, 6 Jul 2020 09:25:45 +0200 Subject: [PATCH 12/27] removed exposedjson --- .../sdk/ws/controller/GaenController.java | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java index 13e07538..40f9f6eb 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java @@ -251,34 +251,6 @@ public GaenController(GAENDataService dataService, FakeKeyService fakeKeyService .header("X-PUBLISHED-UNTIL", Long.toString(publishedUntil)).body(payload.getZip()); } - @GetMapping(value = "/exposedjson/{keyDate}", produces = "application/json") - public @ResponseBody ResponseEntity getExposedKeysAsJson(@PathVariable long keyDate, - @RequestParam(required = false) Long publishedafter, WebRequest request) - throws BadBatchReleaseTimeException { - if (!validationUtils.isValidKeyDate(keyDate)) { - return ResponseEntity.notFound().build(); - } - if (publishedafter != null && !validationUtils.isValidBatchReleaseTime(publishedafter)) { - return ResponseEntity.notFound().build(); - } - - long now = System.currentTimeMillis(); - // calculate exposed until bucket - long publishedUntil = now - (now % bucketLength.toMillis()); - - var exposedKeys = dataService.getSortedExposedForKeyDate(keyDate, publishedafter, publishedUntil); - if (exposedKeys.isEmpty()) { - return ResponseEntity.noContent().cacheControl(CacheControl.maxAge(exposedListCacheControl)) - .header("X-PUBLISHED-UNTIL", Long.toString(publishedUntil)).build(); - } - - var file = new GaenExposedJson(); - var header = new Header(); - file.gaenKeys(exposedKeys).header(header); - return ResponseEntity.ok().cacheControl(CacheControl.maxAge(exposedListCacheControl)) - .header("X-PUBLISHED-UNTIL", Long.toString(publishedUntil)).body(file); - } - @GetMapping(value = "/buckets/{dayDateStr}") public @ResponseBody ResponseEntity getBuckets(@PathVariable String dayDateStr) { var atStartOfDay = LocalDate.parse(dayDateStr).atStartOfDay().toInstant(ZoneOffset.UTC) From 04bfb50768cf62c769b590ebf1bf4ef0c82f658d Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Mon, 6 Jul 2020 11:08:59 +0200 Subject: [PATCH 13/27] fix only midnight ok --- .../java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java index 1e86e0b0..925444d4 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java @@ -11,6 +11,8 @@ import java.time.Duration; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Base64; @@ -55,7 +57,7 @@ public boolean isDateInRange(OffsetDateTime timestamp) { * @return */ public boolean isValidKeyDate(long keyDate) { - return (Instant.ofEpochMilli(keyDate).atOffset(ZoneOffset.UTC).getHour() == 0); + return LocalDateTime.ofInstant(Instant.ofEpochMilli(keyDate), ZoneOffset.UTC).toLocalTime().equals(LocalTime.MIDNIGHT); } public boolean isValidBatchReleaseTime(long batchReleaseTime) throws BadBatchReleaseTimeException { From 21fb3f07676b6c63d49a10e3ae4f77c42d7bab06 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 7 Jul 2020 08:22:03 +0200 Subject: [PATCH 14/27] organize imports --- .../sdk/ws/security/signature/SignatureResponseWrapper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/signature/SignatureResponseWrapper.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/signature/SignatureResponseWrapper.java index e5eccc21..e2808b7e 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/signature/SignatureResponseWrapper.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/signature/SignatureResponseWrapper.java @@ -24,7 +24,6 @@ import java.util.Base64; import java.util.Date; import java.util.List; -import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; From 6b0e2e1ee0425301795605d5c67ee02daf2a502e Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 7 Jul 2020 20:49:22 +0200 Subject: [PATCH 15/27] test validation utils --- .../sdk/ws/util/ValidationUtilsTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java new file mode 100644 index 00000000..27088c66 --- /dev/null +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java @@ -0,0 +1,28 @@ +package org.dpppt.backend.sdk.ws.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneOffset; + +import org.junit.Test; + +public class ValidationUtilsTest { + @Test + public void testOnlyMidnightIsValid() throws Exception { + var validationUtils = new ValidationUtils(16, Duration.ofDays(14), Duration.ofHours(2).toMillis()); + var midnight = LocalDate.now(ZoneOffset.UTC).atStartOfDay(ZoneOffset.UTC); + assertTrue(validationUtils.isValidKeyDate(midnight.toInstant().toEpochMilli())); + assertFalse(validationUtils.isValidKeyDate(midnight.minusSeconds(1).toInstant().toEpochMilli())); + assertFalse(validationUtils.isValidKeyDate(midnight.minusMinutes(1).toInstant().toEpochMilli())); + assertFalse(validationUtils.isValidKeyDate(midnight.minusHours(1).toInstant().toEpochMilli())); + assertFalse(validationUtils.isValidKeyDate(midnight.plusSeconds(1).toInstant().toEpochMilli())); + assertFalse(validationUtils.isValidKeyDate(midnight.plusMinutes(1).toInstant().toEpochMilli())); + assertFalse(validationUtils.isValidKeyDate(midnight.plusHours(1).toInstant().toEpochMilli())); + + assertTrue(LocalTime.MIDNIGHT.equals(midnight.toLocalTime())); + } +} \ No newline at end of file From eee3ec875f11540be08af836e5ba2e51e13fc6a8 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Tue, 7 Jul 2020 21:17:10 +0200 Subject: [PATCH 16/27] use assertequals --- .../sdk/ws/util/ValidationUtilsTest.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java index 27088c66..eedaa290 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/util/ValidationUtilsTest.java @@ -1,7 +1,6 @@ package org.dpppt.backend.sdk.ws.util; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import java.time.Duration; import java.time.LocalDate; @@ -15,14 +14,14 @@ public class ValidationUtilsTest { public void testOnlyMidnightIsValid() throws Exception { var validationUtils = new ValidationUtils(16, Duration.ofDays(14), Duration.ofHours(2).toMillis()); var midnight = LocalDate.now(ZoneOffset.UTC).atStartOfDay(ZoneOffset.UTC); - assertTrue(validationUtils.isValidKeyDate(midnight.toInstant().toEpochMilli())); - assertFalse(validationUtils.isValidKeyDate(midnight.minusSeconds(1).toInstant().toEpochMilli())); - assertFalse(validationUtils.isValidKeyDate(midnight.minusMinutes(1).toInstant().toEpochMilli())); - assertFalse(validationUtils.isValidKeyDate(midnight.minusHours(1).toInstant().toEpochMilli())); - assertFalse(validationUtils.isValidKeyDate(midnight.plusSeconds(1).toInstant().toEpochMilli())); - assertFalse(validationUtils.isValidKeyDate(midnight.plusMinutes(1).toInstant().toEpochMilli())); - assertFalse(validationUtils.isValidKeyDate(midnight.plusHours(1).toInstant().toEpochMilli())); + assertEquals(true, validationUtils.isValidKeyDate(midnight.toInstant().toEpochMilli())); + assertEquals(false, validationUtils.isValidKeyDate(midnight.minusSeconds(1).toInstant().toEpochMilli())); + assertEquals(false, validationUtils.isValidKeyDate(midnight.minusMinutes(1).toInstant().toEpochMilli())); + assertEquals(false, validationUtils.isValidKeyDate(midnight.minusHours(1).toInstant().toEpochMilli())); + assertEquals(false, validationUtils.isValidKeyDate(midnight.plusSeconds(1).toInstant().toEpochMilli())); + assertEquals(false, validationUtils.isValidKeyDate(midnight.plusMinutes(1).toInstant().toEpochMilli())); + assertEquals(false, validationUtils.isValidKeyDate(midnight.plusHours(1).toInstant().toEpochMilli())); - assertTrue(LocalTime.MIDNIGHT.equals(midnight.toLocalTime())); + assertEquals(true, LocalTime.MIDNIGHT.equals(midnight.toLocalTime())); } } \ No newline at end of file From d50065fe0891ca6f3ff73954ebc0e86f85540a84 Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Wed, 8 Jul 2020 09:13:16 +0200 Subject: [PATCH 17/27] Some more comments Added comments in some of the interfaces and classes with regard to the used time interval formats --- .../backend/sdk/data/DPPPTDataService.java | 22 ++++++------ .../sdk/data/gaen/DebugGAENDataService.java | 14 ++++---- .../sdk/data/gaen/GAENDataService.java | 24 ++++++------- .../sdk/ws/controller/DPPPTController.java | 1 + .../backend/sdk/ws/util/ValidationUtils.java | 36 +++++++++++++++++-- 5 files changed, 64 insertions(+), 33 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java index 89360054..6f888218 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java @@ -18,7 +18,7 @@ public interface DPPPTDataService { /** - * Upserts the given exposee + * Upserts (Update or Inserts) the given exposee * * @param exposee the exposee to upsert * @param appSource the app name @@ -26,9 +26,9 @@ public interface DPPPTDataService { void upsertExposee(Exposee exposee, String appSource); /** - * Upserts the given exposees (if keys cannot be derived from one master key) + * Upserts (Update or Inserts) the given exposees (if keys cannot be derived from one master key) * - * @param exposeex the list of exposees to upsert + * @param exposees the list of exposees to upsert * @param appSource the app name */ void upsertExposees(List exposees, String appSource); @@ -36,25 +36,25 @@ public interface DPPPTDataService { /** * Returns the maximum id of the stored exposed entries fo the given batch. * - * @param batchReleaseTime - * @param batchLength - * @return + * @param batchReleaseTime in milliseconds since the start of the Unix Epoch, must be a multiple of + * @param batchLength im milliseconds + * @return the maximum id of the stored exposed entries fo the given batch */ int getMaxExposedIdForBatchReleaseTime(long batchReleaseTime, long batchLength); /** * Returns all exposees for the given batch. - * - * @param batchReleaseTime - * @param batchLength - * @return + * + * @param batchReleaseTime in milliseconds since the start of the Unix Epoch, must be a multiple of + * @param batchLength im milliseconds + * @return all exposees for the given batch */ List getSortedExposedForBatchReleaseTime(long batchReleaseTime, long batchLength); /** * deletes entries older than retentionperiod * - * @param retentionPeriod + * @param retentionPeriod duration of retention period for exposed keys */ void cleanDB(Duration retentionPeriod); diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/DebugGAENDataService.java b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/DebugGAENDataService.java index b442e142..4d07dc94 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/DebugGAENDataService.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/DebugGAENDataService.java @@ -18,19 +18,19 @@ public interface DebugGAENDataService { /** - * Upserts the given list of exposed keys in the debug store + * Upserts (Update or Inserts) the given list of exposed keys in the debug store * - * @param device name - * @param key the list of exposed keys to upsert + * @param deviceName name of the device + * @param keys the list of exposed keys to upsert */ void upsertExposees(String deviceName, List keys); /** - * Returns all exposeed keys for the given batch from the debug store. + * Returns all exposed keys for the given batch from the debug store. * - * @param batchReleaseTime - * @param batchLength - * @return + * @param batchReleaseTime in milliseconds since the beginning of the Unix epoch (1970-01-01) + * @param batchLength in milliseconds + * @return all exposed keys for the given batch from the debug store */ Map> getSortedExposedForBatchReleaseTime(Long batchReleaseTime, long batchLength); diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/GAENDataService.java b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/GAENDataService.java index 81ef2695..951c019c 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/GAENDataService.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/gaen/GAENDataService.java @@ -18,36 +18,36 @@ public interface GAENDataService { /** - * Upserts the given list of exposed keys + * Upserts (Update or Inserts) the given list of exposed keys * - * @param key the list of exposed keys to upsert + * @param keys the list of exposed keys to upsert */ void upsertExposees(List keys); /** * Returns the maximum id of the stored exposed entries for the given batch. * - * @param batchReleaseTime - * @param publishedAfter - * @param publishedUntil - * @return + * @param keyDate in milliseconds since Unix epoch (1970-01-01) + * @param publishedAfter in milliseconds since Unix epoch + * @param publishedUntil in milliseconds since Unix epoch + * @return the maximum id of the stored exposed entries for the given batch */ int getMaxExposedIdForKeyDate(Long keyDate, Long publishedAfter, Long publishedUntil); /** * Returns all exposeed keys for the given batch. - * - * @param batchReleaseTime - * @param publishedAfter - * @param publishedUntil - * @return + * + * @param keyDate in milliseconds since Unix epoch (1970-01-01) + * @param publishedAfter in milliseconds since Unix epoch + * @param publishedUntil in milliseconds since Unix epoch + * @return all exposeed keys for the given batch */ List getSortedExposedForKeyDate(Long keyDate, Long publishedAfter, Long publishedUntil); /** * deletes entries older than retentionperiod * - * @param retentionPeriod + * @param retentionPeriod in milliseconds */ void cleanDB(Duration retentionPeriod); } diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java index 1605bfce..b06d8e00 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java @@ -62,6 +62,7 @@ public class DPPPTController { private final int exposedListCacheControl; private final ValidateRequest validateRequest; private final ValidationUtils validationUtils; + // time in milliseconds that exposed keys are hidden before being served, in order to prevent timing attacks private final long batchLength; private final long requestTime; diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java index 925444d4..3383859f 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java @@ -17,17 +17,33 @@ import java.time.ZoneOffset; import java.util.Base64; +/** + * Offers a set of methods to validate the incoming requests from the mobile devices. + */ public class ValidationUtils { private final int KEY_LENGTH_BYTES; private final Duration retentionPeriod; private final Long batchLength; + /** + * Initialize the validator with the current parameters in use. + * + * @param keyLengthBytes how long the exposed keys are, in bytes + * @param retentionPeriod period during which the exposed keys are stored, before they are deleted + * @param batchLength time in milliseconds that exposed keys are hidden before being served, in order to prevent timing attacks + */ public ValidationUtils(int keyLengthBytes, Duration retentionPeriod, Long batchLength) { this.KEY_LENGTH_BYTES = keyLengthBytes; this.retentionPeriod = retentionPeriod; this.batchLength = batchLength; } + /** + * Check the validty of a base64 value + * + * @param value representation of a base64 value + * @return if _value_ is a valid representation + */ public boolean isValidBase64Key(String value) { try { byte[] key = Base64.getDecoder().decode(value); @@ -40,6 +56,12 @@ public boolean isValidBase64Key(String value) { } } + /** + * Check if the given date is in the range of [now - retentionPeriod ... now], exclusive + * + * @param timestamp to verify + * @return if the date is in the range + */ public boolean isDateInRange(OffsetDateTime timestamp) { if (timestamp.isAfter(Instant.now().atOffset(ZoneOffset.UTC))) { return false; @@ -51,15 +73,23 @@ public boolean isDateInRange(OffsetDateTime timestamp) { } /** - * Check if the given timestamp is a valid key date: Must be midnight utc. + * Check if the given timestamp is a valid key date: Must be midnight UTC. * - * @param keyDate - * @return + * @param keyDate in milliseconds since Unix epoch (1970-01-01) + * @return if keyDate represents midnight UTC */ public boolean isValidKeyDate(long keyDate) { return LocalDateTime.ofInstant(Instant.ofEpochMilli(keyDate), ZoneOffset.UTC).toLocalTime().equals(LocalTime.MIDNIGHT); } + /** + * Check if the given batchReleaseTime is the beginning of a batch, and if it is between + * [now - retentionPeriod ... now], exclusive. + * + * @param batchReleaseTime in milliseconds since Unix epoch (1970-01-01) + * @return if batchReleaseTime is in range + * @throws BadBatchReleaseTimeException if batchReleaseTime is not on a batch boundary + */ public boolean isValidBatchReleaseTime(long batchReleaseTime) throws BadBatchReleaseTimeException { if (batchReleaseTime % batchLength != 0) { throw new BadBatchReleaseTimeException(); From acd2afed77c1343e311c0a38a164b31be15add9c Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Wed, 8 Jul 2020 09:33:10 +0200 Subject: [PATCH 18/27] Simplifying validation Removing redundant checks to avoid errors. --- .../dpppt/backend/sdk/ws/util/ValidationUtils.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java index 925444d4..fe9eca1a 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java @@ -44,10 +44,7 @@ public boolean isDateInRange(OffsetDateTime timestamp) { if (timestamp.isAfter(Instant.now().atOffset(ZoneOffset.UTC))) { return false; } - if (timestamp.isBefore(Instant.now().atOffset(ZoneOffset.UTC).minus(retentionPeriod))) { - return false; - } - return true; + return !timestamp.isBefore(Instant.now().atOffset(ZoneOffset.UTC).minus(retentionPeriod)); } /** @@ -64,14 +61,7 @@ public boolean isValidBatchReleaseTime(long batchReleaseTime) throws BadBatchRel if (batchReleaseTime % batchLength != 0) { throw new BadBatchReleaseTimeException(); } - if (batchReleaseTime > OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()) { - return false; - } - if (batchReleaseTime < OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).minus(retentionPeriod) - .toInstant().toEpochMilli()) { - return false; - } - return true; + return this.isDateInRange(OffsetDateTime.ofInstant(Instant.ofEpochMilli(batchReleaseTime), ZoneOffset.UTC)); } public class BadBatchReleaseTimeException extends Exception { From c566595b67c7674dff0a05cd881939d22c5d9b6e Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Fri, 3 Jul 2020 15:09:58 +0200 Subject: [PATCH 19/27] Adding documentation to the DPPPTController API This PR re-adds the API Documentation similar to how it was before the commit 04bd32afe8df55ae0e21d3f804ce6f04c29507ce More specifically, this does: 1. add a dependency to `ch.ubique.openapi.docannotations` 2. add `@Documentation` annotations to `DPPPTController.java` 3. modivy the `updatedoc` target in the `Makefile` to correctly re-create the documentation only --- Makefile | 2 + documentation/yaml/sdk.yaml | 203 +++++++++++------- .../dpppt-backend-sdk-ws/pom.xml | 16 +- .../sdk/ws/controller/DPPPTController.java | 59 ++++- 4 files changed, 193 insertions(+), 87 deletions(-) diff --git a/Makefile b/Makefile index 44ce7951..f55c3b96 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,9 @@ doc: updatedoc swagger la la2 la3 updateproject: mvn -f dpppt-backend-sdk/pom.xml install + updatedoc: + mvn -f dpppt-backend-sdk/pom.xml install -Dmaven.test.skip=true mvn springboot-swagger-3:springboot-swagger-3 -f dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml cp dpppt-backend-sdk/dpppt-backend-sdk-ws/generated/swagger/swagger.yaml documentation/yaml/sdk.yaml diff --git a/documentation/yaml/sdk.yaml b/documentation/yaml/sdk.yaml index bbc9753c..614a6646 100644 --- a/documentation/yaml/sdk.yaml +++ b/documentation/yaml/sdk.yaml @@ -10,35 +10,43 @@ paths: /v1/exposed: post: summary: addExposee - description: addExposee + description: Send exposed key to server responses: '200': - description: '' + description: The exposed keys have been stored in the database content: application/json: schema: type: string + '400': + description: Invalid base64 encoding in expose request + '403': + description: Authentication failed requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/org.dpppt.backend.sdk.model.ExposeeRequest' - description: N/A + description: The ExposeeRequest contains the SecretKey from the guessed infection + date, the infection date itself, and some authentication data to verify + the test result parameters: - name: User-Agent in: header - description: '' + description: App Identifier (PackageName/BundleIdentifier) + App-Version + + OS (Android/iOS) + OS-Version + example: ch.ubique.android.starsdk;1.0;iOS;13.3 required: true schema: type: string /v1/: get: summary: hello - description: hello + description: Hello return responses: '200': - description: '' + description: server live content: application/json: schema: @@ -46,43 +54,56 @@ paths: /v1/exposedlist: post: summary: addExposee - description: addExposee + description: Send a list of exposed keys to server responses: '200': - description: '' + description: The exposed keys have been stored in the database content: application/json: schema: type: string + '400': + description: Invalid base64 encoding in exposee request + '403': + description: Authentication failed requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/org.dpppt.backend.sdk.model.ExposeeRequestList' - description: N/A + description: The ExposeeRequest contains the SecretKey from the guessed infection + date, the infection date itself, and some authentication data to verify + the test result parameters: - name: User-Agent in: header - description: '' + description: App Identifier (PackageName/BundleIdentifier) + App-Version + + OS (Android/iOS) + OS-Version + example: ch.ubique.android.starsdk;1.0;iOS;13.3 required: true schema: type: string /v1/exposedjson/{batchReleaseTime}: get: summary: getExposedByDayDate - description: getExposedByDayDate + description: Query list of exposed keys from a specific batch release time responses: '200': - description: '' + description: Returns ExposedOverview in json format, which includes all + exposed keys which were published on _batchReleaseTime_ content: application/json: schema: $ref: '#/components/schemas/org.dpppt.backend.sdk.model.ExposedOverview' + '404': + description: Couldn't find _batchReleaseTime_ parameters: - name: batchReleaseTime in: path - description: '' + description: The batch release date of the exposed keys in milliseconds since + Unix Epoch (1970-01-01), must be a multiple of 2 * 60 * 60 * 1000 + example: '1593043200000' required: true schema: type: integer @@ -90,18 +111,23 @@ paths: /v1/exposed/{batchReleaseTime}: get: summary: getExposedByBatch - description: getExposedByBatch + description: Query list of exposed keys from a specific batch release time responses: '200': - description: '' + description: Returns ExposedOverview in protobuf format, which includes + all exposed keys which were published on _batchReleaseTime_ content: application/x-protobuf: schema: $ref: '#/components/schemas/org.dpppt.backend.sdk.model.proto.Exposed.ProtoExposedList' + '404': + description: Couldn't find _batchReleaseTime_ parameters: - name: batchReleaseTime in: path - description: '' + description: The batch release date of the exposed keys in milliseconds since + Unix Epoch (1970-01-01), must be a multiple of 2 * 60 * 60 * 1000 + example: '1593043200000' required: true schema: type: integer @@ -109,10 +135,12 @@ paths: /v1/buckets/{dayDateStr}: get: summary: getListOfBuckets - description: getListOfBuckets + description: Query number of available buckets in a given day, starting from + midnight UTC responses: '200': - description: '' + description: Returns BucketList in json format, indicating all available + buckets since _dayDateStr_ content: application/json: schema: @@ -120,7 +148,9 @@ paths: parameters: - name: dayDateStr in: path - description: '' + description: The date starting when to return the available buckets, in ISO8601 + date format + example: '2019-01-31' required: true schema: type: string @@ -174,47 +204,77 @@ paths: required: true schema: type: string - /v1/gaen/exposedios/{batchReleaseTime}: + /v1/gaen/exposed/{keyDate}: get: - summary: getExposedKeysIos - description: getExposedKeysIos + summary: getExposedKeys + description: getExposedKeys responses: '200': description: '' content: - application/x-protobuf: + application/zip: schema: - $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.proto.FileProto.File' + type: string + format: binary parameters: - - name: batchReleaseTime + - name: keyDate in: path description: '' required: true schema: type: integer format: long - /v1/gaen/exposed/{batchReleaseTime}: + - name: publishedafter + in: query + description: '' + required: false + schema: + type: integer + format: long + /v1/gaen/exposedjson/{keyDate}: get: - summary: getExposedKeys - description: getExposedKeys + summary: getExposedKeysAsJson + description: getExposedKeysAsJson responses: '200': description: '' content: - application/zip: + application/json: schema: - type: string - format: binary + $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.GaenExposedJson' parameters: - - name: batchReleaseTime + - name: keyDate in: path description: '' required: true schema: type: integer format: long - /v1/gaen/exposedjson/{batchReleaseTime}: { - } + - name: publishedafter + in: query + description: '' + required: false + schema: + type: integer + format: long + /v1/gaen/buckets/{dayDateStr}: + get: + summary: getBuckets + description: getBuckets + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.DayBuckets' + parameters: + - name: dayDateStr + in: path + description: '' + required: true + schema: + type: string components: schemas: org.dpppt.backend.sdk.model.BucketList: @@ -287,6 +347,29 @@ components: $ref: '#/components/schemas/org.dpppt.backend.sdk.model.ExposedKey' fake: type: integer + org.dpppt.backend.sdk.model.gaen.DayBuckets: + type: object + properties: + dayTimestamp: + type: integer + format: long + day: + type: string + relativeUrls: + type: array + items: + type: string + org.dpppt.backend.sdk.model.gaen.GaenExposedJson: + type: object + required: + - gaenKeys + properties: + gaenKeys: + type: array + items: + $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.GaenKey' + header: + $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.Header' org.dpppt.backend.sdk.model.gaen.GaenKey: type: object required: @@ -324,58 +407,20 @@ components: properties: delayedKey: $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.GaenKey' - fake: - type: integer - org.dpppt.backend.sdk.model.gaen.proto.FileProto.File: + org.dpppt.backend.sdk.model.gaen.Header: type: object properties: - bitField0_: - type: integer - header_: - $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.proto.FileProto.Header' - key_: - type: array - items: - $ref: '#/components/schemas/org.dpppt.backend.sdk.model.gaen.proto.FileProto.Key' - memoizedSize: - type: integer - memoizedHashCode: - type: integer - org.dpppt.backend.sdk.model.gaen.proto.FileProto.Header: - type: object - properties: - bitField0_: - type: integer - startTimestamp_: + startTimestamp: type: integer format: long - endTimestamp_: + endTimestamp: type: integer format: long - region_: - type: object - batchNum_: - type: integer - batchSize_: - type: integer - memoizedSize: - type: integer - memoizedHashCode: - type: integer - org.dpppt.backend.sdk.model.gaen.proto.FileProto.Key: - type: object - properties: - bitField0_: - type: integer - rollingStartNumber_: - type: integer - rollingPeriod_: - type: integer - transmissionRiskLevel_: - type: integer - memoizedSize: + region: + type: string + batchNum: type: integer - memoizedHashCode: + batchSize: type: integer org.dpppt.backend.sdk.model.proto.Exposed.ProtoExposedList: type: object diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml b/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml index 1c03cd8e..61fcc3b9 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml @@ -36,6 +36,12 @@ spring-boot-starter-oauth2-resource-server + + ch.ubique.openapi + doc-annotations + 0.0.5 + + org.springframework.security spring-security-test @@ -52,7 +58,7 @@ 0.11.1 pom - + io.jsonwebtoken jjwt-jackson @@ -175,6 +181,7 @@ + github @@ -185,4 +192,11 @@ + + + github + github + https://maven.pkg.github.com/Ubique-OSS/springboot-swagger3-annotations + + diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java index 1605bfce..97467bcf 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/DPPPTController.java @@ -51,6 +51,8 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.request.WebRequest; +import ch.ubique.openapi.docannotations.Documentation; + import com.google.protobuf.ByteString; @Controller @@ -80,14 +82,26 @@ public DPPPTController(DPPPTDataService dataService, String appSource, @CrossOrigin(origins = { "https://editor.swagger.io" }) @GetMapping(value = "") + @Documentation(description = "Hello return", responses = {"200=>server live"}) public @ResponseBody ResponseEntity hello() { return ResponseEntity.ok().header("X-HELLO", "dp3t").body("Hello from DP3T WS"); } @CrossOrigin(origins = { "https://editor.swagger.io" }) @PostMapping(value = "/exposed") - public @ResponseBody ResponseEntity addExposee(@Valid @RequestBody ExposeeRequest exposeeRequest, - @RequestHeader(value = "User-Agent", required = true) String userAgent, + @Documentation( + description = "Send exposed key to server", + responses = { + "200=>The exposed keys have been stored in the database", + "400=>Invalid base64 encoding in expose request", + "403=>Authentication failed" + }) + public @ResponseBody ResponseEntity addExposee(@Valid @RequestBody + @Documentation(description = "The ExposeeRequest contains the SecretKey from the guessed infection date, the infection date itself, and some authentication data to verify the test result") + ExposeeRequest exposeeRequest, + @RequestHeader(value = "User-Agent", required = true) + @Documentation(description = "App Identifier (PackageName/BundleIdentifier) + App-Version + OS (Android/iOS) + OS-Version", example = "ch.ubique.android.starsdk;1.0;iOS;13.3") + String userAgent, @AuthenticationPrincipal Object principal) throws InvalidDateException { long now = System.currentTimeMillis(); if (!this.validateRequest.isValid(principal)) { @@ -118,8 +132,17 @@ public DPPPTController(DPPPTDataService dataService, String appSource, @CrossOrigin(origins = { "https://editor.swagger.io" }) @PostMapping(value = "/exposedlist") - public @ResponseBody ResponseEntity addExposee(@Valid @RequestBody ExposeeRequestList exposeeRequests, - @RequestHeader(value = "User-Agent", required = true) String userAgent, + @Documentation(description = "Send a list of exposed keys to server", + responses = { + "200=>The exposed keys have been stored in the database", + "400=>Invalid base64 encoding in exposee request", + "403=>Authentication failed"}) + public @ResponseBody ResponseEntity addExposee(@Valid @RequestBody + @Documentation(description = "The ExposeeRequest contains the SecretKey from the guessed infection date, the infection date itself, and some authentication data to verify the test result") + ExposeeRequestList exposeeRequests, + @RequestHeader(value = "User-Agent", required = true) + @Documentation(description = "App Identifier (PackageName/BundleIdentifier) + App-Version + OS (Android/iOS) + OS-Version", example = "ch.ubique.android.starsdk;1.0;iOS;13.3") + String userAgent, @AuthenticationPrincipal Object principal) throws InvalidDateException { long now = System.currentTimeMillis(); if (!this.validateRequest.isValid(principal)) { @@ -156,7 +179,15 @@ public DPPPTController(DPPPTDataService dataService, String appSource, @CrossOrigin(origins = { "https://editor.swagger.io" }) @GetMapping(value = "/exposedjson/{batchReleaseTime}", produces = "application/json") - public @ResponseBody ResponseEntity getExposedByDayDate(@PathVariable long batchReleaseTime, + @Documentation(description = "Query list of exposed keys from a specific batch release time", + responses = { + "200=>Returns ExposedOverview in json format, which includes all exposed keys which were published on _batchReleaseTime_", + "404=>Couldn't find _batchReleaseTime_" + }) + public @ResponseBody ResponseEntity getExposedByDayDate(@PathVariable + @Documentation(description = "The batch release date of the exposed keys in milliseconds since Unix Epoch (1970-01-01), must be a multiple of 2 * 60 * 60 * 1000", + example = "1593043200000") + long batchReleaseTime, WebRequest request) throws BadBatchReleaseTimeException{ if(!validationUtils.isValidBatchReleaseTime(batchReleaseTime)) { return ResponseEntity.notFound().build(); @@ -171,7 +202,15 @@ public DPPPTController(DPPPTDataService dataService, String appSource, @CrossOrigin(origins = { "https://editor.swagger.io" }) @GetMapping(value = "/exposed/{batchReleaseTime}", produces = "application/x-protobuf") - public @ResponseBody ResponseEntity getExposedByBatch(@PathVariable long batchReleaseTime, + @Documentation(description = "Query list of exposed keys from a specific batch release time", + responses = { + "200=>Returns ExposedOverview in protobuf format, which includes all exposed keys which were published on _batchReleaseTime_", + "404=>Couldn't find _batchReleaseTime_" + }) + public @ResponseBody ResponseEntity getExposedByBatch(@PathVariable + @Documentation(description = "The batch release date of the exposed keys in milliseconds since Unix Epoch (1970-01-01), must be a multiple of 2 * 60 * 60 * 1000", + example = "1593043200000") + long batchReleaseTime, WebRequest request) throws BadBatchReleaseTimeException { if(!validationUtils.isValidBatchReleaseTime(batchReleaseTime)) { return ResponseEntity.notFound().build(); @@ -194,7 +233,13 @@ public DPPPTController(DPPPTDataService dataService, String appSource, @CrossOrigin(origins = { "https://editor.swagger.io" }) @GetMapping(value = "/buckets/{dayDateStr}", produces = "application/json") - public @ResponseBody ResponseEntity getListOfBuckets(@PathVariable String dayDateStr) { + @Documentation(description = "Query number of available buckets in a given day, starting from midnight UTC", + responses = { + "200=>Returns BucketList in json format, indicating all available buckets since _dayDateStr_" + }) + public @ResponseBody ResponseEntity getListOfBuckets(@PathVariable + @Documentation(description = "The date starting when to return the available buckets, in ISO8601 date format", example = "2019-01-31") + String dayDateStr) { OffsetDateTime day = LocalDate.parse(dayDateStr).atStartOfDay().atOffset(ZoneOffset.UTC); OffsetDateTime currentBucket = day; OffsetDateTime now = OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC); From 4d02d1d87e997f803d876cabe78e1e350babfd96 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Wed, 8 Jul 2020 15:56:14 +0200 Subject: [PATCH 20/27] use public bintray repo --- .../dpppt-backend-sdk-ws/pom.xml | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml b/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml index 61fcc3b9..c8594008 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/pom.xml @@ -39,7 +39,7 @@ ch.ubique.openapi doc-annotations - 0.0.5 + 1.0.1 @@ -155,7 +155,7 @@ ch.ubique.openapi springboot-swagger-3 - 1.2.4 + 1.2.8 1.0-gapple @@ -183,20 +183,24 @@ - - github - GitHub OWNER Apache Maven Packages - https://maven.pkg.github.com/Ubique-OSS/springboot-swagger3 - true - true - + + + false + + ubique-oss-springboot-swagger3 + bintray + https://dl.bintray.com/ubique-oss/springboot-swagger3 + - github - github - https://maven.pkg.github.com/Ubique-OSS/springboot-swagger3-annotations - + + false + + ubique-oss-springboot-swagger3 + bintray + https://dl.bintray.com/ubique-oss/springboot-swagger3 + From 57a75d4eb2649d057f51874ed601c371ea0de668 Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Wed, 8 Jul 2020 17:20:50 +0200 Subject: [PATCH 21/27] delayedKeyDate out of bounds check The current out of bounds check in the endpoint /v1/gaen/exposed is faulty. This PR checks that delayedKeyDate is correctly verified. --- .../sdk/ws/controller/GaenController.java | 2 +- .../sdk/ws/controller/GaenControllerTest.java | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java index 40f9f6eb..868624d7 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java @@ -145,7 +145,7 @@ public GaenController(GAENDataService dataService, FakeKeyService fakeKeyService ZoneOffset.UTC); var nowDay = LocalDate.now(ZoneOffset.UTC); - if (!delayedKeyDate.isAfter(nowDay.minusDays(1)) && delayedKeyDate.isBefore(nowDay.plusDays(1))) { + if (delayedKeyDate.isBefore(nowDay.minusDays(1)) || delayedKeyDate.isAfter(nowDay.plusDays(1))) { return () -> { return ResponseEntity.badRequest().body("delayedKeyDate date must be between yesterday and tomorrow"); }; diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java index c2cccb08..861ce123 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java @@ -735,6 +735,47 @@ public void uploadKeysAndUploadKeyNextDayWithNegativeRollingPeriodFails() throws mockMvc.perform(asyncDispatch(responseAsync)).andExpect(status().isBadRequest()); } + @Test + public void delayedKeyDateBounaryCheck() throws Exception { + GaenRequest exposeeRequest = new GaenRequest(); + List keys = new ArrayList<>(); + for (int i = 0; i < 14; i++) { + var tmpKey = new GaenKey(); + tmpKey.setRollingStartNumber((int) Duration.ofMillis(Instant.now().minus(Duration.ofDays(1)).toEpochMilli()) + .dividedBy(Duration.ofMinutes(10))); + tmpKey.setKeyData(Base64.getEncoder().encodeToString("testKey32Bytes--".getBytes("UTF-8"))); + tmpKey.setRollingPeriod(144); + tmpKey.setFake(0); + tmpKey.setTransmissionRiskLevel(0); + keys.add(tmpKey); + } + Map tests = Map.of(-2, false, + -1, true, + 0, true, + 1, true, + 2, false); + tests.forEach((offset, pass) -> { + logger.info("Testing offset {} which should pass {}", offset, pass); + try { + var delayedKeyDateSent = (int) Duration.ofSeconds(LocalDate.now().atStartOfDay(ZoneOffset.UTC).plusDays(offset) + .toEpochSecond()).dividedBy(Duration.ofMinutes(10)); + exposeeRequest.setDelayedKeyDate(delayedKeyDateSent); + exposeeRequest.setGaenKeys(keys); + String token = createToken(OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).plusMinutes(5)); + MvcResult responseAsync = mockMvc.perform(post("/v1/gaen/exposed") + .contentType(MediaType.APPLICATION_JSON).header("Authorization", "Bearer " + token) + .header("User-Agent", "MockMVC").content(json(exposeeRequest))).andExpect(request().asyncStarted()).andReturn(); + if (pass) { + mockMvc.perform(asyncDispatch(responseAsync)).andExpect(status().is(200)).andReturn().getResponse(); + } else { + mockMvc.perform(asyncDispatch(responseAsync)).andExpect(status().is(400)).andReturn().getResponse(); + } + } catch(Exception e){ + logger.error(e.toString()); + } + }); + } + @Test public void testDebugController() throws Exception { LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); From ef51a83fcbae6cab05f47cd6889c272803ffa08f Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Wed, 8 Jul 2020 17:38:59 +0200 Subject: [PATCH 22/27] ubamrein's comments --- .../java/org/dpppt/backend/sdk/data/DPPPTDataService.java | 4 ++-- .../java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java index 6f888218..3b21a8c7 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-data/src/main/java/org/dpppt/backend/sdk/data/DPPPTDataService.java @@ -37,7 +37,7 @@ public interface DPPPTDataService { * Returns the maximum id of the stored exposed entries fo the given batch. * * @param batchReleaseTime in milliseconds since the start of the Unix Epoch, must be a multiple of - * @param batchLength im milliseconds + * @param batchLength in milliseconds * @return the maximum id of the stored exposed entries fo the given batch */ int getMaxExposedIdForBatchReleaseTime(long batchReleaseTime, long batchLength); @@ -46,7 +46,7 @@ public interface DPPPTDataService { * Returns all exposees for the given batch. * * @param batchReleaseTime in milliseconds since the start of the Unix Epoch, must be a multiple of - * @param batchLength im milliseconds + * @param batchLength in milliseconds * @return all exposees for the given batch */ List getSortedExposedForBatchReleaseTime(long batchReleaseTime, long batchLength); diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java index 3383859f..8b286483 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java @@ -57,7 +57,7 @@ public boolean isValidBase64Key(String value) { } /** - * Check if the given date is in the range of [now - retentionPeriod ... now], exclusive + * Check if the given date is in the range of [now - retentionPeriod ... now], inclusive * * @param timestamp to verify * @return if the date is in the range @@ -84,7 +84,7 @@ public boolean isValidKeyDate(long keyDate) { /** * Check if the given batchReleaseTime is the beginning of a batch, and if it is between - * [now - retentionPeriod ... now], exclusive. + * [now - retentionPeriod ... now], inclusive. * * @param batchReleaseTime in milliseconds since Unix epoch (1970-01-01) * @return if batchReleaseTime is in range From 09f727cdc9745a9626be460e7b51bb8354551c60 Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Thu, 9 Jul 2020 08:16:18 +0200 Subject: [PATCH 23/27] check max validity of token --- .../java/org/dpppt/backend/sdk/ws/security/JWTValidator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java index d6fa64e3..517f5e04 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java @@ -11,7 +11,6 @@ package org.dpppt.backend.sdk.ws.security; import java.time.Duration; -import java.time.Instant; import org.dpppt.backend.sdk.data.RedeemDataService; import org.springframework.security.oauth2.core.OAuth2Error; @@ -40,7 +39,8 @@ public OAuth2TokenValidatorResult validate(Jwt token) { //it is a fakte token, but we still assume it is valid return OAuth2TokenValidatorResult.success(); } - if (token.getExpiresAt() == null || Instant.now().plus(maxJwtValidity).isBefore(token.getExpiresAt())) { + //make sure the token has an expiration date AND is not valid for more than 3 days + if (token.getExpiresAt() == null || token.getIssuedAt().plus(maxJwtValidity).isBefore(token.getExpiresAt())) { return OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST)); } if(token.containsClaim(UUID_CLAIM) && this.dataService.checkAndInsertPublishUUID(token.getClaim(UUID_CLAIM))) { From 22c2d6ec52123f21d25635680c4f1991061050fb Mon Sep 17 00:00:00 2001 From: Patrick Amrein Date: Thu, 9 Jul 2020 08:27:55 +0200 Subject: [PATCH 24/27] add tests and fix typo in comment --- .../backend/sdk/ws/security/JWTValidator.java | 2 +- .../sdk/ws/controller/GaenControllerTest.java | 30 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java index 517f5e04..02f0e9ba 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/security/JWTValidator.java @@ -39,7 +39,7 @@ public OAuth2TokenValidatorResult validate(Jwt token) { //it is a fakte token, but we still assume it is valid return OAuth2TokenValidatorResult.success(); } - //make sure the token has an expiration date AND is not valid for more than 3 days + //make sure the token has an expiration date AND is not valid for more than maxJwtValidity if (token.getExpiresAt() == null || token.getIssuedAt().plus(maxJwtValidity).isBefore(token.getExpiresAt())) { return OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST)); } diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java index 861ce123..087f19ae 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java @@ -48,6 +48,7 @@ import org.dpppt.backend.sdk.model.gaen.GaenKey; import org.dpppt.backend.sdk.model.gaen.GaenRequest; import org.dpppt.backend.sdk.model.gaen.GaenSecondDay; +import org.dpppt.backend.sdk.model.gaen.GaenUnit; import org.dpppt.backend.sdk.model.gaen.proto.TemporaryExposureKeyFormat; import org.dpppt.backend.sdk.ws.security.KeyVault; import org.dpppt.backend.sdk.ws.security.signature.ProtoSignature; @@ -55,7 +56,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; @@ -736,7 +736,7 @@ public void uploadKeysAndUploadKeyNextDayWithNegativeRollingPeriodFails() throws } @Test - public void delayedKeyDateBounaryCheck() throws Exception { + public void delayedKeyDateBoundaryCheck() throws Exception { GaenRequest exposeeRequest = new GaenRequest(); List keys = new ArrayList<>(); for (int i = 0; i < 14; i++) { @@ -776,6 +776,32 @@ public void delayedKeyDateBounaryCheck() throws Exception { }); } + @Test + public void testTokenValiditySurpassesMaxJwtValidity() throws Exception{ + GaenRequest exposeeRequest = new GaenRequest(); + List keys = new ArrayList<>(); + for (int i = 0; i < 14; i++) { + var tmpKey = new GaenKey(); + tmpKey.setRollingStartNumber((int) Duration.ofMillis(Instant.now().minus(Duration.ofDays(1)).toEpochMilli()) + .dividedBy(Duration.ofMinutes(10))); + tmpKey.setKeyData(Base64.getEncoder().encodeToString("testKey32Bytes--".getBytes("UTF-8"))); + tmpKey.setRollingPeriod(144); + tmpKey.setFake(0); + tmpKey.setTransmissionRiskLevel(0); + keys.add(tmpKey); + } + exposeeRequest.setGaenKeys(keys); + var delayedKeyDateSent = (int) Duration.ofSeconds(LocalDate.now().atStartOfDay(ZoneOffset.UTC).plusDays(1) + .toEpochSecond()).dividedBy(GaenUnit.TenMinutes.getDuration()); + exposeeRequest.setDelayedKeyDate(delayedKeyDateSent); + int maxJWTValidityInMinutes = 60; + String token = createToken(OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).plusMinutes(maxJWTValidityInMinutes + 1)); + + mockMvc.perform(post("/v1/gaen/exposed") + .contentType(MediaType.APPLICATION_JSON).header("Authorization", "Bearer " + token) + .header("User-Agent", "MockMVC").content(json(exposeeRequest))).andExpect(status().is(401)); +} + @Test public void testDebugController() throws Exception { LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); From 0398acc38f89bb5db29b2e43833e7c72e27bef28 Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Wed, 8 Jul 2020 18:29:41 +0200 Subject: [PATCH 25/27] ubamrein's comments --- .../org/dpppt/backend/sdk/ws/util/ValidationUtils.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java index fe9eca1a..f911c4f6 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/util/ValidationUtils.java @@ -41,10 +41,11 @@ public boolean isValidBase64Key(String value) { } public boolean isDateInRange(OffsetDateTime timestamp) { - if (timestamp.isAfter(Instant.now().atOffset(ZoneOffset.UTC))) { - return false; - } - return !timestamp.isBefore(Instant.now().atOffset(ZoneOffset.UTC).minus(retentionPeriod)); + OffsetDateTime now = Instant.now().atOffset(ZoneOffset.UTC); + OffsetDateTime retention = now.minus(retentionPeriod); + // This should use timestamp.isAfterOrEqual(retention), but this method does not exist. + // Because _now_ has a resolution of 1 millisecond, this precision is acceptable. + return timestamp.isAfter(retention) && timestamp.isBefore(now); } /** From 532171b45c8e3c2aaf445b77ca2b5bc78640bb7a Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Thu, 9 Jul 2020 09:58:04 +0200 Subject: [PATCH 26/27] Don't catch Exceptions in the test --- .../sdk/ws/controller/GaenControllerTest.java | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java index 087f19ae..dc67aeae 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/test/java/org/dpppt/backend/sdk/ws/controller/GaenControllerTest.java @@ -754,26 +754,24 @@ public void delayedKeyDateBoundaryCheck() throws Exception { 0, true, 1, true, 2, false); - tests.forEach((offset, pass) -> { + for (Map.Entry t : tests.entrySet()) { + Integer offset = t.getKey(); + Boolean pass = t.getValue(); logger.info("Testing offset {} which should pass {}", offset, pass); - try { - var delayedKeyDateSent = (int) Duration.ofSeconds(LocalDate.now().atStartOfDay(ZoneOffset.UTC).plusDays(offset) - .toEpochSecond()).dividedBy(Duration.ofMinutes(10)); - exposeeRequest.setDelayedKeyDate(delayedKeyDateSent); - exposeeRequest.setGaenKeys(keys); - String token = createToken(OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).plusMinutes(5)); - MvcResult responseAsync = mockMvc.perform(post("/v1/gaen/exposed") - .contentType(MediaType.APPLICATION_JSON).header("Authorization", "Bearer " + token) - .header("User-Agent", "MockMVC").content(json(exposeeRequest))).andExpect(request().asyncStarted()).andReturn(); - if (pass) { - mockMvc.perform(asyncDispatch(responseAsync)).andExpect(status().is(200)).andReturn().getResponse(); - } else { - mockMvc.perform(asyncDispatch(responseAsync)).andExpect(status().is(400)).andReturn().getResponse(); - } - } catch(Exception e){ - logger.error(e.toString()); + var delayedKeyDateSent = (int) Duration.ofSeconds(LocalDate.now().atStartOfDay(ZoneOffset.UTC).plusDays(offset) + .toEpochSecond()).dividedBy(Duration.ofMinutes(10)); + exposeeRequest.setDelayedKeyDate(delayedKeyDateSent); + exposeeRequest.setGaenKeys(keys); + String token = createToken(OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).plusMinutes(5)); + MvcResult responseAsync = mockMvc.perform(post("/v1/gaen/exposed") + .contentType(MediaType.APPLICATION_JSON).header("Authorization", "Bearer " + token) + .header("User-Agent", "MockMVC").content(json(exposeeRequest))).andExpect(request().asyncStarted()).andReturn(); + if (pass) { + mockMvc.perform(asyncDispatch(responseAsync)).andExpect(status().is(200)).andReturn().getResponse(); + } else { + mockMvc.perform(asyncDispatch(responseAsync)).andExpect(status().is(400)).andReturn().getResponse(); } - }); + } } @Test From a4c0f13e174805b6561ff869a212bee24ad91421 Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Thu, 9 Jul 2020 11:31:58 +0200 Subject: [PATCH 27/27] Following some of IntelliJ's suggestions This PR simply follows some of IntelliJ's suggestions. It also removes two classes that are never used: dp3t-sdk-backend/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/{GaenExposedJSON,Header}.java --- .../backend/sdk/model/gaen/DayBuckets.java | 7 +-- .../sdk/model/gaen/GaenExposedJson.java | 34 ----------- .../dpppt/backend/sdk/model/gaen/GaenKey.java | 12 ++-- .../backend/sdk/model/gaen/GaenRequest.java | 4 +- .../backend/sdk/model/gaen/GaenSecondDay.java | 1 - .../backend/sdk/model/gaen/GaenUnit.java | 3 +- .../dpppt/backend/sdk/model/gaen/Header.java | 56 ------------------- .../sdk/ws/controller/GaenController.java | 30 +++------- 8 files changed, 18 insertions(+), 129 deletions(-) delete mode 100644 dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenExposedJson.java delete mode 100644 dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/Header.java diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/DayBuckets.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/DayBuckets.java index 312267f9..5b45fbe7 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/DayBuckets.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/DayBuckets.java @@ -4,10 +4,9 @@ public class DayBuckets { - Long dayTimestamp; - String day; - List relativeUrls; - + private Long dayTimestamp; + private String day; + private List relativeUrls; public String getDay() { return this.day; diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenExposedJson.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenExposedJson.java deleted file mode 100644 index dcfcc801..00000000 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenExposedJson.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.dpppt.backend.sdk.model.gaen; - -import java.util.List; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -public class GaenExposedJson { - @NotNull - @NotEmpty - List gaenKeys; - Header header; - - - public List getGaenKeys() { - return this.gaenKeys; - } - - public Header getHeader() { - return this.header; - } - - - public GaenExposedJson gaenKeys(List gaenKeys) { - this.gaenKeys = gaenKeys; - return this; - } - - public GaenExposedJson header(Header header) { - this.header = header; - return this; - } - -} \ No newline at end of file diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenKey.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenKey.java index 49a1cd88..d64b8b46 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenKey.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenKey.java @@ -8,20 +8,18 @@ public class GaenKey { @NotNull @Size(min = 24, max = 24) - String keyData; + private String keyData; @NotNull - Integer rollingStartNumber; + private Integer rollingStartNumber; @NotNull - Integer rollingPeriod; + private Integer rollingPeriod; @NotNull - Integer transmissionRiskLevel; - - Integer fake = 0; - + private Integer transmissionRiskLevel; + private Integer fake = 0; public GaenKey() { } diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenRequest.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenRequest.java index af224b54..d642f78b 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenRequest.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenRequest.java @@ -12,10 +12,10 @@ public class GaenRequest { @NotEmpty @Valid @Size(min = 14, max = 14) - List gaenKeys; + private List gaenKeys; @NotNull - Integer delayedKeyDate; + private Integer delayedKeyDate; public List getGaenKeys() { return this.gaenKeys; diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java index 59e98756..7d85aa05 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenSecondDay.java @@ -8,7 +8,6 @@ public class GaenSecondDay { @Valid private GaenKey delayedKey; - public GaenKey getDelayedKey() { return this.delayedKey; } diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenUnit.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenUnit.java index 4fa3536a..262c1b36 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenUnit.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/GaenUnit.java @@ -49,8 +49,7 @@ public R addTo(R temporal, long amount) { @Override public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) { - var between = ChronoUnit.MINUTES.between(temporal1Inclusive, temporal2Exclusive) / GAEN_MINUTES; - return between; + return ChronoUnit.MINUTES.between(temporal1Inclusive, temporal2Exclusive) / GAEN_MINUTES; } } \ No newline at end of file diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/Header.java b/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/Header.java deleted file mode 100644 index 54f8acb8..00000000 --- a/dpppt-backend-sdk/dpppt-backend-sdk-model/src/main/java/org/dpppt/backend/sdk/model/gaen/Header.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.dpppt.backend.sdk.model.gaen; - -public class Header { - Long startTimestamp; - Long endTimestamp; - String region; - Integer batchNum; - Integer batchSize; - - - public Long getStartTimestamp() { - return this.startTimestamp; - } - - public Long getEndTimestamp() { - return this.endTimestamp; - } - - public String getRegion() { - return this.region; - } - - public Integer getBatchNum() { - return this.batchNum; - } - - public Integer getBatchSize() { - return this.batchSize; - } - - public Header startTimestamp(Long startTimestamp) { - this.startTimestamp = startTimestamp; - return this; - } - - public Header endTimestamp(Long endTimestamp) { - this.endTimestamp = endTimestamp; - return this; - } - - public Header region(String region) { - this.region = region; - return this; - } - - public Header batchNum(Integer batchNum) { - this.batchNum = batchNum; - return this; - } - - public Header batchSize(Integer batchSize) { - this.batchSize = batchSize; - return this; - } - -} \ No newline at end of file diff --git a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java index 868624d7..30689219 100644 --- a/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java +++ b/dpppt-backend-sdk/dpppt-backend-sdk-ws/src/main/java/org/dpppt/backend/sdk/ws/controller/GaenController.java @@ -31,12 +31,10 @@ import org.dpppt.backend.sdk.data.gaen.FakeKeyService; import org.dpppt.backend.sdk.data.gaen.GAENDataService; import org.dpppt.backend.sdk.model.gaen.DayBuckets; -import org.dpppt.backend.sdk.model.gaen.GaenExposedJson; import org.dpppt.backend.sdk.model.gaen.GaenKey; import org.dpppt.backend.sdk.model.gaen.GaenRequest; import org.dpppt.backend.sdk.model.gaen.GaenSecondDay; import org.dpppt.backend.sdk.model.gaen.GaenUnit; -import org.dpppt.backend.sdk.model.gaen.Header; import org.dpppt.backend.sdk.ws.security.ValidateRequest; import org.dpppt.backend.sdk.ws.security.ValidateRequest.InvalidDateException; import org.dpppt.backend.sdk.ws.security.signature.ProtoSignature; @@ -101,16 +99,12 @@ public GaenController(GAENDataService dataService, FakeKeyService fakeKeyService @AuthenticationPrincipal Object principal) throws InvalidDateException { var now = Instant.now().toEpochMilli(); if (!this.validateRequest.isValid(principal)) { - return () -> { - return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); - }; + return () -> ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } List nonFakeKeys = new ArrayList<>(); for (var key : gaenRequest.getGaenKeys()) { if (!validationUtils.isValidBase64Key(key.getKeyData())) { - return () -> { - return new ResponseEntity<>("No valid base64 key", HttpStatus.BAD_REQUEST); - }; + return () -> new ResponseEntity<>("No valid base64 key", HttpStatus.BAD_REQUEST); } if (this.validateRequest.isFakeRequest(principal, key)) { continue; @@ -132,9 +126,7 @@ public GaenController(GAENDataService dataService, FakeKeyService fakeKeyService } if (principal instanceof Jwt && ((Jwt) principal).containsClaim("fake") && ((Jwt) principal).getClaim("fake").equals("1") && !nonFakeKeys.isEmpty()) { - return () -> { - return ResponseEntity.badRequest().body("Claim is fake but list contains non fake keys"); - }; + return () -> ResponseEntity.badRequest().body("Claim is fake but list contains non fake keys"); } if (!nonFakeKeys.isEmpty()) { dataService.upsertExposees(nonFakeKeys); @@ -146,9 +138,7 @@ public GaenController(GAENDataService dataService, FakeKeyService fakeKeyService var nowDay = LocalDate.now(ZoneOffset.UTC); if (delayedKeyDate.isBefore(nowDay.minusDays(1)) || delayedKeyDate.isAfter(nowDay.plusDays(1))) { - return () -> { - return ResponseEntity.badRequest().body("delayedKeyDate date must be between yesterday and tomorrow"); - }; + return () -> ResponseEntity.badRequest().body("delayedKeyDate date must be between yesterday and tomorrow"); } var responseBuilder = ResponseEntity.ok(); @@ -180,22 +170,16 @@ public GaenController(GAENDataService dataService, FakeKeyService fakeKeyService var now = Instant.now().toEpochMilli(); if (!validationUtils.isValidBase64Key(gaenSecondDay.getDelayedKey().getKeyData())) { - return () -> { - return new ResponseEntity<>("No valid base64 key", HttpStatus.BAD_REQUEST); - }; + return () -> new ResponseEntity<>("No valid base64 key", HttpStatus.BAD_REQUEST); } if (principal instanceof Jwt && !((Jwt) principal).containsClaim("delayedKeyDate")) { - return () -> { - return ResponseEntity.status(HttpStatus.FORBIDDEN).body("claim does not contain delayedKeyDate"); - }; + return () -> ResponseEntity.status(HttpStatus.FORBIDDEN).body("claim does not contain delayedKeyDate"); } if (principal instanceof Jwt) { var jwt = (Jwt) principal; var claimKeyDate = Integer.parseInt(jwt.getClaimAsString("delayedKeyDate")); if (!gaenSecondDay.getDelayedKey().getRollingStartNumber().equals(Integer.valueOf(claimKeyDate))) { - return () -> { - return ResponseEntity.badRequest().body("keyDate does not match claim keyDate"); - }; + return () -> ResponseEntity.badRequest().body("keyDate does not match claim keyDate"); } } if (!this.validateRequest.isFakeRequest(principal, gaenSecondDay.getDelayedKey())) {