From bffe8886cddaefea218c4059a560f851db2ce31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Thu, 24 Mar 2022 13:27:50 +0100 Subject: [PATCH 01/13] implement foreign rule sync and serving --- .../data/ForeignRulesDataService.java | 45 ++++++++ .../impl/JdbcForeignRulesDataServiceImpl.java | 74 ++++++++++++ .../data/mapper/ForeignRulesRowMapper.java | 31 +++++ .../migration/pgsql/V0_12__foreign_rules.sql | 12 ++ .../pgsql_cluster/V0_12__foreign_rules.sql | 12 ++ .../data/ForeignRulesDataServiceTest.java | 88 ++++++++++++++ .../verifier/data/config/TestConfig.java | 9 ++ .../backend/verifier/model/ForeignRule.java | 61 ++++++++++ .../verifier/sync/config/SyncBaseConfig.java | 18 +++ .../sync/config/SyncSchedulingBaseConfig.java | 19 ++- .../sync/syncer/DgcForeignRulesSyncer.java | 79 +++++++++++++ .../verifier/sync/syncer/DgcRulesClient.java | 43 ++++++- .../verifier/sync/syncer/DgcRulesSyncer.java | 2 + .../backend/verifier/sync/utils/CmsUtil.java | 14 ++- .../verifier/sync/config/TestConfig.java | 20 ++++ .../sync/syncer/ForeignRulesSyncTest.java | 83 +++++++++++++ .../src/test/resources/AT.json | 1 + .../src/test/resources/DE.json | 1 + .../src/test/resources/countrylist.json | 1 + .../verifier/ws/config/SchedulingConfig.java | 2 + .../verifier/ws/config/WsBaseConfig.java | 15 +++ .../controller/ForeignRulesControllerV2.java | 109 ++++++++++++++++++ .../verifier/ws/config/TestConfig.java | 15 +++ .../ForeignRulesControllerV2Test.java | 103 +++++++++++++++++ .../ws/util/MockForeignRuleDataService.java | 59 ++++++++++ .../verifier/ws/util/MockRulesContent.java | 7 ++ 26 files changed, 908 insertions(+), 15 deletions(-) create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataService.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/impl/JdbcForeignRulesDataServiceImpl.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/mapper/ForeignRulesRowMapper.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql/V0_12__foreign_rules.sql create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql_cluster/V0_12__foreign_rules.sql create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataServiceTest.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-model/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/model/ForeignRule.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/AT.json create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/DE.json create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/countrylist.json create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockRulesContent.java diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataService.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataService.java new file mode 100644 index 00000000..633cecc9 --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataService.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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.data; + +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; +import java.util.List; + +/** + * Dataservice to store and retrieve validation rules obtained from the DGC gateway + */ +public interface ForeignRulesDataService { + + /** + * + * @return A list of all countries that we have rules for + */ + public List getCountries(); + + /** + * + * @param country the two-letter country code + * @return All available versions of all rules for that country + */ + public List getRulesForCountry(String country); + + /** + * Insert a rule into the DB + * @param rule the rule + */ + public void insertRule(ForeignRule rule); + + /** + * Remove all rules for a given country (in order to insert a new set) + * @param country the country + */ + public void removeRuleSet(String country); +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/impl/JdbcForeignRulesDataServiceImpl.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/impl/JdbcForeignRulesDataServiceImpl.java new file mode 100644 index 00000000..c93bf2bb --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/impl/JdbcForeignRulesDataServiceImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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.data.impl; + +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.mapper.ForeignRulesRowMapper; +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; +import java.time.LocalDateTime; +import java.util.List; +import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; + +public class JdbcForeignRulesDataServiceImpl implements ForeignRulesDataService { + + private static final Logger logger = + LoggerFactory.getLogger(JdbcForeignRulesDataServiceImpl.class); + + private final NamedParameterJdbcTemplate jt; + + public JdbcForeignRulesDataServiceImpl(DataSource dataSource) { + this.jt = new NamedParameterJdbcTemplate(dataSource); + } + + @Override + public List getCountries() { + String sql = "select distinct country from t_foreign_rules"; + return jt.queryForList(sql, new MapSqlParameterSource(), String.class); + } + + @Override + public List getRulesForCountry(String country) { + String sql = + "select country, rule_id, rule_version, rule_content, valid_from, valid_until from t_foreign_rules where country=:country"; + var params = new MapSqlParameterSource(); + params.addValue("country", country); + return jt.query(sql, params, new ForeignRulesRowMapper()); + } + + @Override + public void insertRule(ForeignRule rule) { + String sql = + "insert into t_foreign_rules" + + "(country, rule_id, rule_version, rule_content, valid_from, valid_until, inserted_at) " + + "values(:country, :rule_id, :rule_version, :rule_content, :valid_from, :valid_until, :inserted_at)"; + var params = new MapSqlParameterSource(); + params.addValue("country", rule.getCountry()); + params.addValue("rule_id", rule.getId()); + params.addValue("rule_version", rule.getVersion()); + params.addValue("rule_content", rule.getContent()); + params.addValue("valid_from", rule.getValidFrom()); + params.addValue("valid_until", rule.getValidUntil()); + params.addValue("inserted_at", LocalDateTime.now()); + jt.update(sql, params); + } + + @Override + public void removeRuleSet(String country) { + String sql = "delete from t_foreign_rules where country=:country"; + var params = new MapSqlParameterSource(); + params.addValue("country", country); + jt.update(sql, params); + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/mapper/ForeignRulesRowMapper.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/mapper/ForeignRulesRowMapper.java new file mode 100644 index 00000000..6955824c --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/mapper/ForeignRulesRowMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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.data.mapper; + +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; +import java.sql.ResultSet; +import java.sql.SQLException; +import org.springframework.jdbc.core.RowMapper; + +public class ForeignRulesRowMapper implements RowMapper { + + @Override + public ForeignRule mapRow(ResultSet resultSet, int i) throws SQLException { + var rule = new ForeignRule(); + rule.setCountry(resultSet.getString("country")); + rule.setId(resultSet.getString("rule_id")); + rule.setVersion(resultSet.getString("rule_version")); + rule.setContent(resultSet.getString("rule_content")); + rule.setValidFrom(resultSet.getTimestamp("valid_from").toLocalDateTime()); + rule.setValidUntil(resultSet.getTimestamp("valid_until").toLocalDateTime()); + return rule; + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql/V0_12__foreign_rules.sql b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql/V0_12__foreign_rules.sql new file mode 100644 index 00000000..75b9e22f --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql/V0_12__foreign_rules.sql @@ -0,0 +1,12 @@ +CREATE TABLE t_foreign_rules +( + pk_id SERIAL NOT NULL, + country CHAR(2) NOT NULL , + rule_id VARCHAR(64) NOT NULL, + rule_version VARCHAR(32) NOT NULL, + rule_content TEXT NOT NULL, + valid_from timestamp with time zone, + valid_until timestamp with time zone, + inserted_at timestamp with time zone, + CONSTRAINT rules_pk_id PRIMARY KEY (pk_id) +); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql_cluster/V0_12__foreign_rules.sql b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql_cluster/V0_12__foreign_rules.sql new file mode 100644 index 00000000..75b9e22f --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/resources/db/migration/pgsql_cluster/V0_12__foreign_rules.sql @@ -0,0 +1,12 @@ +CREATE TABLE t_foreign_rules +( + pk_id SERIAL NOT NULL, + country CHAR(2) NOT NULL , + rule_id VARCHAR(64) NOT NULL, + rule_version VARCHAR(32) NOT NULL, + rule_content TEXT NOT NULL, + valid_from timestamp with time zone, + valid_until timestamp with time zone, + inserted_at timestamp with time zone, + CONSTRAINT rules_pk_id PRIMARY KEY (pk_id) +); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataServiceTest.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataServiceTest.java new file mode 100644 index 00000000..2c03d9dd --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/ForeignRulesDataServiceTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import javax.sql.DataSource; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ForeignRulesDataServiceTest extends BaseDataServiceTest { + + @Autowired ForeignRulesDataService dataService; + @Autowired DataSource dataSource; + + ForeignRule atRule; + ForeignRule deRule1; + ForeignRule deRule2; + + @BeforeAll + void setup(){ + atRule = new ForeignRule(); + atRule.setId("GR-AT-0039"); + atRule.setVersion("0.0.1"); + atRule.setValidFrom(LocalDateTime.now().minus(1, ChronoUnit.DAYS)); + atRule.setValidUntil(LocalDateTime.now().plus(1, ChronoUnit.YEARS)); + atRule.setContent("{}"); + atRule.setCountry("AT"); + deRule1 = new ForeignRule(); + deRule1.setId("GR-DE-0039"); + deRule1.setVersion("0.0.1"); + deRule1.setValidFrom(LocalDateTime.now().minus(1, ChronoUnit.DAYS)); + deRule1.setValidUntil(LocalDateTime.now().plus(1, ChronoUnit.YEARS)); + deRule1.setContent("{}"); + deRule1.setCountry("DE"); + deRule2 = new ForeignRule(); + deRule2.setId("GR-DE-0039"); + deRule2.setVersion("0.0.2"); + deRule2.setValidFrom(LocalDateTime.now().minus(1, ChronoUnit.HOURS)); + deRule2.setValidUntil(LocalDateTime.now().plus(1, ChronoUnit.YEARS)); + deRule2.setContent("{}"); + deRule2.setCountry("DE"); + dataService.insertRule(atRule); + dataService.insertRule(deRule1); + dataService.insertRule(deRule2); + } + + @Test + void countryListTest() { + var countries = dataService.getCountries(); + assertEquals(2, countries.size()); + } + + @Test + void rulesForCountryTest(){ + var de = dataService.getRulesForCountry("DE"); + assertEquals(2, de.size()); + var fr = dataService.getRulesForCountry("FR"); + assertEquals(0, fr.size()); + } + + @Test + void deletionTest(){ + var de = dataService.getRulesForCountry("DE"); + assertEquals(2, de.size()); + dataService.removeRuleSet("DE"); + de = dataService.getRulesForCountry("DE"); + assertEquals(0, de.size()); + var countries = dataService.getCountries(); + assertEquals(1, countries.size()); + var at = dataService.getRulesForCountry("AT"); + assertEquals(1, at.size()); + dataService.insertRule(deRule1); + dataService.insertRule(deRule2); + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/config/TestConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/config/TestConfig.java index 2d54c117..7453847a 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/config/TestConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/data/config/TestConfig.java @@ -11,9 +11,11 @@ package ch.admin.bag.covidcertificate.backend.verifier.data.config; import ch.admin.bag.covidcertificate.backend.verifier.data.AppTokenDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.RevokedCertDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.VerifierDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcAppTokenDataServiceImpl; +import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcForeignRulesDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcRevokedCertDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcVerifierDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.util.CacheUtil; @@ -51,6 +53,13 @@ public RevokedCertDataService revokedCertDataService( return new JdbcRevokedCertDataServiceImpl(dataSource, revokedCertBatchSize); } + @Bean + public ForeignRulesDataService foreignRulesDataService( + DataSource dataSource + ){ + return new JdbcForeignRulesDataServiceImpl(dataSource); + } + @Value("${ws.revocation-list.retention-bucket-duration:PT6H}") public void setRevocationRetentionBucketDuration(Duration bucketDuration) { CacheUtil.REVOCATION_RETENTION_BUCKET_DURATION = bucketDuration; diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-model/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/model/ForeignRule.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-model/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/model/ForeignRule.java new file mode 100644 index 00000000..6edc29e6 --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-model/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/model/ForeignRule.java @@ -0,0 +1,61 @@ +package ch.admin.bag.covidcertificate.backend.verifier.model; + +import java.time.LocalDateTime; +import java.util.Locale.IsoCountryCode; + +public class ForeignRule { + private String country; + private String id; + private String version; + private String content; + private LocalDateTime validFrom; + private LocalDateTime validUntil; + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public LocalDateTime getValidFrom() { + return validFrom; + } + + public void setValidFrom(LocalDateTime validFrom) { + this.validFrom = validFrom; + } + + public LocalDateTime getValidUntil() { + return validUntil; + } + + public void setValidUntil(LocalDateTime validUntil) { + this.validUntil = validUntil; + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncBaseConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncBaseConfig.java index 58347af9..d1dc0bd3 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncBaseConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncBaseConfig.java @@ -11,13 +11,17 @@ package ch.admin.bag.covidcertificate.backend.verifier.sync.config; import ch.admin.bag.covidcertificate.backend.verifier.data.CertUploadDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.ValueSetDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.VerifierDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcCertUploadDataServiceImpl; +import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcForeignRulesDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcValueSetDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcVerifierDataServiceImpl; +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcCertClient; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcCertSyncer; +import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcForeignRulesSyncer; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcRulesClient; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcRulesSyncer; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcValueSetClient; @@ -33,6 +37,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.List; import javax.sql.DataSource; import net.javacrumbs.shedlock.core.LockProvider; import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider; @@ -122,6 +127,12 @@ public ValueSetDataService valueSetDataService( return new JdbcValueSetDataServiceImpl(dataSource, maxHistory); } + @Bean + public ForeignRulesDataService foreignRulesDataService( + DataSource dataSource) { + return new JdbcForeignRulesDataServiceImpl(dataSource); + } + @Bean public DgcValueSetClient dgcValueSetClient(RestTemplate restTemplate) { return new DgcValueSetClient(baseurl, restTemplate); @@ -154,6 +165,13 @@ public DgcRulesSyncer dgcRulesSyncer(DgcRulesClient dgcRulesClient) throws IOExc return new DgcRulesSyncer(verificationRulesUploadString, dgcRulesClient); } + @Bean + public DgcForeignRulesSyncer dgcForeignRulesSyncer( + DgcRulesClient dgcRulesClient, ForeignRulesDataService foreignRulesDataService) + throws IOException { + return new DgcForeignRulesSyncer(dgcRulesClient, foreignRulesDataService); + } + @Bean public DgcHubProxy dgcHubProxy(RestTemplate restTemplate) { return new DgcHubProxy(baseurl, restTemplate); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java index b36a09fc..55fe652f 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java @@ -14,6 +14,7 @@ import ch.admin.bag.covidcertificate.backend.verifier.data.VerifierDataService; import ch.admin.bag.covidcertificate.backend.verifier.model.exception.DgcSyncException; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcCertSyncer; +import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcForeignRulesSyncer; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcValueSetSyncer; import net.javacrumbs.shedlock.core.LockAssert; import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; @@ -36,15 +37,18 @@ public class SyncSchedulingBaseConfig { private final ValueSetDataService valueSetDataService; private final VerifierDataService verifierDataService; private final boolean syncCronEnabled; + private final DgcForeignRulesSyncer dgcForeignRulesSyncer; public SyncSchedulingBaseConfig( DgcCertSyncer dgcSyncer, DgcValueSetSyncer dgcValueSetSyncer, + DgcForeignRulesSyncer dgcForeignRulesSyncer, ValueSetDataService valueSetDataService, VerifierDataService verifierDataService, @Value("${dgc.sync.cron.enable:true}") boolean syncCronEnabled) { this.dgcSyncer = dgcSyncer; this.dgcValueSetSyncer = dgcValueSetSyncer; + this.dgcForeignRulesSyncer = dgcForeignRulesSyncer; this.valueSetDataService = valueSetDataService; this.verifierDataService = verifierDataService; this.syncCronEnabled = syncCronEnabled; @@ -82,11 +86,11 @@ public void valueSetCleanCron() { valueSetDataService.deleteOldValueSets(); } - @Scheduled(cron = "${value-set.sync.cron:0 0 0 ? * *}") - @SchedulerLock(name = "value_set_sync", lockAtLeastFor = "PT15S") - public void valueSetSyncCron() { + @Scheduled(cron = "${foreign-rules.sync.cron:0 0 0 ? * *}") + @SchedulerLock(name = "foreign_rules_sync", lockAtLeastFor = "PT15S") + public void foreignRulesSyncCron() { LockAssert.assertLocked(); - dgcValueSetSyncer.sync(); + dgcForeignRulesSyncer.sync(); } @Scheduled(fixedRate = Long.MAX_VALUE, initialDelay = 0) @@ -95,4 +99,11 @@ public void valueSetSyncOnStartup() { LockAssert.assertLocked(); dgcValueSetSyncer.sync(); } + + @Scheduled(fixedRate = Long.MAX_VALUE, initialDelay = 0) + @SchedulerLock(name = "foreign_rules_sync", lockAtLeastFor = "PT15S") + public void foreignRulesSyncOnStartup() { + LockAssert.assertLocked(); + dgcForeignRulesSyncer.sync(); + } } diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java new file mode 100644 index 00000000..66865695 --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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.sync.syncer; + +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; +import ch.admin.bag.covidcertificate.backend.verifier.sync.utils.CmsUtil; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.operator.OperatorCreationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DgcForeignRulesSyncer { + private static final Logger logger = LoggerFactory.getLogger(DgcForeignRulesSyncer.class); + private final ForeignRulesDataService foreignRulesDataService; + private final DgcRulesClient dgcRulesClient; + + public DgcForeignRulesSyncer( + DgcRulesClient dgcRulesClient, ForeignRulesDataService foreignRulesDataService) { + this.dgcRulesClient = dgcRulesClient; + this.foreignRulesDataService = foreignRulesDataService; + } + + public void sync() { + logger.info("Start foreign rules sync with DGC Gateway"); + var start = Instant.now(); + + List countries = dgcRulesClient.getCountries(); + countries.remove("CH"); + //download data for the country + countries.forEach( + //this gives us an array of rule IDs + country -> dgcRulesClient.download(country) + //each rule ID has an array of rule versions + .forEach((ruleId, ruleVersions) -> (ruleVersions).forEach(ruleVersion -> { + //insert each rule version into the DB + try { + foreignRulesDataService.insertRule(decodeRule(ruleVersion)); + } catch (Exception e){ + logger.error("Failed to decode rule {}", ruleId, e); + } + }))); + var end = Instant.now(); + logger.info( + "Successfully downloaded foreign rules {} ms", + end.toEpochMilli() - start.toEpochMilli()); + } + + private ForeignRule decodeRule(JsonNode rule) + throws CertificateException, IOException, OperatorCreationException, CMSException { + var foreignRule = new ForeignRule(); + String content = new String((byte[]) CmsUtil.decodeCms(rule.get("cms").asText()), StandardCharsets.UTF_8); + foreignRule.setContent(content); + var validUntil = LocalDateTime.ofInstant(Instant.parse(rule.get("validTo").asText()), ZoneId.of("UTC")); + foreignRule.setValidUntil(validUntil); + var validFrom = LocalDateTime.ofInstant(Instant.parse(rule.get("validFrom").asText()), ZoneId.of("UTC")); + foreignRule.setValidFrom(validFrom); + foreignRule.setVersion(rule.get("version").asText()); + foreignRule.setCountry(rule.get("country").asText()); + foreignRule.setId(rule.get("id").asText()); + return foreignRule; + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesClient.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesClient.java index 136f258a..fdc59644 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesClient.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesClient.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; @@ -38,6 +39,7 @@ public class DgcRulesClient { private static final String RULE_UPLOAD_PATH = "/rules"; private static final String RULE_DELETE_PATH = "/rules/delete"; private static final String DOWNLOAD_PATH = "/rules/%s"; + private static final String COUNTRY_LIST_PATH = "/countrylist"; private final String dgcBaseUrl; private final RestTemplate dgcRT; private final SigningClient signingClient; @@ -49,16 +51,16 @@ public DgcRulesClient(String dgcBaseUrl, RestTemplate dgcRT, SigningClient signi } /** - * downloads rules for Switzerland + * downloads rules for the given country * - * @return rules for Switzerland + * @return downloaded rules */ - public Map download() { + public Map download(String country) { logger.info("[DgcRulesClient] Downloading rules"); Map rules = new HashMap<>(); ResponseEntity response = this.dgcRT.exchange( - RequestEntity.get(dgcBaseUrl + String.format(DOWNLOAD_PATH, "CH")) + RequestEntity.get(dgcBaseUrl + String.format(DOWNLOAD_PATH, country)) .headers(CmsUtil.createRuleExchangeHeaders()) .build(), String.class); @@ -78,6 +80,35 @@ public Map download() { return rules; } + /** + * Get the list of countries on the DGC gateway + * + * @return 2-letter country codes + */ + public List getCountries() { + logger.info("[DgcRulesClient] Downloading country list"); + ResponseEntity response = + this.dgcRT.exchange( + RequestEntity.get(dgcBaseUrl + COUNTRY_LIST_PATH) + .headers(CmsUtil.createRuleExchangeHeaders()) + .build(), + String.class); + List countries = new ArrayList<>(); + try { + countries = + new ObjectMapper() + .readValue( + response.getBody(), + TypeFactory.defaultInstance() + .constructCollectionType(List.class, String.class)); + } catch (JsonProcessingException e) { + logger.error("[DgcRulesClient] Failed to deserialize downloaded rules", e); + } + + logger.info("[DgcRulesClient] downloaded country list with {} entries", countries.size()); + return countries; + } + /** * deletes the rules with the given IDs * @@ -133,7 +164,7 @@ public RulesSyncResult delete(Collection identifiers) { */ public RulesSyncResult deleteAll() { logger.info("Deleting all Swiss rules"); - Map rules = download(); + Map rules = download("CH"); Set ruleIds = rules.keySet(); return delete(ruleIds); } @@ -145,7 +176,7 @@ public RulesSyncResult deleteAll() { */ public RulesSyncResult upload(JsonNode rules) { // Download all remote rules and later remove the ones that should not be deleted - Set rulesToDelete = download().keySet(); + Set rulesToDelete = download("CH").keySet(); logger.info("Uploading Swiss rules"); List uploadedRuleIds = new ArrayList<>(); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesSyncer.java index 260bd5e7..3d714c91 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesSyncer.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcRulesSyncer.java @@ -51,4 +51,6 @@ public RulesSyncResult sync() { return new RulesSyncResult(null, null); } } + + } diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/utils/CmsUtil.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/utils/CmsUtil.java index b4953df8..d3549980 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/utils/CmsUtil.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/utils/CmsUtil.java @@ -46,14 +46,11 @@ public static HttpHeaders createRuleExchangeHeaders() { return headers; } - public static SigningPayload encodePayload(byte[] payload){ - var base64encoded = - Base64.getEncoder() - .encodeToString(payload); + public static SigningPayload encodePayload(byte[] payload) { + var base64encoded = Base64.getEncoder().encodeToString(payload); return new SigningPayload(base64encoded); } - public static HttpHeaders createCmsUploadHeaders() { var headers = new HttpHeaders(); headers.add(HttpHeaders.ACCEPT, "application/json"); @@ -70,6 +67,13 @@ public static DbDsc decodeDscCms(String cms) return TrustListMapper.mapDsc(x509, "CH", getKeyId(x509)); } + public static Object decodeCms(String cms) + throws CMSException, CertificateException, IOException, OperatorCreationException { + CMSSignedData cmsSignedData = new CMSSignedData(Base64.getDecoder().decode(cms)); + validateSignature(cmsSignedData); + return cmsSignedData.getSignedContent().getContent(); + } + private static void validateSignature(CMSSignedData cmsSignedData) throws CertificateException, IOException, CMSException, OperatorCreationException { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/TestConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/TestConfig.java index 559a173d..5686acf3 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/TestConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/TestConfig.java @@ -10,10 +10,14 @@ package ch.admin.bag.covidcertificate.backend.verifier.sync.config; +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.VerifierDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcForeignRulesDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcVerifierDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcCertClient; import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcCertSyncer; +import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcForeignRulesSyncer; +import ch.admin.bag.covidcertificate.backend.verifier.sync.syncer.DgcRulesClient; import ch.admin.bag.covidcertificate.backend.verifier.sync.utils.RestTemplateHelper; import java.time.Duration; import javax.sql.DataSource; @@ -49,6 +53,17 @@ public DgcCertClient dgcClient(RestTemplate restTemplate) { return new DgcCertClient(baseurl, restTemplate); } + @Bean + public DgcRulesClient rulesClient(RestTemplate restTemplate) { + logger.info("Instantiated Rules Client with baseurl: {}", baseurl); + return new DgcRulesClient(baseurl, restTemplate, null); + } + + @Bean + public DgcForeignRulesSyncer rulesSyncer(DgcRulesClient rulesClient, ForeignRulesDataService dataService){ + return new DgcForeignRulesSyncer(rulesClient, dataService); + } + @Bean public VerifierDataService verifierDataService( DataSource dataSource, @@ -57,6 +72,11 @@ public VerifierDataService verifierDataService( dataSource, dscBatchSize, keepDscsMarkedForDeletionDuration); } + @Bean + public ForeignRulesDataService foreignRulesDataService(DataSource dataSource){ + return new JdbcForeignRulesDataServiceImpl(dataSource); + } + @Bean public RestTemplate restTemplate() { return RestTemplateHelper.getRestTemplate(); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java new file mode 100644 index 00000000..b02afa69 --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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.sync.syncer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; + +import ch.admin.bag.covidcertificate.backend.verifier.model.sync.CertificateType; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +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.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; +import org.springframework.test.web.client.MockRestServiceServer; + +class ForeignRulesSyncTest extends BaseDgcTest { + + private static final Logger logger = LoggerFactory.getLogger(ForeignRulesSyncTest.class); + private final String COUNTRY_LIST_JSON = "src/test/resources/countrylist.json"; + private final String AT_JSON = "src/test/resources/AT.json"; + private final String DE_JSON = "src/test/resources/DE.json"; + + @Value("${dgc.baseurl}") + String baseurl = "https://testurl.europa.eu"; + + @Autowired DgcForeignRulesSyncer rulesSyncer; + + @Test + void downloadTest() throws Exception { + var countryList = Files.readString(Path.of(COUNTRY_LIST_JSON)); + var at = Files.readString(Path.of(AT_JSON)); + var de = Files.readString(Path.of(DE_JSON)); + final var mockServer = MockRestServiceServer.createServer(rt); + mockServer + .expect( + ExpectedCount.once(), + requestTo(new URI(baseurl + "/countrylist"))) + .andExpect(method(HttpMethod.GET)) + .andRespond( + withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(countryList)); + mockServer + .expect( + ExpectedCount.once(), + requestTo(new URI(baseurl + "/rules/AT"))) + .andExpect(method(HttpMethod.GET)) + .andRespond( + withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(at)); + mockServer + .expect( + ExpectedCount.once(), + requestTo(new URI(baseurl + "/rules/DE"))) + .andExpect(method(HttpMethod.GET)) + .andRespond( + withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(de)); + rulesSyncer.sync(); + } + +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/AT.json b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/AT.json new file mode 100644 index 00000000..c761815f --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/AT.json @@ -0,0 +1 @@ +{"VR-AT-0000":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIBg3siSWRlbnRpZmllciI6IlZSLUFULTAwMDAiLCJUeXBlIjoiQWNjZXB0YW5jZSIsIkNvdW50cnkiOiJBVCIsIlJlZ2lvbiI6IkFUIiwiVmVyc2lvbiI6IjEuMC4xMiIsIlNjaGVtYVZlcnNpb24iOiIxLjAuMCIsIkVuZ2luZSI6IkNFUlRMT0dJQyIsIkVuZ2luZVZlcnNpb24iOiIwLjcuNSIsIkNlcnRpZmljYXRlVHlwZSI6IlZhY2NpbmF0aW9uIiwiRGVzY3JpcHRpb24iOlt7ImxhbmciOiJlbiIsImRlc2MiOiJBdCBtb3N0IG9uZSB2LWV2ZW50LiJ9XSwiVmFsaWRGcm9tIjoiMjAyMS0xMS0yOVQwMDowMDowMFoiLCJWYWxpZFRvIjoiMjAzMC0wNi0wMVQwMDowMDowMFoiLCJBZmZlY3RlZEZpZWxkcyI6WyJ2LjEiXSwiTG9naWMiOnsiISI6W3sidmFyIjoicGF5bG9hZC52LjEifV19fQAAAAAAAKCAMIIB6zCCAZGgAwIBAgIKAXmEuobOfnkGDDAKBggqhkjOPQQDAjBQMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNTE5MTMwNDQ3WhcNMjIwNjE5MTMwNDQ3WjBRMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQ8wDQYDVQQFEwYwMDEwMDExFDASBgNVBAMMC0FUIERHQyBVUCAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEv+mmnT+B88eEinBOl7uTldTphAwd+eAaoAAX80tIDP5aUrsXHC+YYG88BQMPVfM8hl/+xmVMKB60s37Edo/kR6NSMFAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBQFtHGEW0ADbb9gyOb2Y73Deex6TjAfBgNVHSMEGDAWgBR2sKi2xkUpGC1Cr5ehwL0hniIsJzAKBggqhkjOPQQDAgNIADBFAiEAoqPOQgeQLvV40ce+HGTT7FqSEg5kyeZBmIADVJdTq+ACICCvBVw3AY3kqTG7cb0cJhznO7xKLfkWGWwCrEqvb4c1AAAxggFiMIIBXgIBATBeMFAxCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAsMAVExDDAKBgNVBAUTAzAwMTEWMBQGA1UEAwwNQVQgREdDIENTQ0EgMQIKAXmEuobOfnkGDDANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTExMjYxMjQzMTJaMCoGCSqGSIb3DQEJNDEdMBswDQYJYIZIAWUDBAIBBQChCgYIKoZIzj0EAwIwLwYJKoZIhvcNAQkEMSIEIDBkxqJjjDHfU2jVKTJ19B097m4XNhB6rkGcVWO/cUaXMAoGCCqGSM49BAMCBEYwRAIgTuAZwQRbZQ/JEevNQNVmlA9WzgQe1xlramypC0McalQCIERFfG90vdUp8S2hH1ucXBRrOH0mrle5G2jXTBk4eYg2AAAAAAAA"}],"GR-AT-0001":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"VR-AT-0001":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"GR-AT-0000":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"VR-AT-0006":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"VR-AT-0005":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIDoHsiSWRlbnRpZmllciI6IlZSLUFULTAwMDUiLCJUeXBlIjoiQWNjZXB0YW5jZSIsIkNvdW50cnkiOiJBVCIsIlJlZ2lvbiI6IkFUIiwiVmVyc2lvbiI6IjEuMC4xMiIsIlNjaGVtYVZlcnNpb24iOiIxLjAuMCIsIkVuZ2luZSI6IkNFUlRMT0dJQyIsIkVuZ2luZVZlcnNpb24iOiIwLjcuNSIsIkNlcnRpZmljYXRlVHlwZSI6IlZhY2NpbmF0aW9uIiwiRGVzY3JpcHRpb24iOlt7ImxhbmciOiJlbiIsImRlc2MiOiJJZiAodG90YWwgbnVtYmVyIG9mIGRvc2VzID4gMSkgYW5kIChzZXF1ZW5jZW51bWJlciA+PSB0b3RhbCBudW1iZXIgb2YgZG9zZXMpIChBVDogJ1p3ZWl0aW1wZnVuZycgYnp3LiAnd2VpdGVyZSBJbXBmdW5nJykgVmVyaWZpY2F0aW9uIERhdGV0aW1lIG11c3QgYmUgbGVzcyB0aGFuIDM2MCBkYXlzIGFmdGVyIHZhY2NpbmF0aW9uIGRhdGUgKGFuZCB2YWNjaW5hdGlvbiBkYXRlIG5vdCBpbiB0aGUgZnV0dXJlKS4ifV0sIlZhbGlkRnJvbSI6IjIwMjEtMTEtMjlUMDA6MDA6MDBaIiwiVmFsaWRUbyI6IjIwMzAtMDYtMDFUMDA6MDA6MDBaIiwiQWZmZWN0ZWRGaWVsZHMiOlsidi4wIiwidi4wLmRuIiwidi4wLnNkIiwidi4wLmR0Il0sIkxvZ2ljIjp7ImlmIjpbeyJ2YXIiOiJwYXlsb2FkLnYuMCJ9LHsiaWYiOlt7Ij4iOlt7InZhciI6InBheWxvYWQudi4wLnNkIn0sMV19LHsiaWYiOlt7Ij49IjpbeyJ2YXIiOiJwYXlsb2FkLnYuMC5kbiJ9LHsidmFyIjoicGF5bG9hZC52LjAuc2QifV19LHsibm90LWFmdGVyIjpbeyJwbHVzVGltZSI6W3sidmFyIjoicGF5bG9hZC52LjAuZHQifSwwLCJkYXkiXX0seyJwbHVzVGltZSI6W3sidmFyIjoiZXh0ZXJuYWwudmFsaWRhdGlvbkNsb2NrIn0sMCwiZGF5Il19LHsicGx1c1RpbWUiOlt7InZhciI6InBheWxvYWQudi4wLmR0In0sMzYwLCJkYXkiXX1dfSx0cnVlXX0sdHJ1ZV19LHRydWVdfX0AAAAAAACggDCCAeswggGRoAMCAQICCgF5hLqGzn55BgwwCgYIKoZIzj0EAwIwUDELMAkGA1UEBhMCQVQxDzANBgNVBAoMBkJNU0dQSzEKMAgGA1UECwwBUTEMMAoGA1UEBRMDMDAxMRYwFAYDVQQDDA1BVCBER0MgQ1NDQSAxMB4XDTIxMDUxOTEzMDQ0N1oXDTIyMDYxOTEzMDQ0N1owUTELMAkGA1UEBhMCQVQxDzANBgNVBAoMBkJNU0dQSzEKMAgGA1UECwwBUTEPMA0GA1UEBRMGMDAxMDAxMRQwEgYDVQQDDAtBVCBER0MgVVAgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL/ppp0/gfPHhIpwTpe7k5XU6YQMHfngGqAAF/NLSAz+WlK7FxwvmGBvPAUDD1XzPIZf/sZlTCgetLN+xHaP5EejUjBQMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUBbRxhFtAA22/YMjm9mO9w3nsek4wHwYDVR0jBBgwFoAUdrCotsZFKRgtQq+XocC9IZ4iLCcwCgYIKoZIzj0EAwIDSAAwRQIhAKKjzkIHkC71eNHHvhxk0+xakhIOZMnmQZiAA1SXU6vgAiAgrwVcNwGN5Kkxu3G9HCYc5zu8Si35FhlsAqxKr2+HNQAAMYIBYzCCAV8CAQEwXjBQMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDECCgF5hLqGzn55BgwwDQYJYIZIAWUDBAIBBQCggZUwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjExMTI2MTI0MzEzWjAqBgkqhkiG9w0BCTQxHTAbMA0GCWCGSAFlAwQCAQUAoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJBDEiBCBWag4gYKbXVmL5aqTBs3JPLOUdt6mZOkZCJ4dgZIriODAKBggqhkjOPQQDAgRHMEUCIHpsKl8Nd3LGR48v2LQIxeH4kS68sCeG9H8303vKSLD6AiEArv9qXOMV7MOOmq6lh8LlNe1ubd81gMJQjDdddvplvv0AAAAAAAA="}],"TR-AT-0006":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"RR-AT-0002":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"RR-AT-0001":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"RR-AT-0000":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIBgHsiSWRlbnRpZmllciI6IlJSLUFULTAwMDAiLCJUeXBlIjoiQWNjZXB0YW5jZSIsIkNvdW50cnkiOiJBVCIsIlJlZ2lvbiI6IkFUIiwiVmVyc2lvbiI6IjEuMC4xMiIsIlNjaGVtYVZlcnNpb24iOiIxLjAuMCIsIkVuZ2luZSI6IkNFUlRMT0dJQyIsIkVuZ2luZVZlcnNpb24iOiIwLjcuNSIsIkNlcnRpZmljYXRlVHlwZSI6IlJlY292ZXJ5IiwiRGVzY3JpcHRpb24iOlt7ImxhbmciOiJlbiIsImRlc2MiOiJBdCBtb3N0IG9uZSByLWV2ZW50LiJ9XSwiVmFsaWRGcm9tIjoiMjAyMS0xMS0yOVQwMDowMDowMFoiLCJWYWxpZFRvIjoiMjAzMC0wNi0wMVQwMDowMDowMFoiLCJBZmZlY3RlZEZpZWxkcyI6WyJyLjEiXSwiTG9naWMiOnsiISI6W3sidmFyIjoicGF5bG9hZC5yLjEifV19fQAAAAAAAKCAMIIB6zCCAZGgAwIBAgIKAXmEuobOfnkGDDAKBggqhkjOPQQDAjBQMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNTE5MTMwNDQ3WhcNMjIwNjE5MTMwNDQ3WjBRMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQ8wDQYDVQQFEwYwMDEwMDExFDASBgNVBAMMC0FUIERHQyBVUCAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEv+mmnT+B88eEinBOl7uTldTphAwd+eAaoAAX80tIDP5aUrsXHC+YYG88BQMPVfM8hl/+xmVMKB60s37Edo/kR6NSMFAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBQFtHGEW0ADbb9gyOb2Y73Deex6TjAfBgNVHSMEGDAWgBR2sKi2xkUpGC1Cr5ehwL0hniIsJzAKBggqhkjOPQQDAgNIADBFAiEAoqPOQgeQLvV40ce+HGTT7FqSEg5kyeZBmIADVJdTq+ACICCvBVw3AY3kqTG7cb0cJhznO7xKLfkWGWwCrEqvb4c1AAAxggFiMIIBXgIBATBeMFAxCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAsMAVExDDAKBgNVBAUTAzAwMTEWMBQGA1UEAwwNQVQgREdDIENTQ0EgMQIKAXmEuobOfnkGDDANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTExMjYxMjQzMTBaMCoGCSqGSIb3DQEJNDEdMBswDQYJYIZIAWUDBAIBBQChCgYIKoZIzj0EAwIwLwYJKoZIhvcNAQkEMSIEILNhBBu7gGxdTKRr77qwgv928lo4Scxsob9KLoXheNshMAoGCCqGSM49BAMCBEYwRAIgRsNviTymbtdtqyMlYfpYb2Kl4N8mDE6UqdFck1cr/70CIGvuGpNdUznnnZ9lDkL2M3XyRrA0GLqmoyCe7sYWqTAwAAAAAAAA"}],"VR-AT-0007":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIICMnsiSWRlbnRpZmllciI6IlZSLUFULTAwMDciLCJUeXBlIjoiQWNjZXB0YW5jZSIsIkNvdW50cnkiOiJBVCIsIlJlZ2lvbiI6IkFUIiwiVmVyc2lvbiI6IjEuMC4xMiIsIlNjaGVtYVZlcnNpb24iOiIxLjAuMCIsIkVuZ2luZSI6IkNFUlRMT0dJQyIsIkVuZ2luZVZlcnNpb24iOiIwLjcuNSIsIkNlcnRpZmljYXRlVHlwZSI6IlZhY2NpbmF0aW9uIiwiRGVzY3JpcHRpb24iOlt7ImxhbmciOiJlbiIsImRlc2MiOiJJZiAoc2VxdWVuY2VudW1iZXIgPCB0b3RhbCBudW1iZXIgb2YgZG9zZXMpIChBVDogJ0Vyc3RpbXBmdW5nJykgLT4gbm90IHZhbGlkIGFueW1vcmUifV0sIlZhbGlkRnJvbSI6IjIwMjEtMTEtMjlUMDA6MDA6MDBaIiwiVmFsaWRUbyI6IjIwMzAtMDYtMDFUMDA6MDA6MDBaIiwiQWZmZWN0ZWRGaWVsZHMiOlsidi4wIiwidi4wLmRuIiwidi4wLnNkIiwidi4wLmR0Il0sIkxvZ2ljIjp7ImlmIjpbeyJ2YXIiOiJwYXlsb2FkLnYuMCJ9LHsiaWYiOlt7IjwiOlt7InZhciI6InBheWxvYWQudi4wLmRuIn0seyJ2YXIiOiJwYXlsb2FkLnYuMC5zZCJ9XX0sZmFsc2UsdHJ1ZV19LHRydWVdfX0AAAAAAACggDCCAeswggGRoAMCAQICCgF5hLqGzn55BgwwCgYIKoZIzj0EAwIwUDELMAkGA1UEBhMCQVQxDzANBgNVBAoMBkJNU0dQSzEKMAgGA1UECwwBUTEMMAoGA1UEBRMDMDAxMRYwFAYDVQQDDA1BVCBER0MgQ1NDQSAxMB4XDTIxMDUxOTEzMDQ0N1oXDTIyMDYxOTEzMDQ0N1owUTELMAkGA1UEBhMCQVQxDzANBgNVBAoMBkJNU0dQSzEKMAgGA1UECwwBUTEPMA0GA1UEBRMGMDAxMDAxMRQwEgYDVQQDDAtBVCBER0MgVVAgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL/ppp0/gfPHhIpwTpe7k5XU6YQMHfngGqAAF/NLSAz+WlK7FxwvmGBvPAUDD1XzPIZf/sZlTCgetLN+xHaP5EejUjBQMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUBbRxhFtAA22/YMjm9mO9w3nsek4wHwYDVR0jBBgwFoAUdrCotsZFKRgtQq+XocC9IZ4iLCcwCgYIKoZIzj0EAwIDSAAwRQIhAKKjzkIHkC71eNHHvhxk0+xakhIOZMnmQZiAA1SXU6vgAiAgrwVcNwGN5Kkxu3G9HCYc5zu8Si35FhlsAqxKr2+HNQAAMYIBYzCCAV8CAQEwXjBQMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDECCgF5hLqGzn55BgwwDQYJYIZIAWUDBAIBBQCggZUwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjExMTI2MTI0MzEzWjAqBgkqhkiG9w0BCTQxHTAbMA0GCWCGSAFlAwQCAQUAoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJBDEiBCBLmQOOFJNsP6vvwr8hY0yasVruUbPEEhmbG3RorIlXNTAKBggqhkjOPQQDAgRHMEUCIGigTwvtr+oBZnRVCbHh1Cj6yiGiaeSMtYhySU6yZSqRAiEA0IAtX+/l/yUegSLOiGq0+mTkP9Sb8ZnQBdZ6TJfRGSIAAAAAAAA="}],"TR-AT-0004":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIB1nsiSWRlbnRpZmllciI6IlRSLUFULTAwMDQiLCJUeXBlIjoiQWNjZXB0YW5jZSIsIkNvdW50cnkiOiJBVCIsIlJlZ2lvbiI6IkFUIiwiVmVyc2lvbiI6IjEuMC4xMiIsIlNjaGVtYVZlcnNpb24iOiIxLjAuMCIsIkVuZ2luZSI6IkNFUlRMT0dJQyIsIkVuZ2luZVZlcnNpb24iOiIwLjcuNSIsIkNlcnRpZmljYXRlVHlwZSI6IlRlc3QiLCJEZXNjcmlwdGlvbiI6W3sibGFuZyI6ImVuIiwiZGVzYyI6IlRlc3QgcmVzdWx0IG11c3QgYmUgbmVnYXRpdmUgKFwibm90IGRldGVjdGVkXCIpLiJ9XSwiVmFsaWRGcm9tIjoiMjAyMS0xMS0yOVQwMDowMDowMFoiLCJWYWxpZFRvIjoiMjAzMC0wNi0wMVQwMDowMDowMFoiLCJBZmZlY3RlZEZpZWxkcyI6WyJ0LjAiLCJ0LjAudHIiXSwiTG9naWMiOnsiaWYiOlt7InZhciI6InBheWxvYWQudC4wIn0seyI9PT0iOlt7InZhciI6InBheWxvYWQudC4wLnRyIn0sIjI2MDQxNTAwMCJdfSx0cnVlXX19AAAAAAAAoIAwggHrMIIBkaADAgECAgoBeYS6hs5+eQYMMAoGCCqGSM49BAMCMFAxCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAsMAVExDDAKBgNVBAUTAzAwMTEWMBQGA1UEAwwNQVQgREdDIENTQ0EgMTAeFw0yMTA1MTkxMzA0NDdaFw0yMjA2MTkxMzA0NDdaMFExCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAsMAVExDzANBgNVBAUTBjAwMTAwMTEUMBIGA1UEAwwLQVQgREdDIFVQIDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS/6aadP4Hzx4SKcE6Xu5OV1OmEDB354BqgABfzS0gM/lpSuxccL5hgbzwFAw9V8zyGX/7GZUwoHrSzfsR2j+RHo1IwUDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFAW0cYRbQANtv2DI5vZjvcN57HpOMB8GA1UdIwQYMBaAFHawqLbGRSkYLUKvl6HAvSGeIiwnMAoGCCqGSM49BAMCA0gAMEUCIQCio85CB5Au9XjRx74cZNPsWpISDmTJ5kGYgANUl1Or4AIgIK8FXDcBjeSpMbtxvRwmHOc7vEot+RYZbAKsSq9vhzUAADGCAWQwggFgAgEBMF4wUDELMAkGA1UEBhMCQVQxDzANBgNVBAoMBkJNU0dQSzEKMAgGA1UECwwBUTEMMAoGA1UEBRMDMDAxMRYwFAYDVQQDDA1BVCBER0MgQ1NDQSAxAgoBeYS6hs5+eQYMMA0GCWCGSAFlAwQCAQUAoIGVMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIxMTEyNjEyNDMxMlowKgYJKoZIhvcNAQk0MR0wGzANBglghkgBZQMEAgEFAKEKBggqhkjOPQQDAjAvBgkqhkiG9w0BCQQxIgQg2mkLvB047jbMr/tGZlAtE+VaoYmyU/hQAcMMQMn71uMwCgYIKoZIzj0EAwIESDBGAiEAiBITBKhKG8P/P1mTgRAC24qt+x1IMxcPvoK//RDeF7wCIQDB7FAECPwRKAc2pfD1AXnJ8PNvbHaJMp2xfSRTHkSS7gAAAAAAAA=="}],"TR-AT-0001":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIB3XsiSWRlbnRpZmllciI6IlRSLUFULTAwMDEiLCJUeXBlIjoiQWNjZXB0YW5jZSIsIkNvdW50cnkiOiJBVCIsIlJlZ2lvbiI6IkFUIiwiVmVyc2lvbiI6IjEuMC4xMiIsIlNjaGVtYVZlcnNpb24iOiIxLjAuMCIsIkVuZ2luZSI6IkNFUlRMT0dJQyIsIkVuZ2luZVZlcnNpb24iOiIwLjcuNSIsIkNlcnRpZmljYXRlVHlwZSI6IlRlc3QiLCJEZXNjcmlwdGlvbiI6W3sibGFuZyI6ImVuIiwiZGVzYyI6IlRoZSB0ZXN0IHR5cGUgbXVzdCBiZSBOQUEgKFBDUikuIFJBVCBubyBsb25nZXIgYWxsb3dlZC4ifV0sIlZhbGlkRnJvbSI6IjIwMjEtMTEtMjlUMDA6MDA6MDBaIiwiVmFsaWRUbyI6IjIwMzAtMDYtMDFUMDA6MDA6MDBaIiwiQWZmZWN0ZWRGaWVsZHMiOlsidC4wIiwidC4wLnR0Il0sIkxvZ2ljIjp7ImlmIjpbeyJ2YXIiOiJwYXlsb2FkLnQuMCJ9LHsiaW4iOlt7InZhciI6InBheWxvYWQudC4wLnR0In0sWyJMUDY0NjQtNCJdXX0sdHJ1ZV19fQAAAAAAAKCAMIIB6zCCAZGgAwIBAgIKAXmEuobOfnkGDDAKBggqhkjOPQQDAjBQMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNTE5MTMwNDQ3WhcNMjIwNjE5MTMwNDQ3WjBRMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQowCAYDVQQLDAFRMQ8wDQYDVQQFEwYwMDEwMDExFDASBgNVBAMMC0FUIERHQyBVUCAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEv+mmnT+B88eEinBOl7uTldTphAwd+eAaoAAX80tIDP5aUrsXHC+YYG88BQMPVfM8hl/+xmVMKB60s37Edo/kR6NSMFAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBQFtHGEW0ADbb9gyOb2Y73Deex6TjAfBgNVHSMEGDAWgBR2sKi2xkUpGC1Cr5ehwL0hniIsJzAKBggqhkjOPQQDAgNIADBFAiEAoqPOQgeQLvV40ce+HGTT7FqSEg5kyeZBmIADVJdTq+ACICCvBVw3AY3kqTG7cb0cJhznO7xKLfkWGWwCrEqvb4c1AAAxggFkMIIBYAIBATBeMFAxCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAsMAVExDDAKBgNVBAUTAzAwMTEWMBQGA1UEAwwNQVQgREdDIENTQ0EgMQIKAXmEuobOfnkGDDANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTExMjYxMjQzMTFaMCoGCSqGSIb3DQEJNDEdMBswDQYJYIZIAWUDBAIBBQChCgYIKoZIzj0EAwIwLwYJKoZIhvcNAQkEMSIEIHe96n/hl/xOq+lxWS7EA6+zPbetsxz+QtVyX1VC9ajiMAoGCCqGSM49BAMCBEgwRgIhAMwgnXqou/H97Odm7iX7xmHEL6ZZ0l1ROfGne6F8SlywAiEA+x/EV+aufXhzoAO2WSGzRH2461k9cXtprTnteOISkXAAAAAAAAA="}],"TR-AT-0000":[{"version":"1.0.12","validFrom":"2021-11-29T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIBfHsiSWRlbnRpZmllciI6IlRSLUFULTAwMDAiLCJUeXBlIjoiQWNjZXB0YW5jZSIsIkNvdW50cnkiOiJBVCIsIlJlZ2lvbiI6IkFUIiwiVmVyc2lvbiI6IjEuMC4xMiIsIlNjaGVtYVZlcnNpb24iOiIxLjAuMCIsIkVuZ2luZSI6IkNFUlRMT0dJQyIsIkVuZ2luZVZlcnNpb24iOiIwLjcuNSIsIkNlcnRpZmljYXRlVHlwZSI6IlRlc3QiLCJEZXNjcmlwdGlvbiI6W3sibGFuZyI6ImVuIiwiZGVzYyI6IkF0IG1vc3Qgb25lIHQtZXZlbnQuIn1dLCJWYWxpZEZyb20iOiIyMDIxLTExLTI5VDAwOjAwOjAwWiIsIlZhbGlkVG8iOiIyMDMwLTA2LTAxVDAwOjAwOjAwWiIsIkFmZmVjdGVkRmllbGRzIjpbInQuMSJdLCJMb2dpYyI6eyIhIjpbeyJ2YXIiOiJwYXlsb2FkLnQuMSJ9XX19AAAAAAAAoIAwggHrMIIBkaADAgECAgoBeYS6hs5+eQYMMAoGCCqGSM49BAMCMFAxCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAsMAVExDDAKBgNVBAUTAzAwMTEWMBQGA1UEAwwNQVQgREdDIENTQ0EgMTAeFw0yMTA1MTkxMzA0NDdaFw0yMjA2MTkxMzA0NDdaMFExCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAsMAVExDzANBgNVBAUTBjAwMTAwMTEUMBIGA1UEAwwLQVQgREdDIFVQIDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS/6aadP4Hzx4SKcE6Xu5OV1OmEDB354BqgABfzS0gM/lpSuxccL5hgbzwFAw9V8zyGX/7GZUwoHrSzfsR2j+RHo1IwUDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFAW0cYRbQANtv2DI5vZjvcN57HpOMB8GA1UdIwQYMBaAFHawqLbGRSkYLUKvl6HAvSGeIiwnMAoGCCqGSM49BAMCA0gAMEUCIQCio85CB5Au9XjRx74cZNPsWpISDmTJ5kGYgANUl1Or4AIgIK8FXDcBjeSpMbtxvRwmHOc7vEot+RYZbAKsSq9vhzUAADGCAWQwggFgAgEBMF4wUDELMAkGA1UEBhMCQVQxDzANBgNVBAoMBkJNU0dQSzEKMAgGA1UECwwBUTEMMAoGA1UEBRMDMDAxMRYwFAYDVQQDDA1BVCBER0MgQ1NDQSAxAgoBeYS6hs5+eQYMMA0GCWCGSAFlAwQCAQUAoIGVMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIxMTEyNjEyNDMxMVowKgYJKoZIhvcNAQk0MR0wGzANBglghkgBZQMEAgEFAKEKBggqhkjOPQQDAjAvBgkqhkiG9w0BCQQxIgQgshRzTXpYWehtM2pVWRFz+NxYcGdEKj7Y9jLVXuHbRlIwCgYIKoZIzj0EAwIESDBGAiEAwlCF5o79CGgXmL6Dq/WvEOiWc/9yGfnneMXXZrBkIEcCIQDg3ivxmWWUYqEf0fjsUeE477r113i+fQSbfhw2lNh8wgAAAAAAAA=="}]} \ No newline at end of file diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/DE.json b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/DE.json new file mode 100644 index 00000000..941dda99 --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/DE.json @@ -0,0 +1 @@ +{"IR-DE-0001":[{"version":"1.0.2","validFrom":"2021-08-20T11:20:36+02:00","validTo":"2031-08-18T11:20:36+02:00","cms":"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"}],"IR-DE-0002":[{"version":"1.0.4","validFrom":"2021-08-20T11:45:26+02:00","validTo":"2031-08-18T11:45:26+02:00","cms":"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"}],"IR-DE-0003":[{"version":"1.0.4","validFrom":"2021-08-20T11:20:36+02:00","validTo":"2031-08-18T11:20:36+02:00","cms":"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"}],"IR-DE-0004":[{"version":"1.0.4","validFrom":"2022-02-01T10:00:00+01:00","validTo":"2031-08-18T00:00:00+02:00","cms":"MIIPBgYJKoZIhvcNAQcCoIIO9zCCDvMCAQExDzANBglghkgBZQMEAgEFADCCBPIGCSqGSIb3DQEHAaCCBOMEggTfeyJJZGVudGlmaWVyIjogIklSLURFLTAwMDQiLCAiVHlwZSI6ICJJbnZhbGlkYXRpb24iLCAiQ291bnRyeSI6ICJERSIsICJWZXJzaW9uIjogIjEuMC40IiwgIlNjaGVtYVZlcnNpb24iOiAiMS4wLjAiLCAiRW5naW5lIjogIkNFUlRMT0dJQyIsICJFbmdpbmVWZXJzaW9uIjogIjEuMC4wIiwgIkNlcnRpZmljYXRlVHlwZSI6ICJWYWNjaW5hdGlvbiIsICJEZXNjcmlwdGlvbiI6IFt7ImxhbmciOiAiZW4iLCAiZGVzYyI6ICJJc3N1ZXIgaGFzIGJlZW4gY29tcHJvbWlzZWQuIEZvciBzZWN1cml0eSByZWFzb25zLCBhbGwgY2VydGlmaWNhdGVzIG9mIHRoZSBpc3N1ZXIgZnJvbSB3aG9tIHlvdSBoYXZlIHJlY2VpdmVkIHRoZSBjZXJ0aWZpY2F0ZSBtdXN0IGJlIHJlaXNzdWVkLiBZb3UgY2FuIGFycmFuZ2UgZm9yIGEgbmV3IHZhY2NpbmF0aW9uIGNlcnRpZmljYXRlIHRvIGJlIGlzc3VlZCBmcmVlIG9mIGNoYXJnZSBhdCB5b3VyIGhlYWx0aCBjYXJlIHByb3ZpZGVyIG9yIHBoYXJtYWN5IGJ5IHNob3dpbmcgeW91ciB5ZWxsb3cgdmFjY2luYXRpb24gcGFzc3BvcnQgYW5kIGEgdmFsaWQgcGhvdG8gSUQuIn0sIHsiZGVzYyI6ICJBdXNzdGVsbGVyIHd1cmRlIGtvbXByb21pdHRpZXJ0LiBBdXMgU2ljaGVyaGVpdHNnclx1MDBmY25kZW4gbVx1MDBmY3NzZW4gYWxsZSBaZXJ0aWZpa2F0ZSBkZXMgQXVzc3RlbGxlcnMsIHZvbiBkZW0gU2llIElociBaZXJ0aWZpa2F0IGVyaGFsdGVuIGhhYmVuIG5ldSBhdXNnZXN0ZWxsdCB3ZXJkZW4uIFVudGVyIFZvcmxhZ2UgSWhyZXMgZ2VsYmVuIEltcGZwYXNzZXMgdW5kIGVpbmVzIExpY2h0YmlsZGF1c3dlaXNlcyBrXHUwMGY2bm5lbiBT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"}],"TR-DE-0001":[{"version":"1.0.1","validFrom":"2021-07-19T02:00:00+02:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"TR-DE-0002":[{"version":"1.0.1","validFrom":"2021-07-19T02:00:00+02:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsKICAiSWRlbnRpZmllciI6ICJUUi1ERS0wMDAyIiwKICAiVHlwZSI6ICJBY2NlcHRhbmNlIiwKICAiQ291bnRyeSI6ICJERSIsCiAgIlZlcnNpb24iOiAiMS4wLjEiLAogICJTY2hlbWFWZXJzaW9uIjogIjEuMC4wIiwKICAiRW5naW5lIjogIkNFUlRMT0dJQyIsCiAgIkVuZ2luZVZlcnNpb24iOiAiMC43LjUiLAogICJDZXJ0aWZpY2F0ZVR5cGUiOiAiVGVzdCIsCiAgIkRlc2NyaXB0aW9uIjogWwogICAgewogICAgICAibGFuZyI6ICJlbiIsCiAgICAgICJkZXNjIjogIlRoZSBzYW1wbGUgZm9yIGFuIGFudGlnZW4gdGVzdCAoZS5nLiwgcmFwaWQgdGVzdCkgbXVzdCBoYXZlIGJlZW4gdGFrZW4gbm8gbG9uZ2VyIHRoYW4gNDggaG91cnMgYWdvLiIKICAgIH0sCiAgICB7CiAgICAgICJsYW5nIjogImRlIiwKICAgICAgImRlc2MiOiAiRGllIFByb2JlbmFobWUgZsO8ciBlaW5lbiBBbnRpZ2VuLVRlc3QgKHouQi4gU2NobmVsbHRlc3QpIGRhcmYgbWF4aW1hbCA0OCBTdHVuZGVuIHp1csO8Y2tsaWVnZW4uIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiZnIiLAogICAgICAiZGVzYyI6ICJMZSBwcsOpbMOodmVtZW50IHBvdXIgdW4gdGVzdCBhbnRpZ8OpbmlxdWUgKHAuIGV4LiB0ZXN0IHJhcGlkZSkgbmUgZG9pdCBwYXMgZGF0ZXIgZGUgcGx1cyBkZSA0OCBoZXVyZXMuIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiZXMiLAogICAgICAiZGVzYyI6ICJEZWJlbiBoYWJlciB0cmFuc2N1cnJpZG8gNDggaG9yYXMgY29tbyBtw6F4aW1vIGRlc2RlIGxhIGV4dHJhY2Npw7NuIHBhcmEgdW5hIHBydWViYSBkZSBhbnTDrWdlbm9zIChwb3IgZWplbXBsbywgdW4gYXV0b3Rlc3QgcsOhcGlkbykuIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiaXQiLAogICAgICAiZGVzYyI6ICJJbCBjYW1waW9uZSBwZXIgaWwgdGVzdCBhbnRpZ2VuaWNvICh0ZXN0IHJhcGlkbykgZGV2ZSBlc3NlcmUgc3RhdG8gcmlsZXZhdG8gbmVsbGUgdWx0aW1lIDQ4IG9yZS4iCiAgICAEggO+fQogIF0sCiAgIlZhbGlkRnJvbSI6ICIyMDIxLTA3LTE5VDAwOjAwOjAwWiIsCiAgIlZhbGlkVG8iOiAiMjAzMC0wNi0wMVQwMDowMDowMFoiLAogICJBZmZlY3RlZEZpZWxkcyI6IFsKICAgICJ0LjAiLAogICAgInQuMC5zYyIsCiAgICAidC4wLnR0IgogIF0sCiAgIkxvZ2ljIjogewogICAgImlmIjogWwogICAgICB7CiAgICAgICAgInZhciI6ICJwYXlsb2FkLnQuMCIKICAgICAgfSwKICAgICAgewogICAgICAgICJpZiI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgIj09PSI6IFsKICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAidmFyIjogInBheWxvYWQudC4wLnR0IgogICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgIkxQMjE3MTk4LTMiCiAgICAgICAgICAgIF0KICAgICAgICAgIH0sCiAgICAgICAgICB7CiAgICAgICAgICAgICJub3QtYWZ0ZXIiOiBbCiAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgInBsdXNUaW1lIjogWwogICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgInZhciI6ICJleHRlcm5hbC52YWxpZGF0aW9uQ2xvY2siCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgIDAsCiAgICAgICAgICAgICAgICAgICJkYXkiCiAgICAgICAgICAgICAgICBdCiAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAicGx1c1RpbWUiOiBbCiAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAidmFyIjogInBheWxvYWQudC4wLnNjIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICA0OCwKICAgICAgICAgICAgICAgICAgImhvdXIiCiAgICAgICAgICAgICAgICBdCiAgICAgICAgICAgICAgfQogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgdHJ1ZQogICAgICAgIF0KICAgICAgfSwKICAgICAgdHJ1ZQogICAgXQogIH0KfQAAAAAAAKCAMIICuzCCAmKgAwIBAgIUdXK9JWgnVG6EuvuksBTVVaJMxOcwCgYIKoZIzj0EAwIwgdMxJzAlBgkqhkiG9w0BCQEWGERMLWNhby1jd2FAdC1zeXN0ZW1zLmNvbTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkhlc3NlbjEaMBgGA1UEBwwRRnJhbmtmdXJ0IGFtIE1haW4xJTAjBgNVBAoMHFQtU3lzdGVtcyBJbnRlcm5hdGlvbmFsIEdtYkgxFjAUBgNVBAsMDUNXQU9wZXJhdGlvbnMxLzAtBgNVBAMMJmRpc3RyaWJ1dGlvbi1jZmY0ZjcxNDcyNjAuZGNjLXJ1bGVzLmRlMB4XDTIxMDYyNTEwMDM0MFoXDTIyMDYyNTEwMDM0MFowgdMxJzAlBgkqhkiG9w0BCQEWGERMLWNhby1jd2FAdC1zeXN0ZW1zLmNvbTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkhlc3NlbjEaMBgGA1UEBwwRRnJhbmtmdXJ0IGFtIE1haW4xJTAjBgNVBAoMHFQtU3lzdGVtcyBJbnRlcm5hdGlvbmFsIEdtYkgxFjAUBgNVBAsMDUNXQU9wZXJhdGlvbnMxLzAtBgNVBAMMJmRpc3RyaWJ1dGlvbi1jZmY0ZjcxNDcyNjAuZGNjLXJ1bGVzLmRlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcEr90JMtQlYDOSZ/sQbIS3RZIPtnTfEC2TpyZzTGQNSr0+kw64cuG6/Ka3GUJn98uBE7qAUKf8bBWiw7a9f7v6MSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIHVhm4hd677REItCK3fSi2T9PFyEDg8TA5C72Be2BS3KAiAKy2pvIeYqIi80jZZyIckS5Oieqb7JvClQr/fwg/slRQAAMYIB8TCCAe0CAQEwgewwgdMxJzAlBgkqhkiG9w0BCQEWGERMLWNhby1jd2FAdC1zeXN0ZW1zLmNvbTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkhlc3NlbjEaMBgGA1UEBwwRRnJhbmtmdXJ0IGFtIE1haW4xJTAjBgNVBAoMHFQtU3lzdGVtcyBJbnRlcm5hdGlvbmFsIEdtYkgxFjAUBgNVBAsMDUNXQU9wZXJhdGlvbnMxLzAtBgNVBAMMJmRpc3RyaWJ1dGlvbi1jZmY0ZjcxNDcyNjAuZGNjLXJ1bGVzLmRlAhR1cr0laCdUboS6+6SwFNVVokzE5zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTA3MTYwODM1MjZaMCoGCSqGSIb3DQEJNDEdMBswDQYJYIZIAWUDBAIBBQChCgYIKoZIzj0EAwIwLwYJKoZIhvcNAQkEMSIEIBJKLC4Liu3brY3yy2hSBgwjcloy6xHWWrtO732hnQ94MAoGCCqGSM49BAMCBEYwRAIgFlD+UNxV8zLXUXdrs4suxWOPnqJQB0zwathm3AcY1Z8CIBJxTWOh9F0J/QqizJVIdH0elebm8k9BS5Cu8Jc3T+2TAAAAAAAA"}],"TR-DE-0003":[{"version":"1.0.1","validFrom":"2021-07-19T02:00:00+02:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsKICAiSWRlbnRpZmllciI6ICJUUi1ERS0wMDAzIiwKICAiVHlwZSI6ICJBY2NlcHRhbmNlIiwKICAiQ291bnRyeSI6ICJERSIsCiAgIlZlcnNpb24iOiAiMS4wLjEiLAogICJTY2hlbWFWZXJzaW9uIjogIjEuMC4wIiwKICAiRW5naW5lIjogIkNFUlRMT0dJQyIsCiAgIkVuZ2luZVZlcnNpb24iOiAiMC43LjUiLAogICJDZXJ0aWZpY2F0ZVR5cGUiOiAiVGVzdCIsCiAgIkRlc2NyaXB0aW9uIjogWwogICAgewogICAgICAibGFuZyI6ICJlbiIsCiAgICAgICJkZXNjIjogIlRoZSBzYW1wbGUgZm9yIGFuIE5BQSB0ZXN0IChlLmcuLCBQQ1IpIG11c3QgaGF2ZSBiZWVuIHRha2VuIG5vIGxvbmdlciB0aGFuIDcyIGhvdXJzIGFnby4iCiAgICB9LAogICAgewogICAgICAibGFuZyI6ICJkZSIsCiAgICAgICJkZXNjIjogIkRpZSBQcm9iZW5haG1lIGbDvHIgZWluZW4gTkFBLVRlc3QgKHouQi4gUENSKSBkYXJmIG1heGltYWwgNzIgU3R1bmRlbiB6dXLDvGNrbGllZ2VuLiIKICAgIH0sCiAgICB7CiAgICAgICJsYW5nIjogImZyIiwKICAgICAgImRlc2MiOiAiTGUgcHLDqWzDqHZlbWVudCBwb3VyIHVuIHRlc3QgVEFOIChwLiBleC4gUENSKSBuZSBkb2l0IHBhcyBkYXRlciBkZSBwbHVzIGRlIDcyIGhldXJlcy4iCiAgICB9LAogICAgewogICAgICAibGFuZyI6ICJlcyIsCiAgICAgICJkZXNjIjogIkRlYmVuIGhhYmVyIHRyYW5zY3VycmlkbyA3MiBob3JhcyBjb21vIG3DoXhpbW8gZGVzZGUgbGEgZXh0cmFjY2nDs24gcGFyYSB1bmEgcHJ1ZWJhIGRlIGFtcGxpZmljYWNpw7NuIGRlIMOhY2lkb3MgbnVjbGVpY29zIChwb3IgZWplbXBsbywgdW5hIFBDUikuIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiaXQiLAogICAgICAiZGVzYyI6ICJJbCBjYW1waW9uZSBwZXIgaWwgTkFBVCAodGVzdCBQUkMpIGRldmUgZXNzZXJlIHN0YXRvIHJpbGV2YXRvIG5lbGxlIHVsdGltZSA3MiBvcmUuIgogICAgfQogIF0sCiAgIlZhbGlkRnJvbSI6ICIyMDIxLTA3LTE5VDAwOjAwOjAEggOTMFoiLAogICJWYWxpZFRvIjogIjIwMzAtMDYtMDFUMDA6MDA6MDBaIiwKICAiQWZmZWN0ZWRGaWVsZHMiOiBbCiAgICAidC4wIiwKICAgICJ0LjAuc2MiLAogICAgInQuMC50dCIKICBdLAogICJMb2dpYyI6IHsKICAgICJpZiI6IFsKICAgICAgewogICAgICAgICJ2YXIiOiAicGF5bG9hZC50LjAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiaWYiOiBbCiAgICAgICAgICB7CiAgICAgICAgICAgICI9PT0iOiBbCiAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgInZhciI6ICJwYXlsb2FkLnQuMC50dCIKICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICJMUDY0NjQtNCIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgIm5vdC1hZnRlciI6IFsKICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAicGx1c1RpbWUiOiBbCiAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAidmFyIjogImV4dGVybmFsLnZhbGlkYXRpb25DbG9jayIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgMCwKICAgICAgICAgICAgICAgICAgImRheSIKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJwbHVzVGltZSI6IFsKICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICJ2YXIiOiAicGF5bG9hZC50LjAuc2MiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgIDcyLAogICAgICAgICAgICAgICAgICAiaG91ciIKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgICB9CiAgICAgICAgICAgIF0KICAgICAgICAgIH0sCiAgICAgICAgICB0cnVlCiAgICAgICAgXQogICAgICB9LAogICAgICB0cnVlCiAgICBdCiAgfQp9AAAAAAAAoIAwggK7MIICYqADAgECAhR1cr0laCdUboS6+6SwFNVVokzE5zAKBggqhkjOPQQDAjCB0zEnMCUGCSqGSIb3DQEJARYYREwtY2FvLWN3YUB0LXN5c3RlbXMuY29tMQswCQYDVQQGEwJERTEPMA0GA1UECAwGSGVzc2VuMRowGAYDVQQHDBFGcmFua2Z1cnQgYW0gTWFpbjElMCMGA1UECgwcVC1TeXN0ZW1zIEludGVybmF0aW9uYWwgR21iSDEWMBQGA1UECwwNQ1dBT3BlcmF0aW9uczEvMC0GA1UEAwwmZGlzdHJpYnV0aW9uLWNmZjRmNzE0NzI2MC5kY2MtcnVsZXMuZGUwHhcNMjEwNjI1MTAwMzQwWhcNMjIwNjI1MTAwMzQwWjCB0zEnMCUGCSqGSIb3DQEJARYYREwtY2FvLWN3YUB0LXN5c3RlbXMuY29tMQswCQYDVQQGEwJERTEPMA0GA1UECAwGSGVzc2VuMRowGAYDVQQHDBFGcmFua2Z1cnQgYW0gTWFpbjElMCMGA1UECgwcVC1TeXN0ZW1zIEludGVybmF0aW9uYWwgR21iSDEWMBQGA1UECwwNQ1dBT3BlcmF0aW9uczEvMC0GA1UEAwwmZGlzdHJpYnV0aW9uLWNmZjRmNzE0NzI2MC5kY2MtcnVsZXMuZGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARwSv3Qky1CVgM5Jn+xBshLdFkg+2dN8QLZOnJnNMZA1KvT6TDrhy4br8prcZQmf3y4ETuoBQp/xsFaLDtr1/u/oxIwEDAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDRwAwRAIgdWGbiF3rvtEQi0Ird9KLZP08XIQODxMDkLvYF7YFLcoCIArLam8h5ioiLzSNlnIhyRLk6J6pvsm8KVCv9/CD+yVFAAAxggHyMIIB7gIBATCB7DCB0zEnMCUGCSqGSIb3DQEJARYYREwtY2FvLWN3YUB0LXN5c3RlbXMuY29tMQswCQYDVQQGEwJERTEPMA0GA1UECAwGSGVzc2VuMRowGAYDVQQHDBFGcmFua2Z1cnQgYW0gTWFpbjElMCMGA1UECgwcVC1TeXN0ZW1zIEludGVybmF0aW9uYWwgR21iSDEWMBQGA1UECwwNQ1dBT3BlcmF0aW9uczEvMC0GA1UEAwwmZGlzdHJpYnV0aW9uLWNmZjRmNzE0NzI2MC5kY2MtcnVsZXMuZGUCFHVyvSVoJ1RuhLr7pLAU1VWiTMTnMA0GCWCGSAFlAwQCAQUAoIGVMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIxMDcxNjA4MzUyOFowKgYJKoZIhvcNAQk0MR0wGzANBglghkgBZQMEAgEFAKEKBggqhkjOPQQDAjAvBgkqhkiG9w0BCQQxIgQgKzwrW48+2r/eILgzrwiLjaGK5eP02qWrCzjgdZ1OIxEwCgYIKoZIzj0EAwIERzBFAiARoms3Jyqy82aZ93B6FVmgLHxPHoTHOwtiNby9oQKRlwIhAIstdyUPaWr0bq9BQSBH3m+NpoSHk4uOWC8gJ1wjeWk1AAAAAAAA"}],"TR-DE-0004":[{"version":"1.0.1","validFrom":"2021-07-19T02:00:00+02:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}],"TR-DE-0005":[{"version":"1.0.1","validFrom":"2021-07-19T02:00:00+02:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsKICAiSWRlbnRpZmllciI6ICJUUi1ERS0wMDA1IiwKICAiVHlwZSI6ICJBY2NlcHRhbmNlIiwKICAiQ291bnRyeSI6ICJERSIsCiAgIlZlcnNpb24iOiAiMS4wLjEiLAogICJTY2hlbWFWZXJzaW9uIjogIjEuMC4wIiwKICAiRW5naW5lIjogIkNFUlRMT0dJQyIsCiAgIkVuZ2luZVZlcnNpb24iOiAiMC43LjUiLAogICJDZXJ0aWZpY2F0ZVR5cGUiOiAiVGVzdCIsCiAgIkRlc2NyaXB0aW9uIjogWwogICAgewogICAgICAibGFuZyI6ICJlbiIsCiAgICAgICJkZXNjIjogIlRoZSBhbnRpZ2VuIHRlc3QgKGUuZy4sIHJhcGlkIHRlc3QpIG11c3QgYmUgaW5jbHVkZWQgaW4gdGhlIEVV4oCZcyBcIkNvbW1vbiBsaXN0IG9mIENPVklELTE5IHJhcGlkIGFudGlnZW4gdGVzdHNcIi4iCiAgICB9LAogICAgewogICAgICAibGFuZyI6ICJkZSIsCiAgICAgICJkZXNjIjogIkRlciBBbnRpZ2VuLVRlc3QgKHouQi4gU2NobmVsbHRlc3QpIG11c3MgaW4gZGVyIExpc3RlIFwiQ29tbW9uIGxpc3Qgb2YgcmFwaWQgYW50aWdlbiB0ZXN0c1wiIGRlciBFVSBlbnRoYWx0ZW4gc2Vpbi4iCiAgICB9LAogICAgewogICAgICAibGFuZyI6ICJmciIsCiAgICAgICJkZXNjIjogIkxlIHRlc3QgYW50aWfDqW5pcXVlIChwLiBleC4gdGVzdCByYXBpZGUpIGRvaXQgZmlndXJlciBzdXIgbGEgbGlzdGUgY29tbXVuZSBkZXMgdGVzdHMgYW50aWfDqW5pcXVlcyByYXBpZGVzIMKrIENvbW1vbiBsaXN0IG9mIHJhcGlkIGFudGlnZW4gdGVzdHMgwrsgZGUgbOKAmVVFLiIKICAgIH0sCiAgICB7CiAgICAgICJsYW5nIjogImVzIiwKICAgICAgImRlc2MiOiAiTGEgcHJ1ZWJhIGRlIGFudMOtZ2Vub3MgKHBvciBlamVtcGxvLCB1bmEgcHJ1ZWJhIHLDoXBpZGEpIGRlYmUgZXN0YXIgaW5jbHVpZGEgZW4gbGEgbGlzdGEgXCJDb21tb24gbGlzdCBvZiByYXBpZCBhbnRpZ2VuIHRlc3RzXCIgZGUgbGEgVW5pw7NuIEV1cm9wZWEuIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiaXQiLAogICAgICAiZGUEggNQc2MiOiAiSWwgdGVzdCBhbnRpZ2VuaWNvIChhZCBlcy4gdGVzdCByYXBpZG8pIGRldmUgY29ycmlzcG9uZGVyZSBhIHVubyBkZWkgdGVzdCBkZWxsYSBcIkNvbW1vbiBsaXN0IG9mIHJhcGlkIGFudGlnZW4gdGVzdHNcIiBkZWxs4oCZVUUuIgogICAgfQogIF0sCiAgIlZhbGlkRnJvbSI6ICIyMDIxLTA3LTE5VDAwOjAwOjAwWiIsCiAgIlZhbGlkVG8iOiAiMjAzMC0wNi0wMVQwMDowMDowMFoiLAogICJBZmZlY3RlZEZpZWxkcyI6IFsKICAgICJ0LjAiLAogICAgInQuMC5tYSIsCiAgICAidC4wLnR0IgogIF0sCiAgIkxvZ2ljIjogewogICAgImlmIjogWwogICAgICB7CiAgICAgICAgInZhciI6ICJwYXlsb2FkLnQuMCIKICAgICAgfSwKICAgICAgewogICAgICAgICJpZiI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgIj09PSI6IFsKICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAidmFyIjogInBheWxvYWQudC4wLnR0IgogICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgIkxQMjE3MTk4LTMiCiAgICAgICAgICAgIF0KICAgICAgICAgIH0sCiAgICAgICAgICB7CiAgICAgICAgICAgICJpbiI6IFsKICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAidmFyIjogInBheWxvYWQudC4wLm1hIgogICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgInZhciI6ICJleHRlcm5hbC52YWx1ZVNldHMuY292aWQtMTktbGFiLXRlc3QtbWFudWZhY3R1cmVyLWFuZC1uYW1lIgogICAgICAgICAgICAgIH0KICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHRydWUKICAgICAgICBdCiAgICAgIH0sCiAgICAgIHRydWUKICAgIF0KICB9Cn0AAAAAAACggDCCArswggJioAMCAQICFHVyvSVoJ1RuhLr7pLAU1VWiTMTnMAoGCCqGSM49BAMCMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTAeFw0yMTA2MjUxMDAzNDBaFw0yMjA2MjUxMDAzNDBaMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHBK/dCTLUJWAzkmf7EGyEt0WSD7Z03xAtk6cmc0xkDUq9PpMOuHLhuvymtxlCZ/fLgRO6gFCn/GwVosO2vX+7+jEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNHADBEAiB1YZuIXeu+0RCLQit30otk/TxchA4PEwOQu9gXtgUtygIgCstqbyHmKiIvNI2WciHJEuTonqm+ybwpUK/38IP7JUUAADGCAfMwggHvAgEBMIHsMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZQIUdXK9JWgnVG6EuvuksBTVVaJMxOcwDQYJYIZIAWUDBAIBBQCggZUwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjEwNzE2MDgzNTMwWjAqBgkqhkiG9w0BCTQxHTAbMA0GCWCGSAFlAwQCAQUAoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJBDEiBCB+yGRQcvwp4zFtT7A+ef6kjZ8VFX3OsJBPCFt2QZzxUDAKBggqhkjOPQQDAgRIMEYCIQCbPIsK9ZmMQ7HlOpqtcp6py3GXOVw96KLF53lR4ecwawIhALT05s9SPdnrHtjEPQSWjupIlp6zam9JW3NL+4QUMS6dAAAAAAAA"}],"VR-DE-0004":[{"version":"1.0.2","validFrom":"2022-02-14T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsNCiAgIklkZW50aWZpZXIiOiAiVlItREUtMDAwNCIsDQogICJUeXBlIjogIkFjY2VwdGFuY2UiLA0KICAiQ291bnRyeSI6ICJERSIsDQogICJWZXJzaW9uIjogIjEuMC4yIiwNCiAgIlNjaGVtYVZlcnNpb24iOiAiMS4wLjAiLA0KICAiRW5naW5lIjogIkNFUlRMT0dJQyIsDQogICJFbmdpbmVWZXJzaW9uIjogIjAuNy41IiwNCiAgIkNlcnRpZmljYXRlVHlwZSI6ICJWYWNjaW5hdGlvbiIsDQogICJEZXNjcmlwdGlvbiI6IFsNCiAgICB7DQogICAgICAibGFuZyI6ICJlbiIsDQogICAgICAiZGVzYyI6ICJUaGUgcHJpbWFyeSBpbW11bml6YXRpb24gbWF5IGhhdmUgYmVlbiBhZG1pbmlzdGVyZWQgMjcwIGRheXMgYWdvIGF0IG1vc3QuIEJvb3N0ZXIgdmFjY2luYXRpb25zIGFyZSB2YWxpZCBmb3IgYW4gdW5saW1pdGVkIHBlcmlvZC4iDQogICAgfSwNCiAgICB7DQogICAgICAibGFuZyI6ICJkZSIsDQogICAgICAiZGVzYyI6ICJTZWl0IGRlbSBBYnNjaGx1c3MgZGVyIEdydW5kaW1tdW5pc2llcnVuZyBkw7xyZmVuIG1heGltYWwgMjcwIFRhZ2UgdmVyZ2FuZ2VuIHNlaW4uIEVpbmUgQXVmZnJpc2NoaW1wZnVuZyBpc3QgdW5iZWdyZW56dCBnw7xsdGlnLiINCiAgICB9LA0KICAgIHsNCiAgICAgICJsYW5nIjogImZyIiwNCiAgICAgICJkZXNjIjogIkzigJlpbW11bmlzYXRpb24gZGUgYmFzZSBuZSBkb2l0IHBhcyByZW1vbnRlciDDoCBwbHVzIGRlIDI3MCBqb3Vycy4gVW4gdmFjY2luIGRlIHJhcHBlbCBlc3QgdmFsYWJsZSBzYW5zIGxpbWl0ZXMuIg0KICAgIH0sDQogICAgew0KICAgICAgImxhbmciOiAiZXMiLA0KICAgICAgImRlc2MiOiAiUHVlZGVuIGhhYmVyIHBhc2FkbyAyNzAgZMOtYXMgY29tbyBtw6F4aW1vIGRlc2RlIGxhIGlubXVuaXphY2nDs24gZGUgYmFzZS4gTGEgdmFjdW5hIGRlIHJlZnVlcnpvIHRpZW5lIHVuYSB2YWxpZGV6IGluZGVmaW5pZGEuIg0KICAgIH0sDQogICAgew0KICAgICAgImxhbmciOiAiaXQiLA0KICAgICAgImRlc2MiOiAiRGEEggPobGzigJllZmZldHR1YXppb25lIGRlbCBjaWNsbyB2YWNjaW5hbGUgcHJpbWFyaW8gcHXDsiBlc3NlcmUgdHJhc2NvcnNvIHVuIG1hc3NpbW8gZGkgMjcwIGdpb3JuaS4gTGEgZG9zZSBkaSByaWNoaWFtbyBoYSB2YWxpZGl0w6AgaWxsaW1pdGF0YS4iDQogICAgfQ0KICBdLA0KICAiVmFsaWRGcm9tIjogIjIwMjItMDItMTRUMDA6MDA6MDBaIiwNCiAgIlZhbGlkVG8iOiAiMjAzMC0wNi0wMVQwMDowMDowMFoiLA0KICAiQWZmZWN0ZWRGaWVsZHMiOiBbDQogICAgInYuMCIsDQogICAgInYuMC5kdCIsDQogICAgInYuMC5zZCIsDQogICAgInYuMC5kbiINCiAgXSwNCiAgIkxvZ2ljIjogew0KICAgICJpZiI6IFsNCiAgICAgIHsNCiAgICAgICAgInZhciI6ICJwYXlsb2FkLnYuMCINCiAgICAgIH0sDQogICAgICB7DQogICAgICAgICJpZiI6IFsNCiAgICAgICAgICB7DQogICAgICAgICAgICAibm90LWFmdGVyIjogWw0KICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgInBsdXNUaW1lIjogWw0KICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAidmFyIjogImV4dGVybmFsLnZhbGlkYXRpb25DbG9jayINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAwLA0KICAgICAgICAgICAgICAgICAgImRheSINCiAgICAgICAgICAgICAgICBdDQogICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAicGx1c1RpbWUiOiBbDQogICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICJ2YXIiOiAicGF5bG9hZC52LjAuZHQiDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgMjcwLA0KICAgICAgICAgICAgICAgICAgImRheSINCiAgICAgICAgICAgICAgICBdDQogICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIF0NCiAgICAgICAgICB9LA0KICAgICAgICAgIHRydWUsDQogICAgICAgICAgew0KICAgICAgICAgICAgImlmIjogWw0KICAgICAgIASCAg8gICAgICAgew0KICAgICAgICAgICAgICAgICI+IjogWw0KICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAidmFyIjogInBheWxvYWQudi4wLmRuIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgIDINCiAgICAgICAgICAgICAgICBdDQogICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgIHRydWUsDQogICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAiPiI6IFsNCiAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgInZhciI6ICJwYXlsb2FkLnYuMC5kbiINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICJ2YXIiOiAicGF5bG9hZC52LjAuc2QiDQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgXQ0KICAgICAgICAgICAgICB9DQogICAgICAgICAgICBdDQogICAgICAgICAgfQ0KICAgICAgICBdDQogICAgICB9LA0KICAgICAgdHJ1ZQ0KICAgIF0NCiAgfQ0KfQAAAAAAAKCAMIICuzCCAmKgAwIBAgIUdXK9JWgnVG6EuvuksBTVVaJMxOcwCgYIKoZIzj0EAwIwgdMxJzAlBgkqhkiG9w0BCQEWGERMLWNhby1jd2FAdC1zeXN0ZW1zLmNvbTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkhlc3NlbjEaMBgGA1UEBwwRRnJhbmtmdXJ0IGFtIE1haW4xJTAjBgNVBAoMHFQtU3lzdGVtcyBJbnRlcm5hdGlvbmFsIEdtYkgxFjAUBgNVBAsMDUNXQU9wZXJhdGlvbnMxLzAtBgNVBAMMJmRpc3RyaWJ1dGlvbi1jZmY0ZjcxNDcyNjAuZGNjLXJ1bGVzLmRlMB4XDTIxMDYyNTEwMDM0MFoXDTIyMDYyNTEwMDM0MFowgdMxJzAlBgkqhkiG9w0BCQEWGERMLWNhby1jd2FAdC1zeXN0ZW1zLmNvbTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkhlc3NlbjEaMBgGA1UEBwwRRnJhbmtmdXJ0IGFtIE1haW4xJTAjBgNVBAoMHFQtU3lzdGVtcyBJbnRlcm5hdGlvbmFsIEdtYkgxFjAUBgNVBAsMDUNXQU9wZXJhdGlvbnMxLzAtBgNVBAMMJmRpc3RyaWJ1dGlvbi1jZmY0ZjcxNDcyNjAuZGNjLXJ1bGVzLmRlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcEr90JMtQlYDOSZ/sQbIS3RZIPtnTfEC2TpyZzTGQNSr0+kw64cuG6/Ka3GUJn98uBE7qAUKf8bBWiw7a9f7v6MSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIHVhm4hd677REItCK3fSi2T9PFyEDg8TA5C72Be2BS3KAiAKy2pvIeYqIi80jZZyIckS5Oieqb7JvClQr/fwg/slRQAAMYIB8jCCAe4CAQEwgewwgdMxJzAlBgkqhkiG9w0BCQEWGERMLWNhby1jd2FAdC1zeXN0ZW1zLmNvbTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkhlc3NlbjEaMBgGA1UEBwwRRnJhbmtmdXJ0IGFtIE1haW4xJTAjBgNVBAoMHFQtU3lzdGVtcyBJbnRlcm5hdGlvbmFsIEdtYkgxFjAUBgNVBAsMDUNXQU9wZXJhdGlvbnMxLzAtBgNVBAMMJmRpc3RyaWJ1dGlvbi1jZmY0ZjcxNDcyNjAuZGNjLXJ1bGVzLmRlAhR1cr0laCdUboS6+6SwFNVVokzE5zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMjAyMTExNTMxMjRaMCoGCSqGSIb3DQEJNDEdMBswDQYJYIZIAWUDBAIBBQChCgYIKoZIzj0EAwIwLwYJKoZIhvcNAQkEMSIEIGkb6qcsaHQi9X3iHG7h5Tl4E6Y8JJpCLLxzXxS8uTMoMAoGCCqGSM49BAMCBEcwRQIhAPfNXC8hL1NIDT5dQiJy1mT5/xl2khV7xBYTobBfWW75AiAL3o4XxJwHZRihVQFz46xVCchtWgYHpY9ziWP1dP9mqgAAAAAAAA=="}],"RR-DE-0002":[{"version":"1.0.3","validFrom":"2022-02-18T17:30:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsNCiAgIklkZW50aWZpZXIiOiAiUlItREUtMDAwMiIsDQogICJUeXBlIjogIkFjY2VwdGFuY2UiLA0KICAiQ291bnRyeSI6ICJERSIsDQogICJWZXJzaW9uIjogIjEuMC4zIiwNCiAgIlNjaGVtYVZlcnNpb24iOiAiMS4wLjAiLA0KICAiRW5naW5lIjogIkNFUlRMT0dJQyIsDQogICJFbmdpbmVWZXJzaW9uIjogIjAuNy41IiwNCiAgIkNlcnRpZmljYXRlVHlwZSI6ICJSZWNvdmVyeSIsDQogICJEZXNjcmlwdGlvbiI6IFsNCiAgICB7DQogICAgICAibGFuZyI6ICJlbiIsDQogICAgICAiZGVzYyI6ICJUaGUgcG9zaXRpdmUgTkFBIHRlc3QgcmVzdWx0IChlLmcuLCBQQ1IpIG11c3QgYmUgbm8gb2xkZXIgdGhhbiA5MCBkYXlzLiINCiAgICB9LA0KICAgIHsNCiAgICAgICJsYW5nIjogImRlIiwNCiAgICAgICJkZXNjIjogIkRlciBwb3NpdGl2ZSBOQUEtVGVzdCAoei5CLiBQQ1IpIGRhcmYgbWF4aW1hbCA5MCBUYWdlIHp1csO8Y2tsaWVnZW4uIg0KICAgIH0sDQogICAgew0KICAgICAgImxhbmciOiAiZnIiLA0KICAgICAgImRlc2MiOiAiTGUgdGVzdCBUQU4gcG9zaXRpZiAocC4gZXguIFBDUikgbmUgZG9pdCBwYXMgZGF0ZXIgZGUgcGx1cyBkZSA5MCBqb3Vycy4iDQogICAgfSwNCiAgICB7DQogICAgICAibGFuZyI6ICJlcyIsDQogICAgICAiZGVzYyI6ICJEZWJlbiBoYWJlciB0cmFuc2N1cnJpZG8gOTAgZMOtYXMgY29tbyBtw6F4aW1vIGRlc2RlIHF1ZSB1bmEgcHJ1ZWJhIGRlIGFtcGxpZmljYWNpw7NuIGRlIMOhY2lkb3MgbnVjbGVpY29zIChwb3IgZWplbXBsbywgdW5hIFBDUikgZGllc2UgcG9zaXRpdm8uIg0KICAgIH0sDQogICAgew0KICAgICAgImxhbmciOiAiaXQiLA0KICAgICAgImRlc2MiOiAiSWwgTkFBVCBwb3NpdGl2byAoYWQgZXMuIHRlc3QgUFJDKSBkZXZlIHJpc2FsaXJlIG1hc3NpbW8gYSA5MCBnaW9ybmkgZmEuIg0KICAgIH0NCiAgXSwNCiAgIlZhbGlkRnJvbSI6ICIyMDIyLTAyLTE4VDE2OjMwOjAwWiIsDQogICJWYWxpZFRvIjogIjIwMzAEggJgLTA2LTAxVDAwOjAwOjAwWiIsDQogICJBZmZlY3RlZEZpZWxkcyI6IFsNCiAgICAici4wIiwNCiAgICAici4wLmZyIg0KICBdLA0KICAiTG9naWMiOiB7DQogICAgImlmIjogWw0KICAgICAgew0KICAgICAgICAidmFyIjogInBheWxvYWQuci4wIg0KICAgICAgfSwNCiAgICAgIHsNCiAgICAgICAgIm5vdC1hZnRlciI6IFsNCiAgICAgICAgICB7DQogICAgICAgICAgICAicGx1c1RpbWUiOiBbDQogICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAidmFyIjogImV4dGVybmFsLnZhbGlkYXRpb25DbG9jayINCiAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgMCwNCiAgICAgICAgICAgICAgImRheSINCiAgICAgICAgICAgIF0NCiAgICAgICAgICB9LA0KICAgICAgICAgIHsNCiAgICAgICAgICAgICJwbHVzVGltZSI6IFsNCiAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICJ2YXIiOiAicGF5bG9hZC5yLjAuZnIiDQogICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgIDkwLA0KICAgICAgICAgICAgICAiZGF5Ig0KICAgICAgICAgICAgXQ0KICAgICAgICAgIH0NCiAgICAgICAgXQ0KICAgICAgfSwNCiAgICAgIHRydWUNCiAgICBdDQogIH0NCn0AAAAAAACggDCCArswggJioAMCAQICFHVyvSVoJ1RuhLr7pLAU1VWiTMTnMAoGCCqGSM49BAMCMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTAeFw0yMTA2MjUxMDAzNDBaFw0yMjA2MjUxMDAzNDBaMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHBK/dCTLUJWAzkmf7EGyEt0WSD7Z03xAtk6cmc0xkDUq9PpMOuHLhuvymtxlCZ/fLgRO6gFCn/GwVosO2vX+7+jEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNHADBEAiB1YZuIXeu+0RCLQit30otk/TxchA4PEwOQu9gXtgUtygIgCstqbyHmKiIvNI2WciHJEuTonqm+ybwpUK/38IP7JUUAADGCAfMwggHvAgEBMIHsMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZQIUdXK9JWgnVG6EuvuksBTVVaJMxOcwDQYJYIZIAWUDBAIBBQCggZUwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjIwMjE2MTQ1MTIzWjAqBgkqhkiG9w0BCTQxHTAbMA0GCWCGSAFlAwQCAQUAoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJBDEiBCAfc6dcrPYP3oKpfqTROlOnxoiiUbW/qw4Gv3dT12K+WTAKBggqhkjOPQQDAgRIMEYCIQCm8/xMCf7fZCGsP7qgmdTZ7u3hcdCOGK5qChI0bv3ONAIhALFKS6CmsFLdyZwpwK+htpYh8fsu9cmQQ8Kq2s3pJtphAAAAAAAA"}],"VR-DE-0003":[{"version":"1.0.6","validFrom":"2022-02-17T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsNCiAgIklkZW50aWZpZXIiOiAiVlItREUtMDAwMyIsDQogICJUeXBlIjogIkFjY2VwdGFuY2UiLA0KICAiQ291bnRyeSI6ICJERSIsDQogICJWZXJzaW9uIjogIjEuMC42IiwNCiAgIlNjaGVtYVZlcnNpb24iOiAiMS4wLjAiLA0KICAiRW5naW5lIjogIkNFUlRMT0dJQyIsDQogICJFbmdpbmVWZXJzaW9uIjogIjAuNy41IiwNCiAgIkNlcnRpZmljYXRlVHlwZSI6ICJWYWNjaW5hdGlvbiIsDQogICJEZXNjcmlwdGlvbiI6IFsNCiAgICB7DQogICAgICAibGFuZyI6ICJlbiIsDQogICAgICAiZGVzYyI6ICJBdCBsZWFzdCAxNCBkYXlzIG11c3QgaGF2ZSBlbGFwc2VkIHNpbmNlIGNvbXBsZXRpbmcgdGhlIHByaW1hcnkgY291cnNlIG9mIGltbXVuaXphdGlvbi4gQSBib29zdGVyIHNob3Qgb3IgdmFjY2luYXRpb24gb2Ygc29tZW9uZSB3aG8gcmVjb3ZlcmVkIGZyb20gQ09WSUQtMTkgaXMgdmFsaWQgaW1tZWRpYXRlbHkuIg0KICAgIH0sDQogICAgew0KICAgICAgImxhbmciOiAiZGUiLA0KICAgICAgImRlc2MiOiAiU2VpdCBkZW0gQWJzY2hsdXNzIGRlciBHcnVuZGltbXVuaXNpZXJ1bmcgbcO8c3NlbiBtaW5kZXN0ZW5zIDE0IFRhZ2UgdmVyZ2FuZ2VuIHNlaW4uIEVpbmUgQXVmZnJpc2NoaW1wZnVuZyBvZGVyIGVpbmUgR2VuZXNlbmVuaW1wZnVuZyBpc3Qgc29mb3J0IGfDvGx0aWcuIg0KICAgIH0sDQogICAgew0KICAgICAgImxhbmciOiAiZnIiLA0KICAgICAgImRlc2MiOiAiVW5lIGZvaXMgbGEgcHJpbW8tdmFjY2luYXRpb24gZWZmZWN0dcOpZSwgaWwgZmF1dCBhdHRlbmRyZSBhdSBtb2lucyAxNCBqb3Vycy4gVW5lIGluamVjdGlvbiBkZSByYXBwZWwgb3UgdW5lIGluamVjdGlvbiBhcHLDqHMgdW5lIGd1w6lyaXNvbiBlc3QgdmFsYWJsZSBpbW3DqWRpYXRlbWVudC4iDQogICAgfSwNCiAgICB7DQogICAgICAibGFuZyI6ICJlcyIsDQogICAgICAiZGVzYyI6ICJEZWJlbiBoYWJlciB0cmFuc2N1cnJpZG8gYWwgbWVub3MgMTQgZMOtYXMgZGVzZGUgbGEgZmkEggPobmFsaXphY2nDs24gZGUgbGEgaW5tdW5pemFjacOzbiBiw6FzaWNhLiBVbmEgdmFjdW5hIGRlIHJlZnVlcnpvIG8gdW5hIHZhY3VuYSBkZSBjb252YWxlY2VuY2lhIHNlcsOhIHbDoWxpZGEgZGUgZm9ybWEgaW5tZWRpYXRhLiINCiAgICB9LA0KICAgIHsNCiAgICAgICJsYW5nIjogIml0IiwNCiAgICAgICJkZXNjIjogIkRldm9ubyBlc3NlcmUgcGFzc2F0aSBtaW5pbW8gMTQgZ2lvcm5pIGRhbGxhIHZhY2NpbmF6aW9uZSBmaW5hbGUgbmVjZXNzYXJpYSBwZXIgbOKAmWltbXVuaXp6YXppb25lIGRpIGJhc2UuIElsIHJpY2hpYW1vIGRlbCB2YWNjaW5vIG8gaWwgdmFjY2lubyBwZXIgbGUgcGVyc29uZSBjaGUgc29ubyBndWFyaXRlIGRhbCB2aXJ1cyDDqCB2YWxpZG8gZGEgc3ViaXRvLiINCiAgICB9DQogIF0sDQogICJWYWxpZEZyb20iOiAiMjAyMi0wMi0xN1QwMDowMDowMFoiLA0KICAiVmFsaWRUbyI6ICIyMDMwLTA2LTAxVDAwOjAwOjAwWiIsDQogICJBZmZlY3RlZEZpZWxkcyI6IFsNCiAgICAidi4wIiwNCiAgICAidi4wLmR0IiwNCiAgICAidi4wLmRuIiwNCiAgICAidi4wLnNkIg0KICBdLA0KICAiTG9naWMiOiB7DQogICAgImlmIjogWw0KICAgICAgew0KICAgICAgICAidmFyIjogInBheWxvYWQudi4wIg0KICAgICAgfSwNCiAgICAgIHsNCiAgICAgICAgImlmIjogWw0KICAgICAgICAgIHsNCiAgICAgICAgICAgICJub3QtYmVmb3JlIjogWw0KICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgInBsdXNUaW1lIjogWw0KICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAidmFyIjogImV4dGVybmFsLnZhbGlkYXRpb25DbG9jayINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAwLA0KICAgICAgICAgICAgICAgICAgImRheSINCiAgICAgICAgICAgICAgICBdDQogICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAicGx1c1RpbWUiOiBbDQogICAgICAgICAgIASCA+ggICAgICAgew0KICAgICAgICAgICAgICAgICAgICAidmFyIjogInBheWxvYWQudi4wLmR0Ig0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgIDE1LA0KICAgICAgICAgICAgICAgICAgImRheSINCiAgICAgICAgICAgICAgICBdDQogICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIF0NCiAgICAgICAgICB9LA0KICAgICAgICAgIHRydWUsDQogICAgICAgICAgew0KICAgICAgICAgICAgImlmIjogWw0KICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgIj4iOiBbDQogICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICJ2YXIiOiAicGF5bG9hZC52LjAuZG4iDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgMg0KICAgICAgICAgICAgICAgIF0NCiAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgdHJ1ZSwNCiAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICJpZiI6IFsNCiAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgImFuZCI6IFsNCiAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAiPT09IjogWw0KICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhciI6ICJwYXlsb2FkLnYuMC5kbiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgMQ0KICAgICAgICAgICAgICAgICAgICAgICAgXQ0KICAgICAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgIj09PSI6IFsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YXIiOiAicGF5bG9hZC52LjAuZG4iDQogICAgICAgICAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgBIICWSAgICAgICAgICAgICAgICAgICAgICAgInZhciI6ICJwYXlsb2FkLnYuMC5zZCINCiAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgXQ0KICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgXQ0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgIHRydWUsDQogICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICI+IjogWw0KICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YXIiOiAicGF5bG9hZC52LjAuZG4iDQogICAgICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFyIjogInBheWxvYWQudi4wLnNkIg0KICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgXQ0KICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIF0NCiAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgXQ0KICAgICAgICAgIH0NCiAgICAgICAgXQ0KICAgICAgfSwNCiAgICAgIHRydWUNCiAgICBdDQogIH0NCn0AAAAAAACggDCCArswggJioAMCAQICFHVyvSVoJ1RuhLr7pLAU1VWiTMTnMAoGCCqGSM49BAMCMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTAeFw0yMTA2MjUxMDAzNDBaFw0yMjA2MjUxMDAzNDBaMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHBK/dCTLUJWAzkmf7EGyEt0WSD7Z03xAtk6cmc0xkDUq9PpMOuHLhuvymtxlCZ/fLgRO6gFCn/GwVosO2vX+7+jEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNHADBEAiB1YZuIXeu+0RCLQit30otk/TxchA4PEwOQu9gXtgUtygIgCstqbyHmKiIvNI2WciHJEuTonqm+ybwpUK/38IP7JUUAADGCAfIwggHuAgEBMIHsMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZQIUdXK9JWgnVG6EuvuksBTVVaJMxOcwDQYJYIZIAWUDBAIBBQCggZUwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjIwMjE0MTQzMzIzWjAqBgkqhkiG9w0BCTQxHTAbMA0GCWCGSAFlAwQCAQUAoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJBDEiBCBgve0ZJHfvteZooChUGiYPLSekGP705RkIAsL+qO9QKzAKBggqhkjOPQQDAgRHMEUCIDxdxELwsIjM8BuBUujo0JCDUemMa6mcJJv93Y3xuvjxAiEA9MwXKws+FmN+bW0HkE//MqiWMHuig5PUgPMbCvJ2QwEAAAAAAAA="}],"RR-DE-0001":[{"version":"1.0.1","validFrom":"2021-07-19T02:00:00+02:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsKICAiSWRlbnRpZmllciI6ICJSUi1ERS0wMDAxIiwKICAiVHlwZSI6ICJBY2NlcHRhbmNlIiwKICAiQ291bnRyeSI6ICJERSIsCiAgIlZlcnNpb24iOiAiMS4wLjEiLAogICJTY2hlbWFWZXJzaW9uIjogIjEuMC4wIiwKICAiRW5naW5lIjogIkNFUlRMT0dJQyIsCiAgIkVuZ2luZVZlcnNpb24iOiAiMC43LjUiLAogICJDZXJ0aWZpY2F0ZVR5cGUiOiAiUmVjb3ZlcnkiLAogICJEZXNjcmlwdGlvbiI6IFsKICAgIHsKICAgICAgImxhbmciOiAiZW4iLAogICAgICAiZGVzYyI6ICJUaGUgcG9zaXRpdmUgTkFBIHRlc3QgcmVzdWx0IChlLmcuLCBQQ1IpIG11c3QgYmUgb2xkZXIgdGhhbiAyOCBkYXlzLiIKICAgIH0sCiAgICB7CiAgICAgICJsYW5nIjogImRlIiwKICAgICAgImRlc2MiOiAiRGVyIHBvc2l0aXZlIE5BQS1UZXN0ICh6LkIuIFBDUikgbXVzcyBtaW5kZXN0ZW5zIDI4IFRhZ2UgenVyw7xja2xpZWdlbi4iCiAgICB9LAogICAgewogICAgICAibGFuZyI6ICJmciIsCiAgICAgICJkZXNjIjogIkxlIHRlc3QgVEFOIHBvc2l0aWYgKHAuIGV4LiBQQ1IpIGRvaXQgZGF0ZXIgZOKAmGF1IG1vaW5zIDI4IGpvdXJzLiIKICAgIH0sCiAgICB7CiAgICAgICJsYW5nIjogImVzIiwKICAgICAgImRlc2MiOiAiRGViZW4gaGFiZXIgdHJhbnNjdXJyaWRvIGNvbW8gbcOtbmltbyAyOCBkw61hcyBkZXNkZSBxdWUgdW5hIHBydWViYSBkZSBhbXBsaWZpY2FjacOzbiBkZSDDoWNpZG9zIG51Y2xlaWNvcyAocG9yIGVqZW1wbG8sIHVuYSBQQ1IpIGRpZXNlIHBvc2l0aXZvLiIKICAgIH0sCiAgICB7CiAgICAgICJsYW5nIjogIml0IiwKICAgICAgImRlc2MiOiAiSWwgTkFBVCBwb3NpdGl2byAoYWQgZXMuIHRlc3QgUFJDKSBkZXZlIHJpc2FsaXJlIG1pbmltbyBhIDI4IGdpb3JuaSBmYS4iCiAgICB9CiAgXSwKICAiVmFsaWRGcm9tIjogIjIwMjEtMDctMTlUMDA6MDA6MDBaIiwKICAiVmFsaWRUbyI6ICIyMDMwLTA2LTAxVDAwOjAwOjAwWiIsCiAgIkFmZmVjdGVkRmllbGRzIjoEggIYIFsKICAgICJyLjAiLAogICAgInIuMC5mciIKICBdLAogICJMb2dpYyI6IHsKICAgICJpZiI6IFsKICAgICAgewogICAgICAgICJ2YXIiOiAicGF5bG9hZC5yLjAiCiAgICAgIH0sCiAgICAgIHsKICAgICAgICAibm90LWJlZm9yZSI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgInBsdXNUaW1lIjogWwogICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJ2YXIiOiAiZXh0ZXJuYWwudmFsaWRhdGlvbkNsb2NrIgogICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgMCwKICAgICAgICAgICAgICAiZGF5IgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAicGx1c1RpbWUiOiBbCiAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgInZhciI6ICJwYXlsb2FkLnIuMC5mciIKICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgIDI4LAogICAgICAgICAgICAgICJkYXkiCiAgICAgICAgICAgIF0KICAgICAgICAgIH0KICAgICAgICBdCiAgICAgIH0sCiAgICAgIHRydWUKICAgIF0KICB9Cn0AAAAAAACggDCCArswggJioAMCAQICFHVyvSVoJ1RuhLr7pLAU1VWiTMTnMAoGCCqGSM49BAMCMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTAeFw0yMTA2MjUxMDAzNDBaFw0yMjA2MjUxMDAzNDBaMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHBK/dCTLUJWAzkmf7EGyEt0WSD7Z03xAtk6cmc0xkDUq9PpMOuHLhuvymtxlCZ/fLgRO6gFCn/GwVosO2vX+7+jEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNHADBEAiB1YZuIXeu+0RCLQit30otk/TxchA4PEwOQu9gXtgUtygIgCstqbyHmKiIvNI2WciHJEuTonqm+ybwpUK/38IP7JUUAADGCAfIwggHuAgEBMIHsMIHTMScwJQYJKoZIhvcNAQkBFhhETC1jYW8tY3dhQHQtc3lzdGVtcy5jb20xCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xGjAYBgNVBAcMEUZyYW5rZnVydCBhbSBNYWluMSUwIwYDVQQKDBxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMRYwFAYDVQQLDA1DV0FPcGVyYXRpb25zMS8wLQYDVQQDDCZkaXN0cmlidXRpb24tY2ZmNGY3MTQ3MjYwLmRjYy1ydWxlcy5kZQIUdXK9JWgnVG6EuvuksBTVVaJMxOcwDQYJYIZIAWUDBAIBBQCggZUwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjEwNzE2MDgzNTQyWjAqBgkqhkiG9w0BCTQxHTAbMA0GCWCGSAFlAwQCAQUAoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJBDEiBCBHE9tqfTPfkZ3o+vvuAqHpyqH1n/wMBsvd8ytH8kQXEDAKBggqhkjOPQQDAgRHMEUCIQCel5hSx+sSEyGjdeltbuq9fGYx3fcKl6FYctVA0oiP3wIgWFOXuFt5Zg4m+6j8f5Pvc1Zmh32YPIDQue8Y+7Ni6rUAAAAAAAA="}],"VR-DE-0002":[{"version":"1.0.3","validFrom":"2022-03-20T01:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIID6HsKICAiSWRlbnRpZmllciI6ICJWUi1ERS0wMDAyIiwKICAiVHlwZSI6ICJBY2NlcHRhbmNlIiwKICAiQ291bnRyeSI6ICJERSIsCiAgIlZlcnNpb24iOiAiMS4wLjMiLAogICJTY2hlbWFWZXJzaW9uIjogIjEuMC4wIiwKICAiRW5naW5lIjogIkNFUlRMT0dJQyIsCiAgIkVuZ2luZVZlcnNpb24iOiAiMC43LjUiLAogICJDZXJ0aWZpY2F0ZVR5cGUiOiAiVmFjY2luYXRpb24iLAogICJEZXNjcmlwdGlvbiI6IFsKICAgIHsKICAgICAgImxhbmciOiAiZW4iLAogICAgICAiZGVzYyI6ICJPbmx5IHRoZSBmb2xsb3dpbmcgdmFjY2luZXMgYXJlIGFjY2VwdGVkOiBBc3RyYVplbmVjYSwgQmlvbnRlY2gsIEphbnNzZW4sIE1vZGVybmEsIE5vdmF2YXguIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiZGUiLAogICAgICAiZGVzYyI6ICJOdXIgZGllIGZvbGdlbmRlbiBJbXBmc3RvZmZlIHdlcmRlbiBha3plcHRpZXJ0OiBBc3RyYVplbmVjYSwgQmlvbnRlY2gsIEphbnNzZW4sIE1vZGVybmEsIE5vdmF2YXguIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiZnIiLAogICAgICAiZGVzYyI6ICJTZXVscyBsZXMgdmFjY2lucyBzdWl2YW50cyBzb250IGFjY2VwdMOpczogQXN0cmFaZW5lY2EsIEJpb250ZWNoLCBKYW5zc2VuLCBNb2Rlcm5hLCBOb3ZhdmF4LiIKICAgIH0sCiAgICB7CiAgICAgICJsYW5nIjogImVzIiwKICAgICAgImRlc2MiOiAiU29sbyBzZSBhY2VwdGFuIGxhcyBzaWd1aWVudGVzIHZhY3VuYXM6IEFzdHJhWmVuZWNhLCBCaW9udGVjaCwgSmFuc3NlbiwgTW9kZXJuYSB5IE5vdmF2YXguIgogICAgfSwKICAgIHsKICAgICAgImxhbmciOiAiaXQiLAogICAgICAiZGVzYyI6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"}],"VR-DE-0001":[{"version":"1.0.2","validFrom":"2022-03-11T15:00:00+01:00","validTo":"2030-06-01T02:00:00+02:00","cms":"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"}]} \ No newline at end of file diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/countrylist.json b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/countrylist.json new file mode 100644 index 00000000..e46613fc --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/resources/countrylist.json @@ -0,0 +1 @@ +["AT", "DE"] \ No newline at end of file diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/SchedulingConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/SchedulingConfig.java index 1a857e52..71f4ca78 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/SchedulingConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/SchedulingConfig.java @@ -17,11 +17,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; @Configuration @EnableScheduling +@Profile("!test") @EnableSchedulerLock(defaultLockAtMostFor = "PT10M") public class SchedulingConfig { diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java index 19cb9d2b..646a079f 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java @@ -11,16 +11,19 @@ package ch.admin.bag.covidcertificate.backend.verifier.ws.config; import ch.admin.bag.covidcertificate.backend.verifier.data.AppTokenDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.RevokedCertDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.ValueSetDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.VerifierDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcAppTokenDataServiceImpl; +import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcForeignRulesDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcRevokedCertDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcValueSetDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcVerifierDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.util.CacheUtil; import ch.admin.bag.covidcertificate.backend.verifier.ws.client.RevocationListSyncer; import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.DcgaController; +import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.ForeignRulesControllerV2; import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.KeyController; import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.KeyControllerV2; import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.RevocationListController; @@ -224,12 +227,24 @@ public VerificationRulesControllerV2 verificationRulesControllerV2( valueSetDataService, getDisabledVerificationModes()); } + @Bean + public ForeignRulesControllerV2 foreignRulesControllerV2( + ValueSetDataService valueSetDataService, ForeignRulesDataService foreignRulesDataService) throws IOException { + return new ForeignRulesControllerV2( + valueSetDataService, foreignRulesDataService); + } + @Bean public ValueSetsController valueSetsController(ValueSetDataService valueSetDataService) throws IOException { return new ValueSetsController(valueSetDataService); } + @Bean + public ForeignRulesDataService foreignRulesDataService(DataSource dataSource){ + return new JdbcForeignRulesDataServiceImpl(dataSource); + } + @Bean public ValueSetDataService valueSetDataService( DataSource dataSource, @Value("${value-set.max-history:10}") int maxHistory) { diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java new file mode 100644 index 00000000..97425d18 --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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.data.ForeignRulesDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.ValueSetDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.util.CacheUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.WebRequest; + +@Controller +@RequestMapping("trust/v2") +public class ForeignRulesControllerV2 { + + private static final Logger logger = LoggerFactory.getLogger(ForeignRulesControllerV2.class); + + private final ValueSetDataService valueSetDataService; + private final ForeignRulesDataService foreignRulesDataService; + + public ForeignRulesControllerV2( + ValueSetDataService valueSetDataService, ForeignRulesDataService dataService) { + this.foreignRulesDataService = dataService; + this.valueSetDataService = valueSetDataService; + } + + @GetMapping(value = "/countries") + public @ResponseBody ResponseEntity> getCountries(WebRequest request) { + return ResponseEntity.ok(foreignRulesDataService.getCountries()); + } + + @GetMapping(value = "/foreignRules/{country}") + public @ResponseBody ResponseEntity getForeignRules( + @PathVariable("country") String country) { + ObjectMapper mapper = new ObjectMapper(); + Map result = new HashMap<>(); + result.put("validDuration", 172800000); + + // Add rules to output + var foreignRules = foreignRulesDataService.getRulesForCountry(country); + + var rules = + foreignRules.stream() + .map( + rule -> { + try { + return mapper.readTree(rule.getContent()); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + result.put("rules", rules); + + // Add valuesets to output + var allIds = valueSetDataService.findAllValueSetIds(); + HashMap> valueSets = new HashMap<>(); + for (var id : allIds) { + var valueSet = valueSetDataService.findLatestValueSet(id); + if (valueSet != null) { + try { + var entryJson = mapper.readTree(valueSet); + var fieldNameIterator = entryJson.get("valueSetValues").fieldNames(); + var valueSetValues = new ArrayList(); + while (fieldNameIterator.hasNext()) { + valueSetValues.add(fieldNameIterator.next()); + } + valueSets.put(id, valueSetValues); + } catch (Exception ex) { + logger.error("Serving Rules failed", ex); + } + } + } + result.put("valueSets", valueSets); + + return ResponseEntity.ok(result); + } + + private HttpHeaders getVerificationRulesHeaders(Instant now) { + return CacheUtil.createExpiresHeader( + CacheUtil.roundToNextVerificationRulesBucketStart(now)); + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/TestConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/TestConfig.java index 42e8e827..a19f8033 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/TestConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/TestConfig.java @@ -10,13 +10,22 @@ package ch.admin.bag.covidcertificate.backend.verifier.ws.config; +import static ch.admin.bag.covidcertificate.backend.verifier.ws.util.MockRulesContent.AT1_CONTENT; +import static ch.admin.bag.covidcertificate.backend.verifier.ws.util.MockRulesContent.AT2_CONTENT; + +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.RevokedCertDataService; +import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcForeignRulesDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.impl.JdbcRevokedCertDataServiceImpl; import ch.admin.bag.covidcertificate.backend.verifier.data.util.CacheUtil; +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.RevocationListController; import ch.admin.bag.covidcertificate.backend.verifier.ws.controller.RevocationListControllerV2; +import ch.admin.bag.covidcertificate.backend.verifier.ws.util.MockForeignRuleDataService; import ch.admin.bag.covidcertificate.backend.verifier.ws.utils.RestTemplateHelper; import java.time.Duration; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import javax.sql.DataSource; import org.flywaydb.core.Flyway; import org.slf4j.Logger; @@ -74,6 +83,12 @@ public RevokedCertDataService revokedCertDataService( return new JdbcRevokedCertDataServiceImpl(dataSource, revokedCertBatchSize); } + @Bean + @Override + public ForeignRulesDataService foreignRulesDataService(DataSource dataSource){ + return new MockForeignRuleDataService(); + } + @Value("${ws.revocation-list.retention-bucket-duration:PT6H}") public void setRevocationRetentionBucketDuration(Duration bucketDuration) { CacheUtil.REVOCATION_RETENTION_BUCKET_DURATION = bucketDuration; diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java new file mode 100644 index 00000000..35fa2eed --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; + +public class ForeignRulesControllerV2Test extends BaseControllerTest { + + protected MediaType acceptMediaType = MediaType.APPLICATION_JSON; + + private String atRulesUrl = "/trust/v2/foreignRules/AT"; + private String countryListUrl = "/trust/v2/countries"; + + @BeforeAll + static void setup(){ + + } + + @Test + public void countryListTest() throws Exception { + // get verification rules + MockHttpServletResponse response = + mockMvc.perform(get(countryListUrl).accept(acceptMediaType)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse(); + + // verify response + assertNotNull(response); + assertTrue(response.getContentAsString().contains("AT")); + assertTrue(response.getContentAsString().contains("DE")); + + } + + @Test + public void foreignRulesTest() throws Exception { + // get verification rules + MockHttpServletResponse response = + mockMvc.perform(get(atRulesUrl).accept(acceptMediaType)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse(); + + // verify response + assertNotNull(response); + + } + + @Test + @Disabled("calculating expected ETag from the file doesn't work for V2") + public void notModifiedTest() throws Exception { + /*String expectedEtag = EtagUtil.getSha1HashForFiles(true, PATH_TO_VERIFICATION_RULES); + + // get current etag + MockHttpServletResponse response = + mockMvc.perform( + get(verificationRulesUrl) + .accept(acceptMediaType) + .header(HttpHeaders.IF_NONE_MATCH, "random")) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse(); + + // verify etag + String etag = response.getHeader(HttpHeaders.ETAG); + assertEquals(expectedEtag, etag); + + // test not modified + mockMvc.perform( + get(verificationRulesUrl) + .accept(acceptMediaType) + .header(HttpHeaders.IF_NONE_MATCH, etag)) + .andExpect(status().isNotModified()) + .andReturn() + .getResponse();*/ + } + + @Override + protected String getUrlForSecurityHeadersTest() { + return atRulesUrl; + } + + @Override + protected MediaType getSecurityHeadersRequestMediaType() { + return this.acceptMediaType; + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java new file mode 100644 index 00000000..5a8850a6 --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java @@ -0,0 +1,59 @@ +package ch.admin.bag.covidcertificate.backend.verifier.ws.util; + +import static ch.admin.bag.covidcertificate.backend.verifier.ws.util.MockRulesContent.AT1_CONTENT; +import static ch.admin.bag.covidcertificate.backend.verifier.ws.util.MockRulesContent.AT2_CONTENT; + +import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; +import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MockForeignRuleDataService implements ForeignRulesDataService { + private final ForeignRule at1 = new ForeignRule(); + private final ForeignRule at2 = new ForeignRule(); + + public MockForeignRuleDataService(){ + at1.setId("GR-AT-0039"); + at1.setVersion("0.0.1"); + at1.setCountry("AT"); + at1.setValidUntil(LocalDateTime.now().plus(1, ChronoUnit.YEARS)); + at1.setValidFrom(LocalDateTime.now().minus(1, ChronoUnit.DAYS)); + at1.setContent(AT1_CONTENT); + + at2.setId("GR-AT-0039"); + at2.setVersion("0.0.2"); + at2.setCountry("AT"); + at2.setValidUntil(LocalDateTime.now().plus(1, ChronoUnit.YEARS)); + at2.setValidFrom(LocalDateTime.now().plus(1, ChronoUnit.DAYS)); + at2.setContent(AT2_CONTENT); + } + + @Override + public List getCountries() { + return Arrays.asList("AT", "DE"); + } + + @Override + public List getRulesForCountry(String country) { + if(country.equals("AT")){ + return Arrays.asList(at1, at2); + }else if(country.equals("DE")){ + return new ArrayList<>(); + }else{ + return new ArrayList<>(); + } + } + + @Override + public void insertRule(ForeignRule rule) { + + } + + @Override + public void removeRuleSet(String country) { + + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockRulesContent.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockRulesContent.java new file mode 100644 index 00000000..e81568fe --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockRulesContent.java @@ -0,0 +1,7 @@ +package ch.admin.bag.covidcertificate.backend.verifier.ws.util; + +public interface MockRulesContent { + String AT1_CONTENT = "{\"Identifier\":\"VR-AT-0000\",\"Type\":\"Acceptance\",\"Country\":\"AT\",\"Region\":\"AT\",\"Version\":\"1.0.12\",\"SchemaVersion\":\"1.0.0\",\"Engine\":\"CERTLOGIC\",\"EngineVersion\":\"0.7.5\",\"CertificateType\":\"Vaccination\",\"Description\":[{\"lang\":\"en\",\"desc\":\"At most one v-event.\"}],\"ValidFrom\":\"2021-11-29T00:00:00Z\",\"ValidTo\":\"2030-06-01T00:00:00Z\",\"AffectedFields\":[\"v.1\"],\"Logic\":{\"!\":[{\"var\":\"payload.v.1\"}]}}"; + String AT2_CONTENT = + "{\"Identifier\":\"GR-AT-0001\",\"Type\":\"Acceptance\",\"Country\":\"AT\",\"Region\":\"AT\",\"Version\":\"1.0.12\",\"SchemaVersion\":\"1.0.0\",\"Engine\":\"CERTLOGIC\",\"EngineVersion\":\"0.7.5\",\"CertificateType\":\"General\",\"Description\":[{\"lang\":\"en\",\"desc\":\"The \\\"disease or agent targeted\\\" must be COVID-19 of the value set list.\"}],\"ValidFrom\":\"2021-11-29T00:00:00Z\",\"ValidTo\":\"2030-06-01T00:00:00Z\",\"AffectedFields\":[\"r.0\",\"r.0.tg\",\"t.0\",\"t.0.tg\",\"v.0\",\"v.0.tg\"],\"Logic\":{\"and\":[{\"if\":[{\"var\":\"payload.r.0\"},{\"in\":[{\"var\":\"payload.r.0.tg\"},{\"var\":\"external.valueSets.disease-agent-targeted\"}]},true]},{\"if\":[{\"var\":\"payload.t.0\"},{\"in\":[{\"var\":\"payload.t.0.tg\"},{\"var\":\"external.valueSets.disease-agent-targeted\"}]},true]},{\"if\":[{\"var\":\"payload.v.0\"},{\"in\":[{\"var\":\"payload.v.0.tg\"},{\"var\":\"external.valueSets.disease-agent-targeted\"}]},true]}]}}"; +} From bdcb09dcb7a414554d0859490a3a85e1d89d1c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Fri, 25 Mar 2022 10:06:30 +0100 Subject: [PATCH 02/13] change foreign rules sync schedule --- .../verifier/sync/config/SyncSchedulingBaseConfig.java | 2 +- .../verifier/ws/controller/ForeignRulesControllerV2.java | 5 ++++- .../verifier/ws/controller/ForeignRulesControllerV2Test.java | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java index 55fe652f..01071f34 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java @@ -86,7 +86,7 @@ public void valueSetCleanCron() { valueSetDataService.deleteOldValueSets(); } - @Scheduled(cron = "${foreign-rules.sync.cron:0 0 0 ? * *}") + @Scheduled(cron = "${foreign-rules.sync.cron:0 39 * ? * *}") @SchedulerLock(name = "foreign_rules_sync", lockAtLeastFor = "PT15S") public void foreignRulesSyncCron() { LockAssert.assertLocked(); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index 97425d18..e33de50d 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -48,7 +48,7 @@ public ForeignRulesControllerV2( this.valueSetDataService = valueSetDataService; } - @GetMapping(value = "/countries") + @GetMapping(value = "/foreignRules") public @ResponseBody ResponseEntity> getCountries(WebRequest request) { return ResponseEntity.ok(foreignRulesDataService.getCountries()); } @@ -62,6 +62,9 @@ public ForeignRulesControllerV2( // Add rules to output var foreignRules = foreignRulesDataService.getRulesForCountry(country); + if(foreignRules.isEmpty()){ + return ResponseEntity.notFound().build(); + } var rules = foreignRules.stream() diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java index 35fa2eed..425f713e 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java @@ -25,7 +25,7 @@ public class ForeignRulesControllerV2Test extends BaseControllerTest { protected MediaType acceptMediaType = MediaType.APPLICATION_JSON; private String atRulesUrl = "/trust/v2/foreignRules/AT"; - private String countryListUrl = "/trust/v2/countries"; + private String countryListUrl = "/trust/v2/foreignRules"; @BeforeAll static void setup(){ From a45593d58aea72cadcf5778a42c2e9d289cd080f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Mon, 28 Mar 2022 10:15:10 +0200 Subject: [PATCH 03/13] foreign rules sync: remove old rules from DB --- .../sync/syncer/DgcForeignRulesSyncer.java | 39 ++++++++++++------- .../controller/ForeignRulesControllerV2.java | 25 +++++++++--- .../ForeignRulesControllerV2Test.java | 6 ++- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java index 66865695..89c6e1b9 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java @@ -14,6 +14,7 @@ import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; import ch.admin.bag.covidcertificate.backend.verifier.sync.utils.CmsUtil; import com.fasterxml.jackson.databind.JsonNode; +import com.sun.jdi.event.ExceptionEvent; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; @@ -21,6 +22,8 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.bouncycastle.cms.CMSException; import org.bouncycastle.operator.OperatorCreationException; import org.slf4j.Logger; @@ -43,19 +46,29 @@ public void sync() { List countries = dgcRulesClient.getCountries(); countries.remove("CH"); - //download data for the country - countries.forEach( - //this gives us an array of rule IDs - country -> dgcRulesClient.download(country) - //each rule ID has an array of rule versions - .forEach((ruleId, ruleVersions) -> (ruleVersions).forEach(ruleVersion -> { - //insert each rule version into the DB - try { - foreignRulesDataService.insertRule(decodeRule(ruleVersion)); - } catch (Exception e){ - logger.error("Failed to decode rule {}", ruleId, e); - } - }))); + + for(var country: countries){ + var rules = dgcRulesClient.download(country).entrySet().stream() + .map( + rule -> { + try { + return decodeRule(rule.getValue()); + } catch (Exception e) { + logger.error( + "Failed to decode rule {}", + rule.getKey(), + e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + foreignRulesDataService.removeRuleSet(country); + rules.forEach(foreignRulesDataService::insertRule); + if(rules.isEmpty()){ + logger.warn("No rules were downloaded or decoded for {}", country); + } + } var end = Instant.now(); logger.info( "Successfully downloaded foreign rules {} ms", diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index e33de50d..dbf7c531 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -13,9 +13,12 @@ import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.ValueSetDataService; import ch.admin.bag.covidcertificate.backend.verifier.data.util.CacheUtil; +import ch.admin.bag.covidcertificate.backend.verifier.ws.utils.EtagUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.security.NoSuchAlgorithmException; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -24,7 +27,9 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @@ -55,14 +60,14 @@ public ForeignRulesControllerV2( @GetMapping(value = "/foreignRules/{country}") public @ResponseBody ResponseEntity getForeignRules( - @PathVariable("country") String country) { + @PathVariable("country") String country, WebRequest request) { ObjectMapper mapper = new ObjectMapper(); Map result = new HashMap<>(); result.put("validDuration", 172800000); // Add rules to output var foreignRules = foreignRulesDataService.getRulesForCountry(country); - if(foreignRules.isEmpty()){ + if (foreignRules.isEmpty()) { return ResponseEntity.notFound().build(); } @@ -101,12 +106,20 @@ public ForeignRulesControllerV2( } } result.put("valueSets", valueSets); + String etag = ""; + try { + etag = EtagUtil.getSha1HashForStrings(true, mapper.writeValueAsString(result)); + } catch (JsonProcessingException | NoSuchAlgorithmException e) { + logger.error("Failed to calculate ETag for rules", e); + } + if (request.checkNotModified(etag)) { + return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); + } - return ResponseEntity.ok(result); + return ResponseEntity.ok().headers(getVerificationRulesHeaders()).body(result); } - private HttpHeaders getVerificationRulesHeaders(Instant now) { - return CacheUtil.createExpiresHeader( - CacheUtil.roundToNextVerificationRulesBucketStart(now)); + private HttpHeaders getVerificationRulesHeaders() { + return CacheUtil.createExpiresHeader(Instant.now().plus(48, ChronoUnit.HOURS)); } } diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java index 425f713e..b6bbc855 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java @@ -14,6 +14,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import ch.admin.bag.covidcertificate.backend.verifier.ws.utils.EtagUtil; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -63,9 +65,9 @@ public void foreignRulesTest() throws Exception { } @Test - @Disabled("calculating expected ETag from the file doesn't work for V2") + @Disabled public void notModifiedTest() throws Exception { - /*String expectedEtag = EtagUtil.getSha1HashForFiles(true, PATH_TO_VERIFICATION_RULES); + /*String expectedEtag = EtagUtil.getSha1HashForStrings() // get current etag MockHttpServletResponse response = From 4ec89b137ba7851c81d4d660e6d694652900edab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Mon, 28 Mar 2022 14:36:38 +0200 Subject: [PATCH 04/13] refactor rules sync and improve logging --- .../sync/syncer/DgcForeignRulesSyncer.java | 57 +++++++++++-------- .../sync/syncer/ForeignRulesSyncTest.java | 3 +- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java index 89c6e1b9..a96df776 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java @@ -14,13 +14,14 @@ import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; import ch.admin.bag.covidcertificate.backend.verifier.sync.utils.CmsUtil; import com.fasterxml.jackson.databind.JsonNode; -import com.sun.jdi.event.ExceptionEvent; +import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -40,42 +41,48 @@ public DgcForeignRulesSyncer( this.foreignRulesDataService = foreignRulesDataService; } - public void sync() { + public int sync() { logger.info("Start foreign rules sync with DGC Gateway"); var start = Instant.now(); List countries = dgcRulesClient.getCountries(); countries.remove("CH"); - + int successful = 0; for(var country: countries){ - var rules = dgcRulesClient.download(country).entrySet().stream() - .map( - rule -> { - try { - return decodeRule(rule.getValue()); - } catch (Exception e) { - logger.error( - "Failed to decode rule {}", - rule.getKey(), - e); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - foreignRulesDataService.removeRuleSet(country); - rules.forEach(foreignRulesDataService::insertRule); + var rules = new ArrayList(); + var downloaded = dgcRulesClient.download(country); + downloaded.forEach( (id, ruleVersions) -> { + ruleVersions.forEach( version ->{ + try { + rules.add( + decodeRule( + (ObjectNode) version, country, id)); + } catch (Exception e) { + logger.error( + "Failed to decode rule {}", + id, + e); + } + } + ); + }); + if(rules.isEmpty()){ - logger.warn("No rules were downloaded or decoded for {}", country); + logger.error("No rules were downloaded or decoded for {}", country); + }else{ + foreignRulesDataService.removeRuleSet(country); + rules.forEach(foreignRulesDataService::insertRule); + successful += rules.size(); } } var end = Instant.now(); logger.info( - "Successfully downloaded foreign rules {} ms", + "Successfully downloaded {} foreign rules {} ms", successful, end.toEpochMilli() - start.toEpochMilli()); + return successful; } - private ForeignRule decodeRule(JsonNode rule) + private ForeignRule decodeRule(ObjectNode rule, String country, String id) throws CertificateException, IOException, OperatorCreationException, CMSException { var foreignRule = new ForeignRule(); String content = new String((byte[]) CmsUtil.decodeCms(rule.get("cms").asText()), StandardCharsets.UTF_8); @@ -85,8 +92,8 @@ private ForeignRule decodeRule(JsonNode rule) var validFrom = LocalDateTime.ofInstant(Instant.parse(rule.get("validFrom").asText()), ZoneId.of("UTC")); foreignRule.setValidFrom(validFrom); foreignRule.setVersion(rule.get("version").asText()); - foreignRule.setCountry(rule.get("country").asText()); - foreignRule.setId(rule.get("id").asText()); + foreignRule.setCountry(country); + foreignRule.setId(id); return foreignRule; } } diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java index b02afa69..c378b58f 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java @@ -77,7 +77,8 @@ void downloadTest() throws Exception { withStatus(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON) .body(de)); - rulesSyncer.sync(); + int result = rulesSyncer.sync(); + assertEquals(29, result); } } From 209fdd779b9d88f679a3ffef36d521c7435b3ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Mon, 28 Mar 2022 16:51:35 +0200 Subject: [PATCH 05/13] foreign rules: fix datetime parsing for java 11 --- .../backend/verifier/sync/syncer/DgcForeignRulesSyncer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java index a96df776..05e255d2 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java @@ -21,6 +21,7 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -87,9 +88,9 @@ private ForeignRule decodeRule(ObjectNode rule, String country, String id) var foreignRule = new ForeignRule(); String content = new String((byte[]) CmsUtil.decodeCms(rule.get("cms").asText()), StandardCharsets.UTF_8); foreignRule.setContent(content); - var validUntil = LocalDateTime.ofInstant(Instant.parse(rule.get("validTo").asText()), ZoneId.of("UTC")); + var validUntil = LocalDateTime.parse(rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); foreignRule.setValidUntil(validUntil); - var validFrom = LocalDateTime.ofInstant(Instant.parse(rule.get("validFrom").asText()), ZoneId.of("UTC")); + var validFrom = LocalDateTime.parse(rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); foreignRule.setValidFrom(validFrom); foreignRule.setVersion(rule.get("version").asText()); foreignRule.setCountry(country); From 7f626573ebf2d6267c72fd531d3fa62c594a25b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Tue, 29 Mar 2022 10:20:13 +0200 Subject: [PATCH 06/13] convert foreign rule field names to lowercase --- .../verifier/sync/syncer/DgcForeignRulesSyncer.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java index 05e255d2..91f8e288 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java @@ -14,6 +14,7 @@ import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; import ch.admin.bag.covidcertificate.backend.verifier.sync.utils.CmsUtil; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -35,6 +36,7 @@ public class DgcForeignRulesSyncer { private static final Logger logger = LoggerFactory.getLogger(DgcForeignRulesSyncer.class); private final ForeignRulesDataService foreignRulesDataService; private final DgcRulesClient dgcRulesClient; + private final ObjectMapper mapper = new ObjectMapper(); public DgcForeignRulesSyncer( DgcRulesClient dgcRulesClient, ForeignRulesDataService foreignRulesDataService) { @@ -87,7 +89,11 @@ private ForeignRule decodeRule(ObjectNode rule, String country, String id) throws CertificateException, IOException, OperatorCreationException, CMSException { var foreignRule = new ForeignRule(); String content = new String((byte[]) CmsUtil.decodeCms(rule.get("cms").asText()), StandardCharsets.UTF_8); - foreignRule.setContent(content); + ObjectNode convertedNode = mapper.createObjectNode(); + mapper.readTree(content).fields().forEachRemaining(field -> { + convertedNode.set(field.getKey().toLowerCase(), field.getValue()); + }); + foreignRule.setContent(convertedNode.toString()); var validUntil = LocalDateTime.parse(rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); foreignRule.setValidUntil(validUntil); var validFrom = LocalDateTime.parse(rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); @@ -95,6 +101,7 @@ private ForeignRule decodeRule(ObjectNode rule, String country, String id) foreignRule.setVersion(rule.get("version").asText()); foreignRule.setCountry(country); foreignRule.setId(id); + return foreignRule; } } From fb458d69817d8a31dbc2ad1a6722e22f06221b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Wed, 30 Mar 2022 09:38:44 +0200 Subject: [PATCH 07/13] fix JWT for country list endpoint --- .../sync/syncer/DgcForeignRulesSyncer.java | 8 +++----- .../controller/ForeignRulesControllerV2.java | 7 +++++-- .../ForeignRulesControllerV2JWSTest.java | 15 +++++++++++++++ .../ForeignRulesControllerV2Test.java | 19 ++++++++++++------- 4 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2JWSTest.java diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java index 91f8e288..01590880 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java @@ -13,7 +13,6 @@ import ch.admin.bag.covidcertificate.backend.verifier.data.ForeignRulesDataService; import ch.admin.bag.covidcertificate.backend.verifier.model.ForeignRule; import ch.admin.bag.covidcertificate.backend.verifier.sync.utils.CmsUtil; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; @@ -21,12 +20,10 @@ import java.security.cert.CertificateException; import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; +import java.util.Locale; import org.bouncycastle.cms.CMSException; import org.bouncycastle.operator.OperatorCreationException; import org.slf4j.Logger; @@ -91,7 +88,8 @@ private ForeignRule decodeRule(ObjectNode rule, String country, String id) String content = new String((byte[]) CmsUtil.decodeCms(rule.get("cms").asText()), StandardCharsets.UTF_8); ObjectNode convertedNode = mapper.createObjectNode(); mapper.readTree(content).fields().forEachRemaining(field -> { - convertedNode.set(field.getKey().toLowerCase(), field.getValue()); + String fieldName = field.getKey().substring(0,1).toLowerCase().concat(field.getKey().substring(1)); + convertedNode.set(fieldName, field.getValue()); }); foreignRule.setContent(convertedNode.toString()); var validUntil = LocalDateTime.parse(rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index dbf7c531..688574f7 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -54,8 +54,11 @@ public ForeignRulesControllerV2( } @GetMapping(value = "/foreignRules") - public @ResponseBody ResponseEntity> getCountries(WebRequest request) { - return ResponseEntity.ok(foreignRulesDataService.getCountries()); + public @ResponseBody ResponseEntity getCountries(WebRequest request) { + var countries = foreignRulesDataService.getCountries(); + HashMap> res = new HashMap<>(); + res.put("countries", countries); + return ResponseEntity.ok(res); } @GetMapping(value = "/foreignRules/{country}") diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2JWSTest.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2JWSTest.java new file mode 100644 index 00000000..b593100f --- /dev/null +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2JWSTest.java @@ -0,0 +1,15 @@ +package ch.admin.bag.covidcertificate.backend.verifier.ws.controller; + +import ch.admin.bag.covidcertificate.backend.verifier.ws.security.signature.JwsMessageConverter; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +@TestInstance(Lifecycle.PER_CLASS) +public class ForeignRulesControllerV2JWSTest extends ForeignRulesControllerV2Test{ + + @BeforeAll + public void setup(){ + this.acceptMediaType = JwsMessageConverter.JWS_MEDIA_TYPE; + } +} diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java index b6bbc855..bcd4bdf1 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java @@ -15,7 +15,11 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import ch.admin.bag.covidcertificate.backend.verifier.ws.util.TestHelper; import ch.admin.bag.covidcertificate.backend.verifier.ws.utils.EtagUtil; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -29,10 +33,6 @@ public class ForeignRulesControllerV2Test extends BaseControllerTest { private String atRulesUrl = "/trust/v2/foreignRules/AT"; private String countryListUrl = "/trust/v2/foreignRules"; - @BeforeAll - static void setup(){ - - } @Test public void countryListTest() throws Exception { @@ -45,8 +45,11 @@ public void countryListTest() throws Exception { // verify response assertNotNull(response); - assertTrue(response.getContentAsString().contains("AT")); - assertTrue(response.getContentAsString().contains("DE")); + + ObjectNode countries = objectMapper.valueToTree(testHelper.verifyAndReadValue( + response, acceptMediaType, TestHelper.PATH_TO_CA_PEM, Map.class)); + assertTrue(countries.get("countries").get(0).asText().equals("AT")); + assertTrue(countries.get("countries").get(1).asText().equals("DE")); } @@ -61,7 +64,9 @@ public void foreignRulesTest() throws Exception { // verify response assertNotNull(response); - + ObjectNode rules = objectMapper.valueToTree(testHelper.verifyAndReadValue( + response, acceptMediaType, TestHelper.PATH_TO_CA_PEM, Map.class)); + assertNotNull(rules.get("rules").get(0)); } @Test From 3503893b9bd9fe0bf93e75d1de022994e09db1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Fri, 1 Apr 2022 09:22:02 +0200 Subject: [PATCH 08/13] foreign rules: make caching duration configurable --- .../backend/verifier/data/util/CacheUtil.java | 1 + .../backend/verifier/ws/config/WsBaseConfig.java | 6 ++++++ .../verifier/ws/controller/ForeignRulesControllerV2.java | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/util/CacheUtil.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/util/CacheUtil.java index d8a0186e..901c8e90 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/util/CacheUtil.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-data/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/data/util/CacheUtil.java @@ -23,6 +23,7 @@ public class CacheUtil { public static Duration VALUE_SETS_MAX_AGE; public static Duration KEYS_UPDATES_MAX_AGE; public static Duration KEYS_LIST_MAX_AGE; + public static Duration FOREIGN_RULES_MAX_AGE; public static Duration KEYS_BUCKET_DURATION; public static Duration REVOCATION_RETENTION_BUCKET_DURATION; diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java index 646a079f..1540c44e 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/config/WsBaseConfig.java @@ -136,6 +136,12 @@ public void setKeysListMaxAge(Duration maxAge) { CacheUtil.KEYS_LIST_MAX_AGE = maxAge; } + @Value("${ws.foreignRules.max-age:PT1M}") + public void setForeignRulesMaxAge(Duration maxAge) { + CacheUtil.FOREIGN_RULES_MAX_AGE = maxAge; + } + + @Override public void extendMessageConverters(List> converters) { try { diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index 688574f7..b4b75d49 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -58,7 +58,7 @@ public ForeignRulesControllerV2( var countries = foreignRulesDataService.getCountries(); HashMap> res = new HashMap<>(); res.put("countries", countries); - return ResponseEntity.ok(res); + return ResponseEntity.ok().headers(getVerificationRulesHeaders()).body(res); } @GetMapping(value = "/foreignRules/{country}") @@ -123,6 +123,6 @@ public ForeignRulesControllerV2( } private HttpHeaders getVerificationRulesHeaders() { - return CacheUtil.createExpiresHeader(Instant.now().plus(48, ChronoUnit.HOURS)); + return CacheUtil.createExpiresHeader(Instant.now().plus(CacheUtil.FOREIGN_RULES_MAX_AGE)); } } From 250806830eb628a3a24b4904cbd97550401699af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Tue, 5 Apr 2022 16:42:58 +0200 Subject: [PATCH 09/13] add validDuration to country code list --- .../verifier/ws/controller/ForeignRulesControllerV2.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index b4b75d49..a03067a7 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -56,8 +56,9 @@ public ForeignRulesControllerV2( @GetMapping(value = "/foreignRules") public @ResponseBody ResponseEntity getCountries(WebRequest request) { var countries = foreignRulesDataService.getCountries(); - HashMap> res = new HashMap<>(); + HashMap res = new HashMap<>(); res.put("countries", countries); + res.put("validDuration", 172800000); return ResponseEntity.ok().headers(getVerificationRulesHeaders()).body(res); } From 1eaa1086555c5d09cf7bb9d953d10335044b0da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Thu, 7 Apr 2022 13:37:12 +0200 Subject: [PATCH 10/13] fix error logging in ForeignRulesController --- .../verifier/ws/controller/ForeignRulesControllerV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index a03067a7..43d3f745 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -82,7 +82,7 @@ public ForeignRulesControllerV2( try { return mapper.readTree(rule.getContent()); } catch (JsonProcessingException e) { - e.printStackTrace(); + logger.error("Failed to read foreign rule", e); return null; } }) From 7f5eb83e03635956b7e0223c22ef3fd1aa274a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Thu, 7 Apr 2022 13:41:35 +0200 Subject: [PATCH 11/13] fix value set sync cron --- .../verifier/sync/config/SyncSchedulingBaseConfig.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java index 01071f34..c2e351e0 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/config/SyncSchedulingBaseConfig.java @@ -85,6 +85,14 @@ public void valueSetCleanCron() { LockAssert.assertLocked(); valueSetDataService.deleteOldValueSets(); } + + @Scheduled(cron = "${value-set.sync.cron:0 0 0 ? * *}") + @SchedulerLock(name = "value_set_sync", lockAtLeastFor = "PT15S") + public void valueSetSyncCron() { + LockAssert.assertLocked(); + dgcValueSetSyncer.sync(); + } + @Scheduled(cron = "${foreign-rules.sync.cron:0 39 * ? * *}") @SchedulerLock(name = "foreign_rules_sync", lockAtLeastFor = "PT15S") From cf189f48f1b229d467f2e5cb441085f60763cde3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gst=C3=B6hl?= Date: Thu, 7 Apr 2022 17:15:37 +0200 Subject: [PATCH 12/13] refactoring in ForeignRulesController --- .../verifier/ws/controller/ForeignRulesControllerV2.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index 43d3f745..069c13b8 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -43,6 +43,7 @@ public class ForeignRulesControllerV2 { private static final Logger logger = LoggerFactory.getLogger(ForeignRulesControllerV2.class); + public static final int FOREIGN_RULES_VALID_DURATION = 172800000; private final ValueSetDataService valueSetDataService; private final ForeignRulesDataService foreignRulesDataService; @@ -54,11 +55,11 @@ public ForeignRulesControllerV2( } @GetMapping(value = "/foreignRules") - public @ResponseBody ResponseEntity getCountries(WebRequest request) { + public @ResponseBody ResponseEntity getCountries() { var countries = foreignRulesDataService.getCountries(); HashMap res = new HashMap<>(); res.put("countries", countries); - res.put("validDuration", 172800000); + res.put("validDuration", FOREIGN_RULES_VALID_DURATION); return ResponseEntity.ok().headers(getVerificationRulesHeaders()).body(res); } @@ -67,7 +68,7 @@ public ForeignRulesControllerV2( @PathVariable("country") String country, WebRequest request) { ObjectMapper mapper = new ObjectMapper(); Map result = new HashMap<>(); - result.put("validDuration", 172800000); + result.put("validDuration", FOREIGN_RULES_VALID_DURATION); // Add rules to output var foreignRules = foreignRulesDataService.getRulesForCountry(country); From 943318fffa405de149a8a9058813e21ff2f8edcf Mon Sep 17 00:00:00 2001 From: Martin Alig Date: Thu, 7 Apr 2022 17:25:30 +0200 Subject: [PATCH 13/13] Formatting --- .../sync/syncer/DgcForeignRulesSyncer.java | 64 +++++++++++-------- .../sync/syncer/ForeignRulesSyncTest.java | 3 - .../controller/ForeignRulesControllerV2.java | 3 - .../ForeignRulesControllerV2Test.java | 3 - .../ws/util/MockForeignRuleDataService.java | 16 ++--- 5 files changed, 43 insertions(+), 46 deletions(-) diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java index 01590880..0e484d2d 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/DgcForeignRulesSyncer.java @@ -23,7 +23,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import org.bouncycastle.cms.CMSException; import org.bouncycastle.operator.OperatorCreationException; import org.slf4j.Logger; @@ -48,28 +47,24 @@ public int sync() { List countries = dgcRulesClient.getCountries(); countries.remove("CH"); int successful = 0; - for(var country: countries){ + for (var country : countries) { var rules = new ArrayList(); var downloaded = dgcRulesClient.download(country); - downloaded.forEach( (id, ruleVersions) -> { - ruleVersions.forEach( version ->{ - try { - rules.add( - decodeRule( - (ObjectNode) version, country, id)); - } catch (Exception e) { - logger.error( - "Failed to decode rule {}", - id, - e); - } - } - ); - }); + downloaded.forEach( + (id, ruleVersions) -> { + ruleVersions.forEach( + version -> { + try { + rules.add(decodeRule((ObjectNode) version, country, id)); + } catch (Exception e) { + logger.error("Failed to decode rule {}", id, e); + } + }); + }); - if(rules.isEmpty()){ + if (rules.isEmpty()) { logger.error("No rules were downloaded or decoded for {}", country); - }else{ + } else { foreignRulesDataService.removeRuleSet(country); rules.forEach(foreignRulesDataService::insertRule); successful += rules.size(); @@ -77,7 +72,8 @@ public int sync() { } var end = Instant.now(); logger.info( - "Successfully downloaded {} foreign rules {} ms", successful, + "Successfully downloaded {} foreign rules {} ms", + successful, end.toEpochMilli() - start.toEpochMilli()); return successful; } @@ -85,16 +81,30 @@ public int sync() { private ForeignRule decodeRule(ObjectNode rule, String country, String id) throws CertificateException, IOException, OperatorCreationException, CMSException { var foreignRule = new ForeignRule(); - String content = new String((byte[]) CmsUtil.decodeCms(rule.get("cms").asText()), StandardCharsets.UTF_8); + String content = + new String( + (byte[]) CmsUtil.decodeCms(rule.get("cms").asText()), + StandardCharsets.UTF_8); ObjectNode convertedNode = mapper.createObjectNode(); - mapper.readTree(content).fields().forEachRemaining(field -> { - String fieldName = field.getKey().substring(0,1).toLowerCase().concat(field.getKey().substring(1)); - convertedNode.set(fieldName, field.getValue()); - }); + mapper.readTree(content) + .fields() + .forEachRemaining( + field -> { + String fieldName = + field.getKey() + .substring(0, 1) + .toLowerCase() + .concat(field.getKey().substring(1)); + convertedNode.set(fieldName, field.getValue()); + }); foreignRule.setContent(convertedNode.toString()); - var validUntil = LocalDateTime.parse(rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + var validUntil = + LocalDateTime.parse( + rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); foreignRule.setValidUntil(validUntil); - var validFrom = LocalDateTime.parse(rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + var validFrom = + LocalDateTime.parse( + rule.get("validTo").asText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); foreignRule.setValidFrom(validFrom); foreignRule.setVersion(rule.get("version").asText()); foreignRule.setCountry(country); diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java index c378b58f..b8cdab74 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-sync/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/sync/syncer/ForeignRulesSyncTest.java @@ -11,13 +11,10 @@ package ch.admin.bag.covidcertificate.backend.verifier.sync.syncer; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; -import ch.admin.bag.covidcertificate.backend.verifier.model.sync.CertificateType; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java index 069c13b8..97828ae7 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/main/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2.java @@ -18,16 +18,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.security.NoSuchAlgorithmException; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java index bcd4bdf1..db385a5f 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/controller/ForeignRulesControllerV2Test.java @@ -16,11 +16,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import ch.admin.bag.covidcertificate.backend.verifier.ws.util.TestHelper; -import ch.admin.bag.covidcertificate.backend.verifier.ws.utils.EtagUtil; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.Map; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; diff --git a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java index 5a8850a6..8bfcb04f 100644 --- a/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java +++ b/ch-covidcertificate-backend-verifier/ch-covidcertificate-backend-verifier-ws/src/test/java/ch/admin/bag/covidcertificate/backend/verifier/ws/util/MockForeignRuleDataService.java @@ -15,7 +15,7 @@ public class MockForeignRuleDataService implements ForeignRulesDataService { private final ForeignRule at1 = new ForeignRule(); private final ForeignRule at2 = new ForeignRule(); - public MockForeignRuleDataService(){ + public MockForeignRuleDataService() { at1.setId("GR-AT-0039"); at1.setVersion("0.0.1"); at1.setCountry("AT"); @@ -38,22 +38,18 @@ public List getCountries() { @Override public List getRulesForCountry(String country) { - if(country.equals("AT")){ + if (country.equals("AT")) { return Arrays.asList(at1, at2); - }else if(country.equals("DE")){ + } else if (country.equals("DE")) { return new ArrayList<>(); - }else{ + } else { return new ArrayList<>(); } } @Override - public void insertRule(ForeignRule rule) { - - } + public void insertRule(ForeignRule rule) {} @Override - public void removeRuleSet(String country) { - - } + public void removeRuleSet(String country) {} }