Skip to content
This repository has been archived by the owner on Sep 15, 2023. It is now read-only.

Commit

Permalink
Merge pull request #14 from admin-ch/feature/valuesets
Browse files Browse the repository at this point in the history
Feature/valuesets
  • Loading branch information
ubhaller authored Jun 14, 2021
2 parents 4b98ea1 + 57e478b commit 6caaa0b
Show file tree
Hide file tree
Showing 22 changed files with 1,271 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcVerifierDataServiceImpl;
import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.KeyController;
import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.RevocationListController;
import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.ValueSetsController;
import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.VerificationRulesController;
import ch.admin.bag.covidcertificate.backend.verifier.ws.interceptor.HeaderInjector;
import ch.admin.bag.covidcertificate.backend.verifier.ws.security.signature.JwsMessageConverter;
Expand Down Expand Up @@ -84,6 +85,11 @@ public void setVerificationRulesMaxAge(Duration maxAge) {
CacheUtil.VERIFICATION_RULES_MAX_AGE = maxAge;
}

@Value("${ws.valueSets.max-age:PT1M}")
public void setValueSetsMaxAge(Duration maxAge) {
CacheUtil.VALUE_SETS_MAX_AGE = maxAge;
}

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
try {
Expand Down Expand Up @@ -138,6 +144,11 @@ public VerificationRulesController verificationRulesController()
return new VerificationRulesController();
}

@Bean
public ValueSetsController valueSetsController() throws IOException, NoSuchAlgorithmException {
return new ValueSetsController();
}

@Bean
public RestTemplate restTemplate() {
return RestTemplateHelper.getRestTemplate();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/

package ch.admin.bag.covidcertificate.backend.verifier.ws.controller;

import ch.admin.bag.covidcertificate.backend.verifier.ws.model.valuesets.TestValueSets;
import ch.admin.bag.covidcertificate.backend.verifier.ws.model.valuesets.VaccineValueSets;
import ch.admin.bag.covidcertificate.backend.verifier.ws.model.valuesets.ValueSets;
import ch.admin.bag.covidcertificate.backend.verifier.ws.utils.CacheUtil;
import ch.admin.bag.covidcertificate.backend.verifier.ws.utils.EtagUtil;
import ch.ubique.openapi.docannotations.Documentation;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("trust/v1")
public class ValueSetsController {

private static final Logger logger = LoggerFactory.getLogger(ValueSetsController.class);

public static final List<String> PATHS_TO_VALUE_SETS =
List.of(
"valuesets/test-manf.json",
"valuesets/test-type.json",
"valuesets/vaccine-mah-manf.json",
"valuesets/vaccine-medicinal-product.json",
"valuesets/vaccine-prophylaxis.json");

private final ValueSets valueSets;
private final String valueSetsEtag;
private final ObjectMapper mapper = new ObjectMapper();

public ValueSetsController() throws IOException, NoSuchAlgorithmException {
this.valueSets = new ValueSets(getTestValueSet(), getVaccineValueSet());

List<String> pathsToValueSets = new ArrayList<>();
for (String path : PATHS_TO_VALUE_SETS) {
pathsToValueSets.add(new ClassPathResource(path).getFile().getPath());
}
this.valueSetsEtag =
EtagUtil.getSha1HashForFiles(
pathsToValueSets.toArray(new String[pathsToValueSets.size()]));
}

private VaccineValueSets getVaccineValueSet() throws IOException {
VaccineValueSets vaccine = new VaccineValueSets();
vaccine.setMahManf(readFileAsMap("valuesets/vaccine-mah-manf.json"));
vaccine.setMedicinalProduct(readFileAsMap("valuesets/vaccine-medicinal-product.json"));
vaccine.setProphylaxis(readFileAsMap("valuesets/vaccine-prophylaxis.json"));
return vaccine;
}

private TestValueSets getTestValueSet() throws IOException {
TestValueSets test = new TestValueSets();
test.setManf(readFileAsMap("valuesets/test-manf.json"));
test.setType(readFileAsMap("valuesets/test-type.json"));
return test;
}

/**
* maps a file to a generic {@link Map}
*
* @param path relative to classpath
* @return
*/
private Map readFileAsMap(String path) throws IOException {
File file = new ClassPathResource(path).getFile();
return mapper.readValue(file, Map.class);
}

@Documentation(
description = "get value sets",
responses = {"200 => value sets", "304 => no changes since last request"},
responseHeaders = {"ETag:etag to set for next request:string"})
@GetMapping(value = "/metadata")
public @ResponseBody ResponseEntity<ValueSets> getVerificationRules(
@RequestHeader(value = HttpHeaders.ETAG, required = false) String etag) {
if (valueSetsEtag.equals(etag)) {
return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build();
}
return ResponseEntity.ok()
.header(HttpHeaders.ETAG, valueSetsEtag)
.cacheControl(CacheControl.maxAge(CacheUtil.VALUE_SETS_MAX_AGE))
.body(valueSets);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public VerificationRulesController() throws IOException, NoSuchAlgorithmExceptio
ObjectMapper mapper = new ObjectMapper();
File verificationRulesFile = new ClassPathResource("verificationRules.json").getFile();
this.verificationRules = mapper.readValue(verificationRulesFile, Map.class);
this.verificationRulesEtag = EtagUtil.getSha1HashForFile(verificationRulesFile.getPath());
this.verificationRulesEtag = EtagUtil.getSha1HashForFiles(verificationRulesFile.getPath());
}

@Documentation(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/

package ch.admin.bag.covidcertificate.backend.verifier.ws.model.valuesets;

import java.util.Map;

public class TestValueSets {
private Map type;
private Map manf;

public Map getType() {
return type;
}

public void setType(Map type) {
this.type = type;
}

public Map getManf() {
return manf;
}

public void setManf(Map manf) {
this.manf = manf;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/

package ch.admin.bag.covidcertificate.backend.verifier.ws.model.valuesets;

import java.util.Map;

public class VaccineValueSets {
private Map mahManf;
private Map medicinalProduct;
private Map prophylaxis;

public Map getMahManf() {
return mahManf;
}

public void setMahManf(Map mahManf) {
this.mahManf = mahManf;
}

public Map getMedicinalProduct() {
return medicinalProduct;
}

public void setMedicinalProduct(Map medicinalProduct) {
this.medicinalProduct = medicinalProduct;
}

public Map getProphylaxis() {
return prophylaxis;
}

public void setProphylaxis(Map prophylaxis) {
this.prophylaxis = prophylaxis;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/

package ch.admin.bag.covidcertificate.backend.verifier.ws.model.valuesets;

public class ValueSets {
private TestValueSets test;
private VaccineValueSets vaccine;

public ValueSets() {}

public ValueSets(TestValueSets test, VaccineValueSets vaccine) {
this.test = test;
this.vaccine = vaccine;
}

public TestValueSets getTest() {
return test;
}

public void setTest(TestValueSets test) {
this.test = test;
}

public VaccineValueSets getVaccine() {
return vaccine;
}

public void setVaccine(VaccineValueSets vaccine) {
this.vaccine = vaccine;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
public class CacheUtil {
public static Duration REVOCATION_LIST_MAX_AGE;
public static Duration VERIFICATION_RULES_MAX_AGE;
public static Duration VALUE_SETS_MAX_AGE;
public static Duration KEYS_UPDATE_MAX_AGE;
public static Duration KEYS_LIST_MAX_AGE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@ public static int getUnsortedListHashcode(List<String> list) {
return list != null ? list.stream().map(Objects::hash).reduce(0, (a, b) -> a ^ b) : 0;
}

public static String getSha1HashForFile(String pathToFile)
public static String getSha1HashForFiles(String... pathToFiles)
throws IOException, NoSuchAlgorithmException {
String classpathPrefix = "classpath:";
try (InputStream is =
pathToFile.startsWith(classpathPrefix)
? new ClassPathResource(pathToFile.replace(classpathPrefix, ""))
.getInputStream()
: new FileInputStream(pathToFile)) {

MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
byte[] buffer = new byte[8192];
int len = is.read(buffer);

while (len != -1) {
sha1.update(buffer, 0, len);
len = is.read(buffer);
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
for (String pathToFile : pathToFiles) {
final String classpathPrefix = "classpath:";
try (InputStream is =
pathToFile.startsWith(classpathPrefix)
? new ClassPathResource(pathToFile.replace(classpathPrefix, ""))
.getInputStream()
: new FileInputStream(pathToFile)) {

byte[] buffer = new byte[8192];
int len = is.read(buffer);
while (len != -1) {
sha1.update(buffer, 0, len);
len = is.read(buffer);
}
}

return Hex.encodeHexString(sha1.digest());
}
return Hex.encodeHexString(sha1.digest());
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# test p12 for local development use
ws.jws.p12=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
ws.jws.password=test
ws.jws.password=test

ws.authentication.apiKeys.local=4d1d5663-b4ef-46a5-85b6-3d1d376429da
Loading

0 comments on commit 6caaa0b

Please sign in to comment.