From 9e9a41b85c7ebf0cf99c6d72af65ae4372a80869 Mon Sep 17 00:00:00 2001 From: Andreas Scheibal Date: Tue, 7 Dec 2021 13:27:53 +0100 Subject: [PATCH] initial commit --- .github/ISSUE_TEMPLATE/01_bug.md | 43 + .github/ISSUE_TEMPLATE/02_feature_request.md | 31 + .github/ISSUE_TEMPLATE/03_enhancement.md | 30 + .github/ISSUE_TEMPLATE/04_question.md | 20 + .github/workflows/ci-dependency-check.yml | 36 + .github/workflows/ci-main.yml | 148 ++ .github/workflows/ci-openapi.yml | 67 + .github/workflows/ci-pull-request.yml | 45 + .github/workflows/ci-release-notes.yml | 25 + .github/workflows/ci-release.yml | 79 ++ .github/workflows/ci-sonar.yml | 39 + .gitignore | 45 + .grenrc.js | 30 + CODEOWNERS | 8 + CODE_OF_CONDUCT.md | 130 ++ CONTRIBUTING.md | 75 + Dockerfile | 11 + LICENSE | 201 +++ NOTICE | 11 + README.md | 356 +++++ SECURITY.md | 7 + THIRD-PARTY.md | 163 +++ codestyle/checkstyle.xml | 318 +++++ docker-compose.yml | 40 + docs/DGCG-Overview.png | Bin 0 -> 179153 bytes docs/DGCG-deployment-template.png | Bin 0 -> 70832 bytes docs/Nationalbackends.png | Bin 0 -> 116903 bytes docs/Validation-Rules-Additional-Doc.md | 65 + docs/api.png | Bin 0 -> 61277 bytes docs/distribution_of_signing.png | Bin 0 -> 76949 bytes docs/software-design-dgc-gateway.md | 500 +++++++ docs/triangle_of_trust.png | Bin 0 -> 51142 bytes docs/trust.png | Bin 0 -> 120883 bytes owasp/suppressions.xml | 22 + pom.xml | 566 ++++++++ settings.xml | 12 + src/WEB-INF/web.xml | 6 + src/assembly/assembly.xml | 46 + .../ec/dgc/gateway/DgcGatewayApplication.java | 46 + .../ec/dgc/gateway/client/JrcClient.java | 42 + .../dgc/gateway/client/JrcClientConfig.java | 94 ++ .../gateway/config/BouncyCastleConfig.java | 36 + .../gateway/config/DgcConfigProperties.java | 80 ++ .../ec/dgc/gateway/config/DgcKeyStore.java | 96 ++ .../ec/dgc/gateway/config/ErrorHandler.java | 80 ++ .../gateway/config/MdcCleanupInterceptor.java | 37 + .../ec/dgc/gateway/config/OpenApiConfig.java | 106 ++ .../dgc/gateway/config/SchedulerConfig.java | 33 + .../ec/dgc/gateway/config/ShedLockConfig.java | 53 + .../config/ValidationRuleSchemaProvider.java | 62 + .../ec/dgc/gateway/config/WebMvcConfig.java | 34 + .../dgc/gateway/entity/AuditEventEntity.java | 85 ++ .../entity/SignerInformationEntity.java | 97 ++ .../gateway/entity/TrustedPartyEntity.java | 100 ++ .../gateway/entity/ValidationRuleEntity.java | 108 ++ .../ec/dgc/gateway/entity/ValuesetEntity.java | 52 + .../exception/DgcgResponseException.java | 51 + .../ec/dgc/gateway/model/JrcRatValueset.java | 83 ++ .../gateway/model/JrcRatValuesetResponse.java | 38 + .../gateway/model/ParsedValidationRule.java | 56 + .../ec/dgc/gateway/model/RatValueset.java | 50 + .../ec/dgc/gateway/model/TrustList.java | 44 + .../ec/dgc/gateway/model/TrustListType.java | 29 + .../europa/ec/dgc/gateway/model/Valueset.java | 41 + .../repository/AuditEventRepository.java | 27 + .../SignerInformationRepository.java | 43 + .../repository/TrustedPartyRepository.java | 44 + .../repository/ValidationRuleRepository.java | 60 + .../repository/ValuesetRepository.java | 35 + .../controller/CountryListController.java | 82 ++ .../SignerCertificateController.java | 383 ++++++ .../controller/TrustListController.java | 242 ++++ .../controller/ValidationRuleController.java | 378 +++++ .../controller/ValuesetController.java | 141 ++ .../CertificateTypeEnumConverter.java | 41 + .../CmsCertificateMessageConverter.java | 97 ++ .../converter/CmsStringMessageConverter.java | 99 ++ .../restapi/dto/CertificateTypeDto.java | 28 + .../gateway/restapi/dto/ProblemReportDto.java | 46 + .../restapi/dto/SignedCertificateDto.java | 60 + .../gateway/restapi/dto/SignedStringDto.java | 60 + .../dgc/gateway/restapi/dto/TrustListDto.java | 59 + .../restapi/dto/ValidationRuleDto.java | 48 + .../CertificateAuthenticationFilter.java | 202 +++ .../CertificateAuthenticationRequired.java | 31 + .../restapi/mapper/GwTrustListMapper.java | 38 + .../mapper/GwValidationRuleMapper.java | 32 + .../ec/dgc/gateway/service/AuditService.java | 89 ++ .../service/RatValuesetUpdateService.java | 157 +++ .../service/SignerInformationService.java | 328 +++++ .../dgc/gateway/service/TrustListService.java | 145 ++ .../gateway/service/TrustedPartyService.java | 203 +++ .../service/ValidationRuleService.java | 405 ++++++ .../dgc/gateway/service/ValuesetService.java | 84 ++ .../europa/ec/dgc/gateway/utils/DgcMdc.java | 112 ++ src/main/resources/application-dev.yml | 14 + src/main/resources/application-h2.yml | 9 + src/main/resources/application-local.yml | 6 + src/main/resources/application-mysql.yml | 9 + src/main/resources/application.yml | 48 + src/main/resources/db/changelog.xml | 15 + .../db/changelog/add-shedlock-table.xml | 24 + .../db/changelog/add-unique-constraints.xml | 16 + .../changelog/add-validation-rule-table.xml | 40 + .../db/changelog/add-valueset-table.xml | 19 + .../db/changelog/create-audit-table.xml | 34 + .../changelog/fix-certificate-thumbprints.xml | 20 + .../increase-column-size-for-valueset.xml | 12 + .../resources/db/changelog/init-tables.xml | 59 + src/main/resources/db/snapshot.json | 731 ++++++++++ src/main/resources/logback-spring.xml | 57 + .../resources/validation-rule.schema.json | 146 ++ .../CountryListIntegrationTest.java | 88 ++ .../SignerCertificateIntegrationTest.java | 326 +++++ .../controller/TrustListIntegrationTest.java | 490 +++++++ .../ValidationRuleIntegrationTest.java | 1215 +++++++++++++++++ .../controller/ValuesetIntegrationTest.java | 137 ++ .../restapi/filter/CertAuthFilterTest.java | 213 +++ .../dgc/gateway/service/AuditServiceTest.java | 93 ++ .../service/RatValuesetUpdateServiceTest.java | 475 +++++++ .../service/SignerInformationServiceTest.java | 360 +++++ .../gateway/service/TrustListServiceTest.java | 193 +++ .../service/TrustedPartyServiceTest.java | 162 +++ .../gateway/service/ValuesetServiceTest.java | 85 ++ .../testdata/CertificateTestUtils.java | 142 ++ .../dgc/gateway/testdata/DgcTestKeyStore.java | 79 ++ .../testdata/TrustedPartyTestHelper.java | 126 ++ src/test/resources/application.yml | 36 + templates/file-header.txt | 19 + templates/third-party.ftl | 30 + 130 files changed, 14135 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/01_bug.md create mode 100644 .github/ISSUE_TEMPLATE/02_feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/03_enhancement.md create mode 100644 .github/ISSUE_TEMPLATE/04_question.md create mode 100644 .github/workflows/ci-dependency-check.yml create mode 100644 .github/workflows/ci-main.yml create mode 100644 .github/workflows/ci-openapi.yml create mode 100644 .github/workflows/ci-pull-request.yml create mode 100644 .github/workflows/ci-release-notes.yml create mode 100644 .github/workflows/ci-release.yml create mode 100644 .github/workflows/ci-sonar.yml create mode 100644 .gitignore create mode 100644 .grenrc.js create mode 100644 CODEOWNERS create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 THIRD-PARTY.md create mode 100644 codestyle/checkstyle.xml create mode 100644 docker-compose.yml create mode 100644 docs/DGCG-Overview.png create mode 100644 docs/DGCG-deployment-template.png create mode 100644 docs/Nationalbackends.png create mode 100644 docs/Validation-Rules-Additional-Doc.md create mode 100644 docs/api.png create mode 100644 docs/distribution_of_signing.png create mode 100644 docs/software-design-dgc-gateway.md create mode 100644 docs/triangle_of_trust.png create mode 100644 docs/trust.png create mode 100644 owasp/suppressions.xml create mode 100644 pom.xml create mode 100644 settings.xml create mode 100644 src/WEB-INF/web.xml create mode 100644 src/assembly/assembly.xml create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/DgcGatewayApplication.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/client/JrcClient.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/client/JrcClientConfig.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/BouncyCastleConfig.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/DgcKeyStore.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/ErrorHandler.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/MdcCleanupInterceptor.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/OpenApiConfig.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/SchedulerConfig.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/ShedLockConfig.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/ValidationRuleSchemaProvider.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/config/WebMvcConfig.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/entity/AuditEventEntity.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/entity/SignerInformationEntity.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/entity/TrustedPartyEntity.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/entity/ValidationRuleEntity.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/exception/DgcgResponseException.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValueset.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValuesetResponse.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/model/ParsedValidationRule.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/model/RatValueset.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/model/TrustList.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/model/TrustListType.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/model/Valueset.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/repository/AuditEventRepository.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/repository/ValidationRuleRepository.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/repository/ValuesetRepository.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListController.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateController.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListController.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleController.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetController.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CertificateTypeEnumConverter.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsCertificateMessageConverter.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsStringMessageConverter.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/CertificateTypeDto.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ProblemReportDto.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedCertificateDto.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedStringDto.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/TrustListDto.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ValidationRuleDto.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationFilter.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRequired.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwTrustListMapper.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwValidationRuleMapper.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/service/AuditService.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateService.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/service/ValidationRuleService.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/service/ValuesetService.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/utils/DgcMdc.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application-h2.yml create mode 100644 src/main/resources/application-local.yml create mode 100644 src/main/resources/application-mysql.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/db/changelog.xml create mode 100644 src/main/resources/db/changelog/add-shedlock-table.xml create mode 100644 src/main/resources/db/changelog/add-unique-constraints.xml create mode 100644 src/main/resources/db/changelog/add-validation-rule-table.xml create mode 100644 src/main/resources/db/changelog/add-valueset-table.xml create mode 100644 src/main/resources/db/changelog/create-audit-table.xml create mode 100644 src/main/resources/db/changelog/fix-certificate-thumbprints.xml create mode 100644 src/main/resources/db/changelog/increase-column-size-for-valueset.xml create mode 100644 src/main/resources/db/changelog/init-tables.xml create mode 100644 src/main/resources/db/snapshot.json create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/validation-rule.schema.json create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListIntegrationTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateIntegrationTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetIntegrationTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/restapi/filter/CertAuthFilterTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/service/AuditServiceTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateServiceTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/service/SignerInformationServiceTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/service/TrustListServiceTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/service/TrustedPartyServiceTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/service/ValuesetServiceTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/testdata/CertificateTestUtils.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/testdata/DgcTestKeyStore.java create mode 100644 src/test/java/eu/europa/ec/dgc/gateway/testdata/TrustedPartyTestHelper.java create mode 100644 src/test/resources/application.yml create mode 100644 templates/file-header.txt create mode 100644 templates/third-party.ftl diff --git a/.github/ISSUE_TEMPLATE/01_bug.md b/.github/ISSUE_TEMPLATE/01_bug.md new file mode 100644 index 00000000..0c12e2dc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_bug.md @@ -0,0 +1,43 @@ +--- +name: "\U0001F6A8 Bug" +about: Did you come across a bug or unexpected behaviour differing from the docs? +labels: bug +--- + + + +## Describe the bug + + + +## Expected behaviour + + + +## Steps to reproduce the issue + + + + + +## Technical details + +- Host Machine OS (Windows/Linux/Mac): + +## Possible Fix + + + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/02_feature_request.md b/.github/ISSUE_TEMPLATE/02_feature_request.md new file mode 100644 index 00000000..ad11b6b4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_feature_request.md @@ -0,0 +1,31 @@ +--- +name: "\U0001F381 Feature Request" +about: Do you have an idea for a new feature? +labels: feature request +--- + + + +## Feature description + + + +## Problem and motivation + + + +## Is this something you're interested in working on + + diff --git a/.github/ISSUE_TEMPLATE/03_enhancement.md b/.github/ISSUE_TEMPLATE/03_enhancement.md new file mode 100644 index 00000000..bbdc3b75 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03_enhancement.md @@ -0,0 +1,30 @@ +--- +name: "\u23F1\uFE0F Enhancement" +about: Do you have an idea for an enhancement? +labels: enhancement +--- + + + +## Current Implementation + + + +## Suggested Enhancement + + + +## Expected Benefits + + diff --git a/.github/ISSUE_TEMPLATE/04_question.md b/.github/ISSUE_TEMPLATE/04_question.md new file mode 100644 index 00000000..2be9b929 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/04_question.md @@ -0,0 +1,20 @@ +--- +name: "\U00002753 Question" +about: If you have questions about pieces of the code or documentation for this component, please post them here. +labels: question +--- + + + +## Your Question + + + +* Source File: +* Line(s): +* Question: diff --git a/.github/workflows/ci-dependency-check.yml b/.github/workflows/ci-dependency-check.yml new file mode 100644 index 00000000..e43fa93b --- /dev/null +++ b/.github/workflows/ci-dependency-check.yml @@ -0,0 +1,36 @@ +name: Dependency Check (OWASP) +on: + schedule: + - cron: '48 02 * * 0' # Each Sunday at 02:48 UTC + push: + branches: + - main + workflow_dispatch: + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: mvn + run: >- + mvn dependency-check:check + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml new file mode 100644 index 00000000..874e618e --- /dev/null +++ b/.github/workflows/ci-main.yml @@ -0,0 +1,148 @@ +name: ci-main +on: + push: + branches: + - main +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: version + run: >- + APP_SHA=$(git rev-parse --short ${GITHUB_SHA}); + APP_REV=$(git rev-list --tags --max-count=1); + APP_TAG=$(git describe --tags ${APP_REV} 2> /dev/null || echo 0.0.0); + APP_VERSION=${APP_TAG}-${APP_SHA}; + echo "APP_SHA=${APP_SHA}" >> ${GITHUB_ENV}; + echo "APP_TAG=${APP_TAG}" >> ${GITHUB_ENV}; + echo "APP_VERSION=${APP_VERSION}" >> ${GITHUB_ENV}; + - name: mvn + run: >- + mvn versions:set + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define newVersion="${APP_VERSION}"; + mvn clean package + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - name: Upload ZIP for TST + uses: actions/upload-artifact@v2 + with: + name: DGCG001_TST + path: target/DGCG001_TST* + - name: Upload ZIP for ACC + uses: actions/upload-artifact@v2 + with: + name: DGCG001_ACC + path: target/DGCG001_ACC* + - name: Upload ZIP for PRD + uses: actions/upload-artifact@v2 + with: + name: DGCG001_PRD + path: target/DGCG001_PRD* + build-docker: + runs-on: ubuntu-20.04 + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: version + run: >- + APP_SHA=$(git rev-parse --short ${GITHUB_SHA}); + APP_REV=$(git rev-list --tags --max-count=1); + APP_TAG=$(git describe --tags ${APP_REV} 2> /dev/null || echo 0.0.0); + APP_VERSION=${APP_TAG}-${APP_SHA}; + echo "APP_SHA=${APP_SHA}" >> ${GITHUB_ENV}; + echo "APP_TAG=${APP_TAG}" >> ${GITHUB_ENV}; + echo "APP_VERSION=${APP_VERSION}" >> ${GITHUB_ENV}; + - name: mvn + run: >- + mvn versions:set + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define newVersion="${APP_VERSION}"; + mvn clean install -P docker + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - name: docker + run: >- + echo "${APP_PACKAGES_PASSWORD}" | + docker login "${APP_PACKAGES_URL}" + --username "${APP_PACKAGES_USERNAME}" + --password-stdin; + docker build . + --file ./Dockerfile + --tag "${APP_PACKAGES_URL}:${APP_VERSION}"; + docker push "${APP_PACKAGES_URL}:${APP_VERSION}"; + env: + APP_PACKAGES_URL: docker.pkg.github.com/${{ github.repository }}/dgc-gateway + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + license: + runs-on: ubuntu-20.04 + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + token: ${{ secrets.GPR_TOKEN_F11H }} + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: mvn + run: >- + mvn license:update-file-header license:add-third-party + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - name: Commit and Push changes + run: | + git config user.name github-actions + git config user.email github-actions@github.com + git add . + git diff --quiet && git diff --staged --quiet || git commit -m "Update License Header and Third Party Notices" + git push diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml new file mode 100644 index 00000000..af396736 --- /dev/null +++ b/.github/workflows/ci-openapi.yml @@ -0,0 +1,67 @@ +name: ci-openapi +on: + workflow_dispatch: + release: + types: + - created +jobs: + release: + runs-on: ubuntu-20.04 + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: version + run: >- + APP_SHA=$(git rev-parse --short ${GITHUB_SHA}); + APP_TAG=${GITHUB_REF/refs\/tags\/} + APP_VERSION=${APP_TAG}; + echo "APP_SHA=${APP_SHA}" >> ${GITHUB_ENV}; + echo "APP_TAG=${APP_TAG}" >> ${GITHUB_ENV}; + echo "APP_VERSION=${APP_VERSION}" >> ${GITHUB_ENV}; + - name: mvn + run: >- + mvn versions:set + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define newVersion="${APP_VERSION}"; + mvn clean verify + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: openapi.json + path: target/openapi.json + - name: Checkout OpenApi Doc Branch + uses: actions/checkout@v2 + with: + ref: openapi-doc + - name: Delete existing openapi.json + run: rm -f openapi.json + - name: Download openapi.json + uses: actions/download-artifact@v2 + with: + name: openapi.json + - name: Commit and Push changes + run: | + git config user.name github-actions + git config user.email github-actions@github.com + git commit -a --allow-empty -m "Update OpenAPI JSON" + git push diff --git a/.github/workflows/ci-pull-request.yml b/.github/workflows/ci-pull-request.yml new file mode 100644 index 00000000..c9233c81 --- /dev/null +++ b/.github/workflows/ci-pull-request.yml @@ -0,0 +1,45 @@ +name: ci-pull-request +on: + pull_request: + types: + - opened + - synchronize + - reopened +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: mvn + run: >- + mvn clean package + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + mvn clean package -P docker + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}" + -DskipTests; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - name: docker + run: >- + docker build . + --file ./Dockerfile; diff --git a/.github/workflows/ci-release-notes.yml b/.github/workflows/ci-release-notes.yml new file mode 100644 index 00000000..b12c3216 --- /dev/null +++ b/.github/workflows/ci-release-notes.yml @@ -0,0 +1,25 @@ +name: ci-release-notes +on: + release: + types: + - created +jobs: + release-notes: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: version + run: >- + APP_SHA=$(git rev-parse --short ${GITHUB_SHA}); + APP_TAG=${GITHUB_REF/refs\/tags\/} + APP_VERSION=${APP_TAG}; + echo "APP_SHA=${APP_SHA}" >> ${GITHUB_ENV}; + echo "APP_TAG=${APP_TAG}" >> ${GITHUB_ENV}; + echo "APP_VERSION=${APP_VERSION}" >> ${GITHUB_ENV}; + - name: release-notes + run: npx github-release-notes release --override --tags ${APP_TAG} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GREN_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml new file mode 100644 index 00000000..f28aebf6 --- /dev/null +++ b/.github/workflows/ci-release.yml @@ -0,0 +1,79 @@ +name: ci-release +on: + release: + types: + - created +jobs: + release: + runs-on: ubuntu-20.04 + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: version + run: >- + APP_SHA=$(git rev-parse --short ${GITHUB_SHA}); + APP_TAG=${GITHUB_REF/refs\/tags\/} + APP_VERSION=${APP_TAG}; + echo "APP_SHA=${APP_SHA}" >> ${GITHUB_ENV}; + echo "APP_TAG=${APP_TAG}" >> ${GITHUB_ENV}; + echo "APP_VERSION=${APP_VERSION}" >> ${GITHUB_ENV}; + - name: mvn + run: >- + mvn versions:set + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define newVersion="${APP_VERSION}"; + mvn clean verify + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Artifacts + run: | + for f in target/*.zip; do gh release upload ${APP_TAG} --clobber $f; done + gh release upload ${APP_TAG} --clobber target/openapi.json + gh release upload ${APP_TAG} --clobber src/main/resources/validation-rule.schema.json + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: mvn docker + run: >- + mvn clean package -P docker + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - name: docker + run: >- + echo "${APP_PACKAGES_PASSWORD}" | + docker login "${APP_PACKAGES_URL}" + --username "${APP_PACKAGES_USERNAME}" + --password-stdin; + docker build . + --file ./Dockerfile + --tag "${APP_PACKAGES_URL}:latest" + --tag "${APP_PACKAGES_URL}:${APP_VERSION}"; + docker push "${APP_PACKAGES_URL}:latest"; + docker push "${APP_PACKAGES_URL}:${APP_VERSION}"; + env: + APP_PACKAGES_URL: docker.pkg.github.com/${{ github.repository }}/container + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci-sonar.yml b/.github/workflows/ci-sonar.yml new file mode 100644 index 00000000..90171d45 --- /dev/null +++ b/.github/workflows/ci-sonar.yml @@ -0,0 +1,39 @@ +name: ci-sonar +on: + push: + branches: + - main + pull_request: + types: + - opened + - synchronize + - reopened +jobs: + sonar: + runs-on: ubuntu-20.04 + steps: + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: adopt + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + - name: mvn + run: >- + mvn verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar + --batch-mode + --file ./pom.xml + --settings ./settings.xml + --define app.packages.username="${APP_PACKAGES_USERNAME}" + --define app.packages.password="${APP_PACKAGES_PASSWORD}"; + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + APP_PACKAGES_USERNAME: ${{ github.actor }} + APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..dd5934d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +.jpb + +### NetBeans ### +/nbproject/ +/nbbuild/ +/dist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ + +### Others ### +~$*.docx +*.b64 +/testdata/ +*.log + +/keystore + +/tools/* +!/tools/*.bat +!/tools/*.sh + +certs/* diff --git a/.grenrc.js b/.grenrc.js new file mode 100644 index 00000000..e50821ee --- /dev/null +++ b/.grenrc.js @@ -0,0 +1,30 @@ +module.exports = { + "dataSource": "prs", + "prefix": "", + "onlyMilestones": false, + "groupBy": { + "Enhancements": [ + "enhancement", + "internal" + ], + "Bug Fixes": [ + "bug" + ], + "Documentation": [ + "documentation" + ], + "Others": [ + "other" + ] + }, + "changelogFilename": "CHANGELOG.md", + "template": { + commit: ({ message, url, author, name }) => `- [${message}](${url}) - ${author ? `@${author}` : name}`, + issue: "- {{name}} [{{text}}]({{url}})", + noLabel: "other", + group: "\n#### {{heading}}\n", + changelogTitle: "# Changelog\n\n", + release: "## {{release}} ({{date}})\n{{body}}", + releaseSeparator: "\n---\n\n" + } +} diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..94e2fb2a --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,8 @@ +# This file provides an overview of code owners in this repository. + +# Each line is a file pattern followed by one or more owners. +# The last matching pattern has the most precedence. +# For more details, read the following article on GitHub: https://help.github.com/articles/about-codeowners/. + +# These are the default owners for the whole content of this repository. The default owners are automatically added as reviewers when you open a pull request, unless different owners are specified in the file. +* @eu-digital-green-certificates/dgc-gateway-members diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..e1811e07 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,130 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[opensource@telekom.de](mailto:opensource@telekom.de). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..d97f3776 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,75 @@ +# Contributing + +## Code of conduct + +All members of the project community must abide by the [Contributor Covenant, version 2.0](CODE_OF_CONDUCT.md). +Only by respecting each other can we develop a productive, collaborative community. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting [opensource@telekom.de](mailto:opensource@telekom.de) and/or a project maintainer. + +We appreciate your courtesy of avoiding political questions here. Issues which are not related to the project itself will be closed by our community managers. + +## Engaging in our project + +We use GitHub to manage reviews of pull requests. + +* If you are a new contributor, see: [Steps to Contribute](#steps-to-contribute) + +* If you have a trivial fix or improvement, go ahead and create a pull request, addressing (with `@...`) a suitable maintainer of this repository (see [CODEOWNERS](CODEOWNERS) of the repository you want to contribute to) in the description of the pull request. + +* If you plan to do something more involved, please reach out to us and send an [email](mailto:opensource@telekom.de). This will avoid unnecessary work and surely give you and us a good deal of inspiration. + +* Relevant coding style guidelines are available in the respective sub-repositories as they are programming language-dependent. + +## Steps to Contribute + +Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on. This is to prevent duplicated efforts from other contributors on the same issue. + +If you have questions about one of the issues, please comment on them, and one of the maintainers will clarify. + +We kindly ask you to follow the [Pull Request Checklist](#Pull-Request-Checklist) to ensure reviews can happen accordingly. + +## Contributing Code + +You are welcome to contribute code in order to fix a bug or to implement a new feature. + +The following rule governs code contributions: + +* Contributions must be licensed under the [Apache 2.0 License](./LICENSE) +* Newly created files must be opened by an instantiated version of the file 'templates/file-header.txt' +* At least if you add a new file to the repository, add your name into the contributor section of the file NOTICE (please respect the preset entry structure) + +## Contributing Documentation + +You are welcome to contribute documentation to the project. + +The following rule governs documentation contributions: + +* Contributions must be licensed under the same license as code, the [Apache 2.0 License](./LICENSE) + +## Pull Request Checklist + +* Branch from the main branch and, if needed, rebase to the current main branch before submitting your pull request. If it doesn't merge cleanly with main you may be asked to rebase your changes. + +* Commits should be as small as possible while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests). + +* Test your changes as thoroughly as possible before you commit them. Preferably, automate your test by unit/integration tests. If tested manually, provide information about the test scope in the PR description (e.g. “Test passed: Upgrade version from 0.42 to 0.42.23.”). + +* Create _Work In Progress [WIP]_ pull requests only if you need clarification or an explicit review before you can continue your work item. + +* If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or a comment, or you can ask for a review by contacting us via [email](mailto:opensource@telekom.de). + +* Post review: + * If a review requires you to change your commit(s), please test the changes again. + * Amend the affected commit(s) and force push onto your branch. + * Set respective comments in your GitHub review to resolved. + * Create a general PR comment to notify the reviewers that your amendments are ready for another round of review. + +## Issues and Planning + +* We use GitHub issues to track bugs and enhancement requests. + +* Please provide as much context as possible when you open an issue. The information you provide must be comprehensive enough to reproduce that issue for the assignee. Therefore, contributors may use but aren't restricted to the issue template provided by the project maintainers. + +* When creating an issue, try using one of our issue templates which already contain some guidelines on which content is expected to process the issue most efficiently. If no template applies, you can of course also create an issue from scratch. + +* Please apply one or more applicable [labels](/../../labels) to your issue so that all community members are able to cluster the issues better. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..e95e0f80 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM adoptopenjdk:11-jre-hotspot + +WORKDIR / + +COPY [ "./target/docker/dgcg.jar", "/dgcg.jar" ] + +ENV JAVA_OPTS="$JAVA_OPTS -Xms256M -Xmx1G" + +EXPOSE 8080 + +ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /dgcg.jar" ] diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..6519b830 --- /dev/null +++ b/NOTICE @@ -0,0 +1,11 @@ +Copyright (c) 2021 T-Systems International GmbH and all other contributors. + +This project is licensed under Apache License, Version 2.0; +you may not use them except in compliance with the License. + +Contributors: +------------- + +Daniel Eder [daniel-eder], T-Mobile International Austria GmbH +Andreas Scheibal [ascheibal], T-Systems International GmbH +Felix Dittrich [f11h], T-Systems International GmbH \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..50e8f098 --- /dev/null +++ b/README.md @@ -0,0 +1,356 @@ +

+ EU Digital COVID Certificate Gateway +

+ +

+ + + + + + + + + + + + + + + +

+ +

+ About • + Development • + Documentation • + Support • + Contribute • + Contributors • + Licensing +

+ +## About + +This repository contains the source code of the EU Digital COVID Certificate Gateway (DGCG). + +DGCG is used to share validation and verification information across all national backend servers. By using DGCG, +backend-to-backend integration is facilitated, and countries can onboard incrementally, while the national backends +retain flexibility and can control data processing of their users. + +## Development + +### Prerequisites + +- OpenJDK 11 (with installed ```keytool``` CLI) +- Maven +- Authenticate to [Github Packages](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry) + +#### Authenticating to GitHub Packages + +As some of the required libraries (and/or versions are pinned/available only from GitHub Packages) You need to authenticate +to [GitHub Packages](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry) +The following steps need to be followed + +- Create [PAT](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with scopes: + - `read:packages` for downloading packages + +##### GitHub Maven + +- Copy/Augment `~/.m2/settings.xml` with the contents of `settings.xml` present in this repository + - Replace `${app.packages.username}` with your github username + - Replace `${app.packages.password}` with the generated PAT + +##### GitHub Docker Registry + +- Run `docker login docker.pkg.github.com/eu-digital-green-certificates` before running further docker commands. + - Use your GitHub username as username + - Use the generated PAT as password + +#### Additional Tools for starting Gateway locally + +- OpenSSL (with installed CLI) +- DGC-CLI (https://github.com/eu-digital-green-certificates/dgc-cli) + +### Build + +Whether you cloned or downloaded the 'zipped' sources you will either find the sources in the chosen checkout-directory +or get a zip file with the source code, which you can expand to a folder of your choice. + +In either case open a terminal pointing to the directory you put the sources in. The local build process is described +afterwards depending on the way you choose. + +#### Maven based build for Tomcat WAR-File + +``` +mvn clean install +``` + +#### Maven based build for Docker Image + +``` +mvn clean install -P docker +docker-compose build +``` + +### Start Local + +In order to start the gateway on your local computer you have to follow these steps: + +* Create TrustAnchor +* Create Database +* Start Gateway +* Insert Trusted Parties + +#### Create TrustAnchor + +The TrustAnchor is used to sign TrustedParty entries in the DB. To validate these signatures the gateway needs to public +key of the TrustAnchor. + +To create a TrustAnchor you can execute the following OpenSSL command: + +``` +openssl req -x509 -newkey rsa:4096 -keyout key_ta.pem -out cert_ta.pem -days 365 -nodes +``` + +afterwards the PublicKey has to be exported in a Java KeyStore. + +``` +keytool -importcert -alias dgcg_trust_anchor -file cert_ta.pem -keystore ta.jks -storepass dgcg-p4ssw0rd +``` + +Put the created ta.jks file in the "certs" directory of dgc-gateway. If you are using the Docker image then this folder must +be in the root directory of your local workspace (on the same level as this readme file). Create directory it does not already exist. + +#### Create Database + +DGC Gateway needs a database to persist data. For local deployment a MySQL is recommended. A MySQL DB will be started +when docker-compose file is started, so no additional tasks are required. + +#### Start Gateway + +To start the Gateway just start the docker-compose file. Please assure that the project was build for Docker build +before. + +``` +docker-compose up --build +``` + +#### Common issues + +`ERROR: for dgc-gateway_dgc-gateway_1 Cannot create container for service dgc-gateway` + +This error occurs in Docker-for-Windows if Docker does not have access to the gateway folder. In Docker-for-Windows, +go to `Settings > Resources > File Sharing` and add the root directory of the repository, then restart Docker-for-Windows. + +#### Insert Trusted Parties + +The data structure in the database should be now be created by DGC Gateway. In order to access the DGC Gateway it is +required to onboard some certificates. You will need AUTHENTICATION, UPLOAD and CSCA certificates. + +The certificates can be created with OpenSSL: + +``` +openssl req -x509 -newkey rsa:4096 -keyout key_auth.pem -out cert_auth.pem -days 365 -nodes +openssl req -x509 -newkey rsa:4096 -keyout key_csca.pem -out cert_csca.pem -days 365 -nodes +openssl req -x509 -newkey rsa:4096 -keyout key_upload.pem -out cert_upload.pem -days 365 -nodes +``` + +To sign them with TrustAnchor you can use DGC-CLI: + +``` +dgc ta sign -c cert_ta.pem -k key_ta.pem -i cert_auth.pem +dgc ta sign -c cert_ta.pem -k key_ta.pem -i cert_csca.pem +dgc ta sign -c cert_ta.pem -k key_ta.pem -i cert_upload.pem +``` + +Afterwards you can create a new entry in the `trusted_parties` table and fill all of the fields with the data produced by the above commands. + +##### Inserting Trusted Parties into the Database + +Log on to the mysql container (using the docker commands or opening a shell with the docker UI) and open mysql cli like this: + +``` +mysql --user=root --password=admin dgc +``` + +To show the available tables: + +``` + select * from INFORMATION_SCHEMA.tables where table_schema='dgc'; +``` + +We're interested in the table `trusted_party`; you can see the structure of it by using this command: + +``` +describe trusted_party; +``` + +To insert your certificates you can do this (replacing this with your own information from the `dgc` command): + +``` +INSERT INTO trusted_party (created_at, country, thumbprint, raw_data, signature, certificate_type) +SELECT + NOW() as created_at, + 'NL' as country, + '{Certificate_Thumbprint}' as thumbprint, + '{Certificate_Raw_Data}' as raw_data, + '{TrustAnchor_Signature}' as signature, + '{AUTHENTICATION|UPLOAD|CSCA}' as certificate_type; +``` + +Here is a set of example queries including all of the data: + +``` +-- Authentication certificate +insert into trusted_party (created_at, country, thumbprint, raw_data, signature, certificate_type) +select + now() as created_at, + 'NL' as country, + '397da9eb17467a2b3b83704ab6490a540bef43e84f06a6bd885e6621572da401' as thumbprint, + 'MIIFrzCCA5egAwIBAgIUPg0bGwARBnhfTWmOTpOOdYMKESMwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UEBhMCTkwxDDAKBgNVBAgMA1RMUzEMMAoGA1UEBwwDVExTMQwwCgYDVQQKDANUTFMxDDAKBgNVBAsMA1RMUzEMMAoGA1UEAwwDVExTMRIwEAYJKoZIhvcNAQkBFgNUTFMwHhcNMjEwNTA1MTAxOTMwWhcNMjIwNTA1MTAxOTMwWjBnMQswCQYDVQQGEwJOTDEMMAoGA1UECAwDVExTMQwwCgYDVQQHDANUTFMxDDAKBgNVBAoMA1RMUzEMMAoGA1UECwwDVExTMQwwCgYDVQQDDANUTFMxEjAQBgkqhkiG9w0BCQEWA1RMUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALiYJTlNwVftS7k794t/Zog54HQGTPrjreDa4c4eQT3fzjjFF7QnyLn1yERggBX3v0pVP8skTxTMbYc9uLFNYNcpMT6H6eNQDKLmyGIoh8Lq4HQ6vLUGse1IjOreJNtCyFB5z4hFeY/QmJykBza9HE+Pfw9O/otOqO2Jpupk1r+dxlL0+kugRFB+vepmeNMocbFT6mPzQdzToNdMMvuNKNxP/2NeDTzpxVdDQTHvqCK6bQuVcBj6NkLMTkdx2h0iPy7Xwoq8t5Wui/AF4c8lkdIu9/OlLMSCGTX6LaB9zxXEQVCZKml6TZ9snNe9T6OTEuFAGjKr+rpgSL3zNxfo0FurO/Rs+H1w7424yKGPL4WOBtXR9EHZz1/l8YR9tXCGlqarsFjzmZIsUvOFdRFCVAxzYsWRUdWn5wZ9YpG5wbUjzImnLm0nCBdyrnEHBhWHPXS6uXHueEuKJb5gg0Y6+owD9tMYZ7y9tgH6JaYHMYbHKDoOa0cpbUQVjhGA2ce7axIQMo/mSvd5CxXapH5N0Zope7yDjUiyNdRsHJj24r/LpxXBm7eMtIWAlsaXBL1OYF3TJXnVFXWacxaZqKam1orJrmWwyWmd2qwq/ycvs2cDjSipMNKC8WYPI11jnjLC1YcaBEVPRr2BG2mDMFZu7HGxGPIRyWC/IJn7D1LEzywVAgMBAAGjUzBRMB0GA1UdDgQWBBQw3Wsw2XD3R0MgcPpnXSvdnAJedDAfBgNVHSMEGDAWgBQw3Wsw2XD3R0MgcPpnXSvdnAJedDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCNfqF1DmFLOCOtNF/I4ZMbS4gZymEe6v3dUw9Z4eGcQWGgz19bmpeh8l21PNU+c+KgemQAoa7XMnrfySpjnVDqJ7+k6Dba4KY8ImwIDkY/RYXQxgqXGudxMiPUul0CGpjzvo385Z5VppNWu3ZgRcTcAUCe+42mWpfmvQGEJailRSoIY5K7GceaP62dRgkAmVnq8tP/EtLvnrAlAo1xk00sVLJUnxpBl/J5yOua1qM10ROo/6Md3IB820L7jOUki0rLmH2FzbdEu15PPwnxtHyjgDIr+JbY1BYnCihHI8635HBS1pv+hAj0M0cBY+ChuD5V5yfCCM+QxZM4q4HNd9Fb3hIyfod5KHKTzFxYkpG54KKYUgyPHdEMuj3RFzcIYcYlAdyfc1Q0Ms0YyqeV6Xu0HjibeV16wfZ/+0SLK6WkzMOutLL7L73xVAo1AnIkXUXjQnDOjGusttH7RbMC6BdiC/SevQQuCsFO1b0dx1OQxehNe0wiaFj63ZPXjUFz5QhCPqhZJKjEXmK55RLBUpkYOGNdcS96t+8vI+HucZAqR+2Vu00K6od3cAqjTPV37PxQQY47BnNqIjOzWqvykZNLovQ28iccZWn3R1OkWXbN44+ehGoB2ELFrdu5B3GoWXEP0RSe7+Jo2unoq77rIq4qVoKsyG7+6YDjDy6qV5HdJQ==' as raw_data, + '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' as signature, + 'AUTHENTICATION' as certificate_type; + +-- Upload certificate +insert into trusted_party (created_at, country, thumbprint, raw_data, signature, certificate_type) +select + now() as created_at, + 'NL' as country, + '7a58cc85a1bcfecb1bc69822cc2a72dfb4fbc9fe23d588fa9b0660b929d368f9' as thumbprint, + 'MIIF0zCCA7ugAwIBAgIUZiCTld5e+Bhk5ott4lejMfphwAMwDQYJKoZIhvcNAQELBQAweTELMAkGA1UEBhMCTkwxDzANBgNVBAgMBlVQTE9BRDEPMA0GA1UEBwwGVVBMT0FEMQ8wDQYDVQQKDAZVUExPQUQxDzANBgNVBAsMBlVQTE9BRDEPMA0GA1UEAwwGVVBMT0FEMRUwEwYJKoZIhvcNAQkBFgZVUExPQUQwHhcNMjEwNTA1MTAyNTI1WhcNMjIwNTA1MTAyNTI1WjB5MQswCQYDVQQGEwJOTDEPMA0GA1UECAwGVVBMT0FEMQ8wDQYDVQQHDAZVUExPQUQxDzANBgNVBAoMBlVQTE9BRDEPMA0GA1UECwwGVVBMT0FEMQ8wDQYDVQQDDAZVUExPQUQxFTATBgkqhkiG9w0BCQEWBlVQTE9BRDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMCrQqfrlprHDAGsaa7eVfBLxBmJ94+N+rNZ51Jlq3zkYV0nNzPpzac3TgNu7Vz6LeWdKBf07ozEQRNM6ojFBT+1Af+3jbT43sqs+qnRJLaD0vO0U0JPtBjk1OGkRaSJFwRId6XQWm0qjeE9Z5F8XjmA+2pSKDja37G7u4zOxnQ7qC1tI3Vs4rahonOk7npXi02o//v1VVaBlKiF4HTZhFaAIGWtoz9SDtLxPJiTGvwx/5NTJlWia1Y0t0Br+kCfuLsAnM20HnwY3CO2RPhkSC2eEDSZ6jFYaah1ggfmSanlHTlwkGzyx6P7aNlcOYCiqERYG61yjVHC5Rd8+aQeGcmF1kXF91Fz0w+LWMZ1FaRQ32bHYGv1M62BZrH58cor9eVc98iKGmlKh8VJ6Qr1bNlijD5BONfFQeKgwFGIdMJrZtbYFMDra+7RmIA+SMf3SaQsYzngBDHiQSjyTHjO3dg7PM5ZDYI79onM9SF3W3Ogj8CM+SgE67kxbvMS92zLbPB7UwJjd4j4JMDM8Z4yf9Kq/cE0mcuZVUs+9ow8LUmPGvQmRZvIpAg3m+XRnMOziUhukx3vI6NzbiqTd4rIR5RBIlgNnTxCwlb5L+6Td/C26HjpKzTTkico2vd8ux61KeG9M7nlsOU+T+w4Ff2Tcpc8eEJZkV0/hIjTIVj1hvmjAgMBAAGjUzBRMB0GA1UdDgQWBBQ9eygTuQWztj0o0b4OCqQNtqPoqzAfBgNVHSMEGDAWgBQ9eygTuQWztj0o0b4OCqQNtqPoqzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCZ/48PbHIFUDbNdqGvcw/UgT2ZlF7GhrWB667iP2XEi8m58eYvYLASV0ujhfVEhS3/Sr41fW2hApv47xU2uqkvJSMZ0bNePQv5kakVUaF/a3CtPXoYo29vXBCX1DebNoSjHMBjZRe4f6TZEY7sD9Za9Nvcvpy6Q6ly1tSyqYU/0V2DwmvDKndaF2ejNBwuc9o/FcYWPi3bGjPexbYhqjqp8ZrMbITkKibP6CXFFikAx0xVT8cHU2yBhAjclnVJMfYnzECYmO3Cpuf7r5HK204nWBkG5mnoVb6D7qtjiLImDJGMTGi2RY9AlyD968QNbh7/PcCWptVZdUOrAOOd/yYo+YucVZNcxgSNfkVjE7YCDYQr4Lf0dV47MNPXe/QOFB6fKmueQtRMl2Hn1ht+cojoG3i+qiSEwJKh2hSNGimZBT6AEd93/XA3hmsWA7UcX/YV5HZdPpc9T38vE08f4bB/JBq2yJ58LOpDpUMaVA6wkzmwXDRHBpKeMEDz2JgDZJN+Ud3mo16z7mFEgIqYNGVJvvfeRTrLOoyy+39Ge9amzeArcEqjbQ75qb/cHwJAyElKPQNh6Iet8g5o33yxDsP5LTju3s6ssU4F/CXlZ35QectNvLTx0ewfjxnDFCiAs0zy0I4jf8tb/Obx+awXrRblgGGcHjIcFUk5E3gVY5CDjg==' as raw_data, + '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' as signature, + 'UPLOAD' as certificate_type; + +-- CSCA certificate +insert into trusted_party (created_at, country, thumbprint, raw_data, signature, certificate_type) +select + now() as created_at, + 'NL' as country, + 'a5d441bfa7fdbc2b64b73fb1d78e801bc131d670f6e97218a1625098b3ced707' as thumbprint, + 'MIIFuzCCA6OgAwIBAgIURm8BBiv9BHHG479oKAOOg0kGOvgwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCTkwxDTALBgNVBAgMBENTQ0ExDTALBgNVBAcMBENTQ0ExDTALBgNVBAoMBENTQ0ExDTALBgNVBAsMBENTQ0ExDTALBgNVBAMMBENTQ0ExEzARBgkqhkiG9w0BCQEWBENTQ0EwHhcNMjEwNTA1MTAxOTU1WhcNMjIwNTA1MTAxOTU1WjBtMQswCQYDVQQGEwJOTDENMAsGA1UECAwEQ1NDQTENMAsGA1UEBwwEQ1NDQTENMAsGA1UECgwEQ1NDQTENMAsGA1UECwwEQ1NDQTENMAsGA1UEAwwEQ1NDQTETMBEGCSqGSIb3DQEJARYEQ1NDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXXPXyaDm2GQ9Cs5+MM7jvtOQODwRI4s5kIB2iyG4gOfADEOVe6DYNRm1lvqAdTCrgUw9/quGKPEpfP/4kbit8auU1MY2haicOBYDHcJDAa5UHXCOhEvCF45diehCIy582kNP0fxEQJM7KBe+XsJDyH9joA50V3JKXhih6Nx4iqAq/JyNg29E25FUC3Ml2SPZmE6g/IlidT8+B8NDPVHgGjH5a9+vOjCAUVoIcON+Ez8H/Yop87AfMGhjtSeuJCJ6F4lVfnsSQ4wbAEHKR7YKyyPAm5NAiWQ22FyM4UFS+vNijmWfLcS4uyKfxVk8gBuBOqszZrqmL5VQhFiRwz9MNtj1rUb4ZOFm2laecDXj15oVUTgw7mNLX8MB4jCyjrxUeOQ9XrVdmWQCUm3Sdf9eBwX60J0tkiuPJauaIV115r5CzXxQ3D8y+6B9mDS+7lciIaX/SzFMqI79BwJ1Klc3A8MNt0lIAYhDPSh1BGs7JAGzNbF88Sh1RWF4yJhdfIKl+e5uKXgtlzK6MqbWpr8T0lsV/DccMei9TgXiSwbPQ8DT83WRvDsPyYTWJmfCtCjTE8hWMXiStmpQaYf6fsPMdNW/8l03kpmYwmHL3ToU9e5N6cEyuUGNMjIZB8zMwta4ZdUinG62rvgEK/e6+adE4UG9hfWvs/CcXbYwt7UGK/AgMBAAGjUzBRMB0GA1UdDgQWBBSsY6WtCh6zKpcWm9jLr8pSlwxSfDAfBgNVHSMEGDAWgBSsY6WtCh6zKpcWm9jLr8pSlwxSfDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBCvDxMoziwQueLaBwDIe+UnXahaoNdw7Rw6wp4fMqccRs6mCRYYX5h2W9ukTX71BcuSyPGUdewHKndvG33odp9Vwm3a/63LiBJqk0+TGfbj5brD8DN5E0+EL3PNxEBGe81Nz2UctAr1rjHuKfCeHR+xzZZTKnDEg1lzs6VV13K9iN4Q9f9HeCvSqHNcbQEweFKZfk2tEJHjjlBwDwYWWfFraVA4FykzYrmZPBtMqrG+UWQJI6B/FjkC3urmotAP3MLjTwhhtIayzkCNpmnkvvbrY/pVnWPEzEptSqdWp3w+jTCEzd0VJeJlH9kOYxi0Mg5KaCONxCQrwI+iKoQnEwjF9cvYo+wVmslYiHMgT+0Ik4jopIiVCinKCeGjt2Ol7eGETfpg1mzjMta4+Abq1N9U36iD3qi/PhSSc6ApCr7ddfLxPLEeDu4Pt/BxsGyPNWPI7gtXLom2+gvbbMRDGpBqDHB/crE4OAAWe0DIrhaOFmSNH+yy8gEkSXfUn1FupVVeOAAOVLpRPQQaC6SxdNXufsO/mzMco5DQUovhrj1HyNM261tpupJgoR0JC8fNIAYAmdy/57ibSn/i48J+PcsaCskzzcYDA18XThImvodGlAWlFa6qhCEhJ/cbrFp75e6pU3CrP6/gf+ssAmbDWfLbNOAI1U4GY1q6+MxhH+bdA==' as raw_data, + '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' as signature, + 'CSCA' as certificate_type; +``` + +#### Testing that everything works + +You can test that everything works quickly by using this curl: + +``` +curl -X GET http://localhost:8080/trustList -H "accept: application/json" -H "X-SSL-Client-SHA256: 397da9eb17467a2b3b83704ab6490a540bef43e84f06a6bd885e6621572da401" -H "X-SSL-Client-DN: C=NL" +``` + +* Replace the example SHA with that of your own test certificate in the `X-SSL-Client-SHA256` header +* Replace the example country with your own country in the `X-SSL-Client-DN` header (i.e. US, CN, ZA) + +That command will return something looking like this (but with large base64 strings) + +``` +[ + { + "kid":"OX2p6xdGeis=", + "timestamp":"2021-05-05T12:54:49Z", + "country":"NL", + "certificateType":"AUTHENTICATION", + "thumbprint":"397da9eb17467a2b3b83704ab6490a540bef43e84f06a6bd885e6621572da401", + "signature":"", + "rawData":"" + }, + { + "kid":"eljMhaG8/ss=", + "timestamp":"2021-05-05T12:57:26Z", + "country":"NL", + "certificateType":"UPLOAD", + "thumbprint":"7a58cc85a1bcfecb1bc69822cc2a72dfb4fbc9fe23d588fa9b0660b929d368f9", + "signature":"", + "rawData":"" + }, + { + "kid":"pdRBv6f9vCs=", + "timestamp":"2021-05-05T12:57:36Z", + "country":"NL", + "certificateType":"CSCA", + "thumbprint":"a5d441bfa7fdbc2b64b73fb1d78e801bc131d670f6e97218a1625098b3ced707", + "signature":"", + "rawData":"" + } +] +``` + +NOTE: the url uses mixed cases; it's `trustList` not `trustlist`! + +If something goes wrong, the best place to look is in the logging. + +Docker users can read the logs by copying them to their machine; use `docker ps` to get the ID of the running containers +and `docker cp [CONTAINER_ID]:/logs/dgcg.log .` to copy the log file to the current directory. + +#### Send requests + +DGC Gateway does not do any mTLS termination. To simulate the LoadBalancer on your local deployment you have to send +HTTP requests to the gateway and set two HTTP-Headers: + +X-SSL-Client-SHA256: Containing the SHA-256 Hash of the AUTHENTICATION certificate (thumbprint from dgc ta command +output) +X-SSL-Client-DN: Containing the Distinguish Name (Subject) of the AUTHENTICATION certificate. (Must only contain Country +Property, e.g. C=EU) + +#### Coverting the certificate/private key into PKCS12 + +Windows users may wish to convert their certificate/private keys into a PKCS12 package so that it can be imported into the +machine's certificate store. Thankfully that is pretty simple using openssl. + +For example to convert the test authentication certificate created earlier: + +``` + openssl pkcs12 -export -out auth.pfx -inkey key_auth.pem -in cert_auth.pem +``` + +## Documentation + +### OpenAPI Spec + +The latest OpenAPI specification can always be found here: https://eu-digital-green-certificates.github.io/dgc-gateway/ + +It is also possible to access OpenAPI when DGC Gateway is deployed on your local computer when Spring-Profile "dev" or " +local" is enabled. In order to set authentication headers for authentication without a mTLS terminating LoadBalancer at +least the profile "local" +should be enabled. Then both headers can be set in Swagger UI. + +http://localhost:8090/swagger-ui/index.html + +### Other Documentation + +* [Software Design](docs/software-design-dgc-gateway.md) +* [Onboarding Document](https://github.com/eu-digital-green-certificates/dgc-participating-countries/blob/main/gateway/OnboardingChecklist.md) + +## Support and feedback + +The following channels are available for discussions, feedback, and support requests: + +| Type | Channel | +| ------------------------ | ------------------------------------------------------ | +| **Gateway issues** | | +| **Other requests** | | + +## How to contribute + +Contribution and feedback is encouraged and always welcome. For more information about how to contribute, the project structure, +as well as additional contribution information, see our [Contribution Guidelines](./CONTRIBUTING.md). By participating in this +project, you agree to abide by its [Code of Conduct](./CODE_OF_CONDUCT.md) at all times. + +## Contributors + +Our commitment to open source means that we are enabling -in fact encouraging- all interested parties to contribute and become part of its developer community. + +## Licensing + +Copyright (C) 2021 T-Systems International GmbH and all other contributors + +Licensed under the **Apache License, Version 2.0** (the "License"); you may not use this file except in compliance with the License. + +You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the [LICENSE](./LICENSE) for the specific +language governing permissions and limitations under the License. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..f494bb23 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Reporting Security Vulnerabilities + +This software is built with security and data privacy in mind to ensure your data is safe. We are grateful for security researchers and users reporting a vulnerability to us, first. To ensure that your request is handled in a timely manner and non-disclosure of vulnerabilities can be assured, please follow the below guideline. + +**Please do not report security vulnerabilities directly on GitHub. GitHub Issues can be publicly seen and therefore would result in a direct disclosure.** + +* Please address questions about data privacy, security concepts, and other media requests to the cert@telekom.de mailbox. diff --git a/THIRD-PARTY.md b/THIRD-PARTY.md new file mode 100644 index 00000000..16989ce4 --- /dev/null +++ b/THIRD-PARTY.md @@ -0,0 +1,163 @@ +ThirdPartyNotices +----------------- +This project uses third-party software or other resources that +may be distributed under licenses different from this software. +In the event that we overlooked to list a required notice, please bring this +to our attention by contacting us via this email: +opensource@telekom.de + +ThirdParty Licenses +----------------- + +| Dependency | License | +| --- | --- | +| antlr:antlr:2.7.7 | BSD License | +| ch.qos.logback:logback-classic:1.2.3 | Eclipse Public License - v 1.0 GNU Lesser General Public License | +| ch.qos.logback:logback-core:1.2.3 | Eclipse Public License - v 1.0 GNU Lesser General Public License | +| com.damnhandy:handy-uri-templates:2.1.8 | The Apache Software License, Version 2.0 | +| com.fasterxml:classmate:1.5.1 | Apache License, Version 2.0 | +| com.fasterxml.jackson.core:jackson-annotations:2.12.3 | The Apache Software License, Version 2.0 | +| com.fasterxml.jackson.core:jackson-core:2.12.3 | The Apache Software License, Version 2.0 | +| com.fasterxml.jackson.core:jackson-databind:2.12.3 | The Apache Software License, Version 2.0 | +| com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.12.3 | The Apache Software License, Version 2.0 | +| com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.12.3 | The Apache Software License, Version 2.0 | +| com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3 | The Apache Software License, Version 2.0 | +| com.fasterxml.jackson.module:jackson-module-parameter-names:2.12.3 | The Apache Software License, Version 2.0 | +| com.github.everit-org.json-schema:org.everit.json.schema:1.13.0 | Apache License, Version 2.0 | +| com.github.peteroupc:numbers:1.8.0 | CC0-1.0 | +| com.google.re2j:re2j:1.3 | Go License | +| com.h2database:h2:1.4.200 | MPL 2.0 or EPL 1.0 | +| com.jayway.jsonpath:json-path:2.5.0 | The Apache Software License, Version 2.0 | +| com.sun.activation:jakarta.activation:1.2.2 | EDL 1.0 | +| com.sun.istack:istack-commons-runtime:3.0.12 | Eclipse Distribution License - v 1.0 | +| com.upokecenter:cbor:4.4.4 | CC0-1.0 | +| com.vaadin.external.google:android-json:0.0.20131108.vaadin1 | Apache License 2.0 | +| com.vdurmont:semver4j:3.1.0 | The MIT License | +| com.zaxxer:HikariCP:4.0.3 | The Apache Software License, Version 2.0 | +| commons-codec:commons-codec:1.15 | Apache License, Version 2.0 | +| commons-collections:commons-collections:3.2.2 | Apache License, Version 2.0 | +| commons-digester:commons-digester:1.8.1 | The Apache Software License, Version 2.0 | +| commons-fileupload:commons-fileupload:1.4 | Apache License, Version 2.0 | +| commons-io:commons-io:2.11.0 | Apache License, Version 2.0 | +| commons-logging:commons-logging:1.2 | The Apache Software License, Version 2.0 | +| commons-validator:commons-validator:1.6 | Apache License, Version 2.0 | +| io.github.classgraph:classgraph:4.8.69 | The MIT License (MIT) | +| io.github.openfeign:feign-core:10.12 | The Apache Software License, Version 2.0 | +| io.github.openfeign:feign-httpclient:10.12 | The Apache Software License, Version 2.0 | +| io.github.openfeign:feign-slf4j:10.12 | The Apache Software License, Version 2.0 | +| io.github.openfeign.form:feign-form:3.8.0 | The Apache Software License, Version 2.0 | +| io.github.openfeign.form:feign-form-spring:3.8.0 | The Apache Software License, Version 2.0 | +| io.micrometer:micrometer-core:1.7.1 | The Apache Software License, Version 2.0 | +| io.swagger.core.v3:swagger-annotations:2.1.10 | Apache License 2.0 | +| io.swagger.core.v3:swagger-core:2.1.10 | Apache License 2.0 | +| io.swagger.core.v3:swagger-integration:2.1.10 | Apache License 2.0 | +| io.swagger.core.v3:swagger-models:2.1.10 | Apache License 2.0 | +| jakarta.activation:jakarta.activation-api:1.2.2 | EDL 1.0 | +| jakarta.annotation:jakarta.annotation-api:1.3.5 | EPL 2.0 GPL2 w/ CPE | +| jakarta.persistence:jakarta.persistence-api:2.2.3 | Eclipse Distribution License v. 1.0 Eclipse Public License v. 2.0 | +| jakarta.transaction:jakarta.transaction-api:1.3.3 | EPL 2.0 GPL2 w/ CPE | +| jakarta.validation:jakarta.validation-api:2.0.2 | Apache License 2.0 | +| jakarta.xml.bind:jakarta.xml.bind-api:2.3.3 | Eclipse Distribution License - v 1.0 | +| javax.activation:javax.activation-api:1.2.0 | CDDL/GPLv2+CE | +| javax.xml.bind:jaxb-api:2.3.1 | CDDL 1.1 GPL2 w/ CPE | +| joda-time:joda-time:2.10.2 | Apache 2 | +| mysql:mysql-connector-java:8.0.25 | The GNU General Public License, v2 with FOSS exception | +| net.bytebuddy:byte-buddy:1.10.22 | Apache License, Version 2.0 | +| net.bytebuddy:byte-buddy-agent:1.10.22 | Apache License, Version 2.0 | +| net.javacrumbs.shedlock:shedlock-core:4.25.0 | The Apache Software License, Version 2.0 | +| net.javacrumbs.shedlock:shedlock-provider-jdbc-template:4.25.0 | The Apache Software License, Version 2.0 | +| net.javacrumbs.shedlock:shedlock-spring:4.25.0 | The Apache Software License, Version 2.0 | +| net.minidev:accessors-smart:2.4.7 | The Apache Software License, Version 2.0 | +| net.minidev:json-smart:2.4.7 | The Apache Software License, Version 2.0 | +| org.apache.commons:commons-lang3:3.12.0 | Apache License, Version 2.0 | +| org.apache.httpcomponents:httpclient:4.5.13 | Apache License, Version 2.0 | +| org.apache.httpcomponents:httpcore:4.4.14 | Apache License, Version 2.0 | +| org.apache.logging.log4j:log4j-api:2.14.1 | Apache License, Version 2.0 | +| org.apache.logging.log4j:log4j-to-slf4j:2.14.1 | Apache License, Version 2.0 | +| org.apache.tomcat.embed:tomcat-embed-core:9.0.48 | Apache License, Version 2.0 | +| org.apache.tomcat.embed:tomcat-embed-el:9.0.48 | Apache License, Version 2.0 | +| org.apache.tomcat.embed:tomcat-embed-websocket:9.0.48 | Apache License, Version 2.0 | +| org.apiguardian:apiguardian-api:1.1.0 | The Apache License, Version 2.0 | +| org.aspectj:aspectjweaver:1.9.6 | Eclipse Public License - v 1.0 | +| org.assertj:assertj-core:3.19.0 | Apache License, Version 2.0 | +| org.bouncycastle:bcpkix-jdk15on:1.69 | Bouncy Castle Licence | +| org.bouncycastle:bcprov-jdk15on:1.69 | Bouncy Castle Licence | +| org.bouncycastle:bcutil-jdk15on:1.69 | Bouncy Castle Licence | +| org.dom4j:dom4j:2.1.3 | BSD 3-clause New License | +| org.glassfish.jaxb:jaxb-runtime:2.3.4 | Eclipse Distribution License - v 1.0 | +| org.glassfish.jaxb:txw2:2.3.4 | Eclipse Distribution License - v 1.0 | +| org.hamcrest:hamcrest:2.2 | BSD License 3 | +| org.hdrhistogram:HdrHistogram:2.1.12 | BSD-2-Clause Public Domain, per Creative Commons CC0 | +| org.hibernate:hibernate-core:5.4.32.Final | GNU Library General Public License v2.1 or later | +| org.hibernate.common:hibernate-commons-annotations:5.1.2.Final | GNU Library General Public License v2.1 or later | +| org.hibernate.validator:hibernate-validator:6.2.0.Final | Apache License 2.0 | +| org.javassist:javassist:3.27.0-GA | Apache License 2.0 LGPL 2.1 MPL 1.1 | +| org.jboss:jandex:2.2.3.Final | Apache License, Version 2.0 | +| org.jboss.logging:jboss-logging:3.4.2.Final | Apache License, version 2.0 | +| org.json:json:20201115 | The JSON License | +| org.junit.jupiter:junit-jupiter:5.7.2 | Eclipse Public License v2.0 | +| org.junit.jupiter:junit-jupiter-api:5.7.2 | Eclipse Public License v2.0 | +| org.junit.jupiter:junit-jupiter-engine:5.7.2 | Eclipse Public License v2.0 | +| org.junit.jupiter:junit-jupiter-params:5.7.2 | Eclipse Public License v2.0 | +| org.junit.platform:junit-platform-commons:1.7.2 | Eclipse Public License v2.0 | +| org.junit.platform:junit-platform-engine:1.7.2 | Eclipse Public License v2.0 | +| org.latencyutils:LatencyUtils:2.0.3 | Public Domain, per Creative Commons CC0 | +| org.liquibase:liquibase-core:4.4.2 | Apache License, Version 2.0 | +| org.mapstruct:mapstruct:1.4.2.Final | The Apache Software License, Version 2.0 | +| org.mockito:mockito-core:3.11.2 | The MIT License | +| org.mockito:mockito-junit-jupiter:3.11.2 | The MIT License | +| org.objenesis:objenesis:3.2 | Apache License, Version 2.0 | +| org.opentest4j:opentest4j:1.2.0 | The Apache License, Version 2.0 | +| org.ow2.asm:asm:9.1 | BSD-3-Clause | +| org.projectlombok:lombok:1.18.20 | The MIT License | +| org.skyscreamer:jsonassert:1.5.0 | The Apache Software License, Version 2.0 | +| org.slf4j:jul-to-slf4j:1.7.31 | MIT License | +| org.slf4j:slf4j-api:1.7.31 | MIT License | +| org.springdoc:springdoc-openapi-common:1.5.10 | The Apache License, Version 2.0 | +| org.springdoc:springdoc-openapi-ui:1.5.10 | The Apache License, Version 2.0 | +| org.springdoc:springdoc-openapi-webmvc-core:1.5.10 | The Apache License, Version 2.0 | +| org.springframework:spring-aop:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-aspects:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-beans:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-context:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-core:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-expression:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-jcl:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-jdbc:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-orm:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-test:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-tx:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-web:5.3.8 | Apache License, Version 2.0 | +| org.springframework:spring-webmvc:5.3.8 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-actuator:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-actuator-autoconfigure:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-autoconfigure:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-actuator:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-aop:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-data-jpa:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-jdbc:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-json:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-logging:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-test:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-tomcat:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-validation:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-starter-web:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-test:2.5.2 | Apache License, Version 2.0 | +| org.springframework.boot:spring-boot-test-autoconfigure:2.5.2 | Apache License, Version 2.0 | +| org.springframework.cloud:spring-cloud-commons:3.0.3 | Apache License, Version 2.0 | +| org.springframework.cloud:spring-cloud-context:3.0.3 | Apache License, Version 2.0 | +| org.springframework.cloud:spring-cloud-openfeign-core:3.0.3 | Apache License, Version 2.0 | +| org.springframework.cloud:spring-cloud-starter:3.0.3 | Apache License, Version 2.0 | +| org.springframework.cloud:spring-cloud-starter-openfeign:3.0.3 | Apache License, Version 2.0 | +| org.springframework.data:spring-data-commons:2.5.2 | Apache License, Version 2.0 | +| org.springframework.data:spring-data-jpa:2.5.2 | Apache License, Version 2.0 | +| org.springframework.security:spring-security-core:5.5.1 | Apache License, Version 2.0 | +| org.springframework.security:spring-security-crypto:5.5.1 | Apache License, Version 2.0 | +| org.springframework.security:spring-security-rsa:1.0.10.RELEASE | Apache 2.0 | +| org.springframework.security:spring-security-web:5.5.1 | Apache License, Version 2.0 | +| org.webjars:swagger-ui:3.51.1 | Apache 2.0 | +| org.webjars:webjars-locator-core:0.46 | MIT | +| org.xmlunit:xmlunit-core:2.8.2 | The Apache Software License, Version 2.0 | +| org.yaml:snakeyaml:1.28 | Apache License, Version 2.0 | diff --git a/codestyle/checkstyle.xml b/codestyle/checkstyle.xml new file mode 100644 index 00000000..ce3b135e --- /dev/null +++ b/codestyle/checkstyle.xml @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..12b2a20d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,40 @@ +version: '3' + +services: + mysql: + image: mysql/mysql-server:5.7 + ports: + - 3306:3306 + environment: + - MYSQL_DATABASE=dgc + - MYSQL_ROOT_PASSWORD=admin + - MYSQL_USER=dgc_adm + - MYSQL_PASSWORD=admin + networks: + persistence: + + dgc-gateway: + build: . + image: eu-digital-green-certificates/dgc-gateway + volumes: + - ./certs:/ec/prod/app/san/dgc + ports: + - 8080:8080 + environment: + - SERVER_PORT=8080 + - SPRING_PROFILES_ACTIVE=mysql,docker + - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/dgc + - SPRING_DATASOURCE_USERNAME=dgc_adm + - SPRING_DATASOURCE_PASSWORD=admin + - DGC_TRUSTANCHOR_KEYSTOREPATH=/ec/prod/app/san/dgc/ta.jks + - DGC_TRUSTANCHOR_KEYSTOREPASS=dgcg-p4ssw0rd + - DGC_TRUSTANCHOR_CERTIFICATEALIAS=dgcg_trust_anchor + depends_on: + - mysql + networks: + backend: + persistence: + +networks: + persistence: + backend: diff --git a/docs/DGCG-Overview.png b/docs/DGCG-Overview.png new file mode 100644 index 0000000000000000000000000000000000000000..53caf498f6212950d36c0296f9dafae4969070ef GIT binary patch literal 179153 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

^{O{qvs)m&DNBVSsdZNH<7#iKKMO zP)c{_5bxpn{jc}?J@?F9YMpMpdNV{g0@D>cY$uh!V|CZoON! zo6qy0`J&bM)me(4c9fCMXte$MXtm#k%~a7?S#pp`iXMef%2BKT-aT8;?6OMsO~M@I z+{`9sl#fQT%pm-;Wa{`+aG4qQaKW4MPQA)sM6KcQb4^lzUIHxvl6AwPCCJ>AxX6Xr zeh3-__t{UqPITpkdN_URk}s(9Ma>ct7u&&~oyomG^XH~OvS_ehWZacBcF@3_S?q51?eqVRz2K%ko!}`pb+;R~BlX{Uu^k7W?dPEg zgRk8y^ip^!W2+yA>q%AbMZSxa0UkyrQX{EiH=3UzAT~hP-REl*e5E5rF&dFMj)_;T zr=12DDS1CL(Gj+Hft2<4l>W5ouD2=HJfo=zrZLJ3_gaNE$ha~-rcYN1mHmS;v`}r8 zU1j&ZUl@t~Yj1wGU|NR`Qk~ztyNBWA_Mp^H>KmCbNqL=}J^?4;Z<-x< zQmwqy&YFPVIqlF=MNh(gq}yjNX-{$yvJwrk?o0Fp|KglZ%n8_QS8o{eZ`4 zBk)6l0H6Nn=a)M{L@u~?O(SGOp9SqR&Jmph^@L2n?7_#QbX+@499Ln>Y=VGY_La@? zzYpuBtEV|AVP;)9*IbVKMjZq*Z-5?^GnE<}Md?gNsl+@*Tt)5|U`Smz3LaD|iIkHf zTp;R+hgXX!GBEwi?F+00?p~F_O_gEXiIvSrq9R2{z<-aw!>VhxS`V#u?t~P3_X*O# zyvx|&H?!tp{HQ!>Cp5G9BHb|F{b+joHR1Z3z zu##d^f5z-e*Yaa8%2iq|FX*`(YEnN4%)Ys)K&Ef}A$H_}bDy?-My8d>r6Ei^L1ZgX zrGwh$(Q#|M)v)Fb_K6x5e{Yc1=q*K9+qIJS8j{mRZdSB^)I)x2m2vo5!om4$o+^O3 z7xYC_{zL4^cy~ZO!R$=@N+g(=?S7;Cmk{(kwTE+kF#eN*OI-{mT#tk0ZH|_RRNzew1;6Ki9ejuGMCtCEzIwFgkV%l2P@0A zyc-LU6=R1DgVs(z2Rzp~NiI%Y`8+bWgZN%P`+4%e4ITQ=?Hk3P$0_&8pOYonwf(WE z_+zIV4BNg(l}D=B1e}SjjFx){#*tZ!*K2K4I?bZ)KHIM8^@O~pD=PQj~2RIkC7{^VWc2^nG zP+f_+cprp!oY8YKzV1-g5b|pC%EuI*0||M@8EJ8%Q^!;$ULVV>`0NZRRpXRs1LiJmA*Td13Lzg$cJwY>S-c+Wv<`h1{<)dip;q85c zmp6V|BRiKW*M1LCx>;5mqCb74B+Ap0)**G+bN3FNg=-U*{xmjAJ_8#q!$ij|Eu2oe zeXsXFr+Mji}DyNqT8|xpLvG@ZmgNUg-q@ zD2F{m(<^GkMPXO&3X9*ZMQ8|kgSkeshbm|XeRI}mCh+B?Wx4La!?ky8W>{51yI-Q6gsUuA z;}K$LVjbd)un7?etu=R=MEpon^%)ifj70|xVmfmPdTmR~tBN4s)t7v|w(K+76t$%t zAfF>J}d!!v~<{kZRHF5?4%3s ztlfyNty-f$!EKr!!uT&+(ai|E4o>r4TK&j8oSP+Y(ygn{r2?hp8n5sXtv3_G;|F_H zJEo>$cG1TA@(q8xR<#u>Y!Oc$An0wg-JB6HD%Q^4sZS>&*{sbl6YE@gsaEDfpOvF& z3oCnnc!(OXNA*{z$hZ=0vhQ6#>0cl-pOb4d|Cd^5wpXguzZ!O!_|?LYmRCt+d81U= zva^t7N|+zn7NKKPinV*rt0FLcKjn>WBwmQ7&WZ1iu?VC~F6MZKC%8 zv`7tsG~xK_oo@0^K&V1zcz=?#KBcBqhhS8a8MjTJSHP#-G}gGFe(?5LE7P&e*GOp^ zRL=$1zdee5tlMbK+sH#`qI1oY)7t)jF=)tmZ$6jcfA1|o)i(UW#3~Ne{pYdKxN%jW zkL%by5Z9%p4(o(aI5j&>>~9nJv*bid^x|^PE{R1%GO6@QELG-X9D3Y# zJiIzt3HJM25?NbwQx}e0XFPttE#~w!P5o(Yj04OeOMVsaW41*}SmD#guICGpD{hXN zNP}k|&uo>azS|qdJlr2{U64gR9HDX2I{KrOXvLRj&xF=PI{z|rI{m4i_G@hV$xoXA zhor;wUi&%-2KjSiK_o>xbZ@Spq0%=|K6K3#eXHD~&phv3I~+7D6Sl!#x@p!!3r$`B z@?Q7OU*zeGbW*#&ZqEJk%}j$C06^aJ7Sr~oRPO7uB?-Z4F;tZh#x9PX#t=0}_rR&% zOwQ7&YOXN~i&rO>F^4uyO=-yazRP(1nrCiH%W3|@tcI+WM$B5`UV@}<;lOYlTQrsE z1%HPS`}3sRdd5o;oe{q?KCpubV>O#cD`hsmnYshm*~woVf2~YF68WKzYqmphmPn`|sxxumqVl$Voo z(!!1_M~!GmwMJ5`9(XyEy)D3(bcd<2v{5A!DrDL^rWxaecD4|S7 z)F((xOc_ik3_0SO6Ioc6m~o&DesVgJ+0&v1(cF>N|2ZyBa?}$*Acj?RFmth#`LThY zpHeTvdinU~Z1UG_*fFBekxI2gNak|yGYUR?Y`#=<_8+Mm)b_H!Z6$@JG+i7^A7jY2NJ={Tn%p~}f( zbiORjnGgM`yh#IX?6;Hc;lINwgnN~uj{M*z)q(|g`WJ+UCrN{Pd%;dVVeV+NF_Y{6(a_4@%fUkR)@80C|VV7-so$iqf4)D9JQP9p0D z;8bHT+b`SPGLX?@5m&&>a@FJ_Un&=A(j)WXjGk$5ARaPt)$g(I@63T{Fqi9&I?UkO z*Ax8=Ku?o!{EmWoN{GaUA_gA;crNS;4->?&J||nW&eZyYZ1&l=ylfjc{BC<%q+Dhp zcNMf%vi^aLYROsE{Qix1Qu+sttJt2S-b=K=HFfNr^P+o{HY*h*Me86oPcVmhrDIH{ ztQyguU^hTQDNWKhU**` zttA~ONwfX=mdd4VF>{_|TsSDxwqzMt^|>y}!;0`=q)8op@btEcTl0~~Pr?4kYij^$ z!US?6LmR-?8;W>0k<@O6a1iBpMtcq5`|OXS8CceD116Pxchq=HF8W08f9Sc4OY#WZ zeHFd^j#3*izI?mEYw_el^kmrHPYp~Sng2a_9O@`^4n`i6p<`Dm1R@^I5jO?$8|LZg zM){aCz9amb{iK%ZFR0*ieQU%WSD(w#WG(_8orskKlHn7nE;t1&ycEXlswK0*B+UaNK)d&qA)D``ZYEbG@U4BJW#XQh`dSA zoCvy2C~D5JMXVDaH>T}Q4--=-`xl3SADpX>M~n(_u8Q>G8e6l#Dpn%#DWIzo-^fX+ z0#UI4z74vai84>H4r4{A%U)xt68vd>K9D~7!>HV-mi5xTce|`v+Gr=#>U5#(MNZe^ zq7r|M(%zO+q6s8hIU>yl>liS~Q+zSW#uz%^%-vXcSGZTvMgUDe$$FiM$xi0!5 zm?t*z=c^F{`i)fIJm$fQMuVo4jbm4zYUT3a1k-q!%YI!_y z99%4~$gbf%1WI%LrZ>|d{h(rP-O$4&+I)I}w7U&-V6YNb1mw?-H$^V_$@DQkZrs3Q zqI`M|1nWBr;TV6|CIzLj#=|AY! zc>BQwtjb@ob*pkW<_WI%LOS=Y968aYCL0Ct?|%N`%%}P@I7-Za*t&I*{-k45mT5DrAqrI21)QGSPa&Yi~(VZ%r^-^fN)T1R8D(0Ux z$0QD_D#PkOt_CPxn(0(!a4PR@_v_3ky%s)yY6x#i`MuK62fs0Np|l0_$n=Z1Yl z&c4c2N}BC>T9l#2oB4G@IWkbq@%6ocZ&GMm|aoPjhhib`}TNJo>w;oQa$o~rOBpwU>y?3GG)__Fpg;9_*>AxRbj?e5s+ zM6W8PM@l|#VOK@>U^7U`htf{Y7{0U~!G4_+b#O1wLmyEOPs7rMaN6 zTik_9+*m}$?Zc(KJJ9@#Zs+|bUh?zlOoH`8UnS>hH|j^vEsZ4Lh*&W=k9(ENmRiQs1tFzoGF;WaP>S06b>iWeRFyI!HM>*L0EyR)_)70Xl#PjWpwAYPxc5X`|Dd2=D-EYbmp^Vb32c>PcZ2=f7#=OE`+{NTz<&p(gEkGMbBtv3E1(qcKg*T>dJ|Vq zF@hl1f;KaOw|~&Db2)|S`XuvfKrJLcGyE(h#S9eC}yQnyJ4|NN5ryzF{YM@k2) z_uc!Ee!SxNi_EBl3v?ED2kAYG4-5}RgCgSZWMj?N3b1s+SLmQy=l&Xs?4H$~-S7|+ z%%AvH`&}Keu5z=y#?z|fZtOuvOjdE;8P!L}0!qGI9&X}4ZYO%)?G2qrL4$s6y~*+=Q@YP^7Sf6u@LORa}#O z0Y>?%im`)u3AZ~`iK$tOaRQnktANQChNzii#2hz;O3uC?sMEW5yMHn*z_0B6i%@gU zhB-U2NXMO`P|sx0UlF9X>UPL{V{|EVU%!j}`RBtuqGyyK*;LNy|=*BR#jht>V zuJx1cWYpLrutEe1&gavqrS)NtJGIe);|Y3uS(uVM97Ci%#@3NIs!uftn%PlIMm${` zQ2X(B@)`(#z!KHJLaBpdXvvkttr$ggc+xcsPGXK#Y~P~fZhgF*3cfXB#<6lgcm}4` za$uG@zz2SRcSl${9~(43*Fzot`t1VlQv-LaFukxQ$9amJj!~@n75>hQFjaxn%s$z; zlr$dOVGS5FaD%!5v^3aOmlZgj%Y6Cv;a9SR%DVP9GEuCpY9l)jD*XAE1!wLYu@a_R&`qr?!ylRx($4z1rtX{;FUpr;|ZlG#D37y#_O zwb?u`&k<&ARQdFPAiI=amgfT|t{dp%)n`5)CP5VIVnJrI5QzlLWoP zESv|e-!V#&PHn*hVFr12ba^V2jJtTr%9e$@8~f@P%y*us{%fty zn-Gy=j7`*?q-t3=yZI{cYYM^1@x?=Uvhs9^`un~yMExa(vRu5OQt*5PT=UPz`9q;w z1O7Hpl3H6j1kDQpsil_-e@Dj`tz!`|4&%Auh>^?o#;mShMjIV@Mb(?jqOmNFX5@ z3v#w9)sAEjtEoASn~Xn%#X^t?;|jxU{po9DmU|G>IwKd+OR@5M$ap@Z7vsQ9l{TE9 zQh5M$tJ)kxuLq4p{kGmw#e#8<8@XcO)HKKLXqo|Z;mt5Hmiu4G1io8_Q*@%>bN>f# zr&Be4n~Hfha8cBfgyXfn7sS9$fk&BGSwYH&v+$FqRWES+#KIV1qYfgT3MW@5_^5;rUFP zFdzJJ*)^qVBj#OtuhH^RU#HwJMfdR{XpIZCD$v*3GkSFy;J`&n8pV6Z4xe{e1?Ne~ zT%32jLUcM#_B{HfB>eq_+ym|M;iV(aH<1%@{@l?YT#n~zsJI3RFGx1+ladnsxT`MGmTwF6M;kis zpt_Nzyt_>+K^V4!h?nXtFezD-#P9Z0^oTi`UOUXX>=#(;)%$Ccu1dAto6_9h*$Lzx z58&muU<{I>E+S+em-#J;0yFi+9La%U1+w~$lKt3cQ}wKDW_q9WS}^<&<>TIHt&UIx z%wbI}2n_s+bK5bEUjjUY95^OcoHZ?#^5js(c2fJarm-y+GL%8dvsAS`Qw~Yh4w# zrm$W35t0v?JZNVQBSqpkPEABE?Jr#=e z1U48?_R$6ucGrXHrS{Ukdb@>>3=L=MTx6%;Z8?gF-3Rzo9)30N?fqeanV!PyNs<+t zm1v{I!3$LmcTD*z2DwRDz{DIGn#>Ik4E8nX%?{AB)2YI~A9kuG?QylfJXjo_#8au1 zm~f(?{YF%^Q=o!o=NCH?wJkKGid0(>s~FY~anfw6->UfOVjajA`Yc1r zZV(mi3){C@;gW~v-+Mb+urmWNAT zraY+3MG^W!+DK=Zm%lvcS;wKGctNC)bgyc=S8i$m^l7;Sp}AffW6BbKgBh1MhURTl zl5nH=i>w9Ae+RxCFRAlU1u&XHRtJZ{ckqiFZ6 zlqNr$$M18l-Xj0XN>}hbxjr$kbwG!)G)$lWxP_&cu4UNqhdE%Ta2XjUyuLfkg?Yup z&;Fu?unncSaw!GbXPW0cn{rc>7(|)O8fUn6Kl&wILWHG%6MeffzG{m-r$%)haQkt^ z(p=8Yy0x~nq~fhzs!x0IEN8u)&%OCoadpGTB+;+3`LgaQ2JbGJp4bKvKM{a-DIdn; ze_x%|n{-^m*kJQiFJ!UJ*#m6e=E%N#KMcMq3R-6*g17~l2_?DtVr!m(R}ueQng;3Z zL@F|xb?X@2w4uqEdAY%U-D;7u*xY9NDtq2HDRgT=M!Rn(jRer~Iysu15FeXE$KsLx za#~C)2ybZQf&%GrvWq$#8~%yl?8gSOv^ezk-}|TI2emTpcbnv$b)qUU6=H^n+zb2x zt_Yu+S5dw!R`}m@HY-yZB?(WF>NFx!{AtsF@Zs%ObR5oG45G@PXYY{h ztch}0+JXSNXQ!k0;q8g6n(6Jt$C1@2)@HT#LKS4Lm$_Ly0a1Dx5Sxfj+V?THhv z!YUi4&xw7Ghbm9=msY9;6?^v7UKh9hv->;KA=AtI%P{%m6;@)6Om1_;nhE+7rq6lV zJZ>>$jB{+;RrKh2Tr1u{fHLaisl-W)+Y2bo&cB2|2g&05IR2pQ`QMtb=WJ+}X&J*> zY|<8#Uo>dYNKCPrH`n8o$&|*U9d8}?<-*+O0E)-L5{^;)FCl2YBGg%o!;&7FaEj1$ z)%=oD0~Hm;ehLy4_>y&o7 zJzpk^)(C2-8PYc>we&}63BOl7@U1;d@s5lCkUv6z5I2jRlqGWEnHC!;ROiF?N91Q# z2>@T}7tBWc#JG?e5wuc6Y`1Fje_=?P;Mt>rhKjEHU^hd@dWpvU+Y&Qix4oRZV|-CG zb&lXSIlIlVsR2d&__XVs|^{$kc zM$r=_{G8+tARWy2Nk8WO2zGZV`p(6m^H(ZGNRh8J_Kby5yd_!=>j_D%O|H*`#cYK> zY~ALyN=>@%LIm{)tB`O~f2}MtJxX212L;powV8u>UYq*O(Oe@P?HSwHg%<#b=*h z@mKhRHW94v4vOhZi@ZWC|Ga znJ4p4tUmt6tGR8Eo75+wNF4|XCm|${Mc++mPdT~*r@f4ws z{Ta?TmJa8Nm@frq9tfCXZ^+Mkm-AS*(3R<}6NwX>PGxc?U*?qo-$l3dL4R4@5+>_H z_H9@!_4q5*9#;{C`<7^RrG@vJfQfs=B11iEh6Lzuo?|Yl{}otF(5s7Y0%gt(u0{TU zdx_qqA{AwrmBv((wBmT&iN}4%T5<3!sUcNT*6)sq+I9bWYgoqls;6dubnxu^3R)r@ zP8bK>!}CAN*CsjvI(EI$H!O3CRu2niG6`<7=sZSTO84iVK(S`+o1^Hd;mkP#NMbm- zE)p2?>P1{tCCcVcb8apAXV+WqherDI;b+xLstIp7wDVPeu}|7aSCFJTCb2yUd$d+ofRDV-s)-mc*JxWK>8(agmMYPiZj2FSB}v9NU*p?a<2JQT`}zfNAy z67k4=gl@5)FkH8ER*rC^BzyO2ythx_+Ng#saT$dKLN=SGlH{@_$Rt+f8IH7&19;9Zu1v}p`}QRzY$1fwGV zhhCO8@#%?)VNhXX=yUpc!O0Z%kL|mA$r-L3en!_42B*bRWv~n@uC_00p}};R0UX3Z z;fR1Kf}r51E8N`t8l$?^r@4%2bh}pbIXin6gBk|f6-CvR7;8p-pyxv^o?tv_U4>R_ zt4~R*s>r*G**u=2l{Iu}*Zp{R=UHA%J~N7(l#b=;b_*xew8O1n!N2EAr#J>!6xd0`C@Jnj4$ zrB2ZHlj1TQq)VT+mG?zRowr0VWT=KZgzRvBx3J`JPkXx8QDXkf6n2UFQ*j3txB^m~ zC*-i7fK9gFP!oFVDbGB&!O1vV)=W?UEW?@o$Kbp~DCW|hEao50%BrI}ETT`v9C%<) z;vs_ThibgH+YARjU)2c5{#x%Icze^tE+J595^WQPRF8T9GihsJnR69-B)2u;O=p_8 zs*^=Ne%!h~P^wXmY~obX)41{5^`oZfJ^2re#ulKJwBZcN5;JmAu&4!2uiTX|%bi1w|4SFG|pahEnkFM zYQKNoH@}(9b*biu3tawsI-3BA-+Gyy)_^9;j;IxbP64^-QD<-M5G1@w&SIDQu-UiD zh^6!Rv!|lB0Eu>9>AzS}qgiR-1cZ_Y?fp*4Fie2x>mp`&llEzt;ca)KClEiD?(npK8#-_LdINeY}_rFVkS16U|t&l+qu9&4#vp1f=7*PGp5jZ>tK zIoAePD`#=_cXgup@cJl`_jMgEj#&#B(2AjVuG{YElLrHGyDRAXS!6tzD+0L={0Fx& zZ=ivBwCkG8e}yy=38t}~U)-z~EB9}3*FBE_Ha+AO%Hk+De3yYk?FVWJ|= zh|+)lITMj}kpZR7u{7_#O2M=nE2@Ym@upqkX7gVEL&EUmMTha8`o!+nH^TCYhhvE; zMxD-oc(m{5h_lW=l~})8>y_Em78@GsQRK=}Vr$ZTX|1~1J6Y?RyH*(G-_?GEsqC$K znA>)S8L@p_@hP%L!Ec4ZR8EAS%@gPC(R;*G2u8}+!saIePLl|CVlwTfe;jmh8dxTi zzjpkbeU&5W`7(SPnFpDaXkCqcW^=Hs%Z9NlR22Sabj`g3p3#P|6O?%6avo7amSZ?9 zkTT*GP}KRMVpJuEvJ9;g-JkZ3>3q|>_e9gcGv5dFa~j6$Z5dsuRSB!)=!92hQf{h5 zHbw^qj%UYofcRmIlFvT^BAOc3sXzPN*-!`H5Ty&k)B3tm?GD)Htsen{eAP;dbAp-! zWn^g4y{v=qUWW_jb57uPL#Uo^tt#Fo2Qwt#Cosy}MuGEO+$dt$OX($skD}|{yOgWE z`r9o!j9dSiAAt}OFWZwJnG21{EP9Pqo}L2!3kVnn>NRiCtxaci-{^vSo_8*dY#hp5 zhgJ4hl5R+@uyEQmN_-zQv6_ZWAQ-%QdSAJS##3_2(Ts7-$E=1PW0m;99Sr)yBggFw zZ*j90Su|0T9mVE2m+(A{ZwNTN+T>>WSOla)5Y7*rMYQEp+Ql?!ttP;?w&cB8 zcQ#kPPuS2&_2Y#$j-T0f`NkTy^ev~J5%6m1rMYGaQA|!M2aBLYsW>ZcKRuqPYB6k_ zLL7U90lqI?;@>}XY7}zqb@`bjN+U{s#H`Dk;}jzEPiM$hpVA(23)%Y@7Y3F<=3%asM^(Ym3dJJI`N9Pf;v zcgB(BpbMnr_WU&ZL%@~-uLX)7zWz-cXsfh?Ud|xs3^M~Qa$jdJ4jKjR3xG39DyN-9 zC{9YY32Nzb020hYXd*`l+;q31{MT!7`+R99XgHKCk3w4dlz_V{UkajC(E+%$8uY(R zp*&LK-CmFS*RMB#sw`l9BT9XQBE*zM_)1cQ=D4cV?Qvf~pD`7rNBQoVasdLihcsFg zh(b5bY!J;^Z;Gi(?IuEPf-S!SS9tv$r|bSpP% z5RmZuwj9d60(`-chcg)oadp1;70o*@$rSc_hAl5hnaPjX{jcRnL5FlbZkrCoVv*T% zE@um&S&>wpm0b99OhpPs9@jJT->wS?&PHoL*8wEfb<)tpMCitmbe13=-7a>$uGsEG zz8tMmr}20(ZIVJ&4cb}6Cf;CeQqX*|#vd&U=jj3)^?lkV|GKJz>KDFLhwF;m6wGMa zR+npu`?9E>v4^mLA*T-+NE+iH?xI~yJ9?i-G-Lo8jCA;gDNe*P4^RvlHNZRV~d3D>z zcC(W4@Wq>v4($;DLwXENc}gybmHf2b$ZZ3~g^N?0t(yclRnyWEoBw7BU3mUciGF@A z_o>ks;^EULxMbW#zB=$or$=4_gp?hfYnjIrA(Aw_DPIpmyVo2&6OmHw=Bm07Wml+_ zyl_813L*^E%^KJ^_uIkhhl7L%otRttn+8|mk6Hu1#|%d}9G=(fNxCDyk}pAjD&ewq zQg!6=tq83CGy!( z%d%!8?w4-Lx<4!2kC?6s$nIQvgeX;83_pK>&6xNrTcX7x)Fr!5RIzUl)jaS~K|%wI z_p+F}Fqkm57CzuI_(F)!j=M3~=RPj)RDiuW@%V z0iS1ci`(M`l`(NqGJRsUlwdWxw4XbA{g9}!?tIHCqcq+{!$r-fO#8fodmAU2mn0@h z!wM%FY@E`PA9@=^IG2VD&lY=Crx&t4zkiI5=jrj|rcEG3Eti3ojd79Zny znEO!<_@*)tQ>SKQ!Vht1d@7Sox10q8dAT<9x z+5j`(x^osh{gIyR!h7Xa^fU_LaldGE%twWrf^em#W3k%3|GEGGq}bNZpFOB%4pGlU z^j4?k7b`jALD$24#^mgDN%22t# zXz2LEBiS5Gb-RK_w19|JOlB0bO3s88Vv!awzUJ;4Q}O#<9-FI083hrbh`)VA!Y$E3 zt23MzX~HGNGp%8HXM1>P-T2<8aL6kD%g zu@mVSSSadQdBkOeNgpv_AhEf-{}6V>cN=dUak*bu#r$u(s5Wnupm0PqG+9kPly687 zHzs}KcWw|Q8b{brVL|h{ro(#65_3gL3xW}53g>AHS-bgck~L0hjCx~KNxfN9yg-tJ z&t?Y<(3x6);8;G~te2eUSQ)aWWh4T(LL)|-d=aj6gDhOE4ykLwX=C#zb^GQ6|Nc-H zQ~C%@w9B~Bg$irfB4bl$}QRX2C@jSsE)|wo6p}#ybOpY z3kndKKyDPuhZsO;`fzXwJ3|MSZ@@UqETUkA5~K^qck3OIAEFumMJU5()A5J9N6Zja zDWf@J0j9yABq^91_+Cv(Iqj~JEB3CFg?-BjLSnQh#&wSJL6vtUuaxW*A|Fb({?sMl z)e$F?6AzMdt^otlt0MZsGNihxl>HaNpO8kwR}q#WPWN_7V&*GbfY8oasDR>m^T9K=A_2L#n z9EXkuLf^bHpu_*K;M2TbGW=1-z0OywaqqXP5|0*J|j;MsmJA)o&$?wfs9v+bmHQZs~B_U6l{Yu!0F@O(+9s zgn#RQof+gf>p#yi?kD{8jVi(vmtT|TAj+^x>0033-`uR&Uogun=d>p zv0Bm2Z33NlfgGV5Vq0F8Afeh$?+KZz(I9yA6En1=z%Fv$J@=WRn>Gd*MUKEQ9jko#JmD^zDkIy)P}8Rh>}xZ{O?;!^r-ia}yKu9Sz{X%ZDB z?*)5XOXOr?Xw~6B8f!7cdVFCx&em@JzXG1dV~Vg|kuH~CRlS*sPYJ-}P9vR4uqVrt z(7Z$Vde-Q@#0BC0$!0$$f;B>7YxK)2bw>6knsWptyST&vVp%+u-AK&}85Xog(>>V- zB7*47+uvw$|CW7}0`SL(j^sYEZsS0HbPfWp%|F}7lpO5PU-@q#Q?WPMKt7owXujkA zypo8rn#Bs$u6dgQ*5S6^+>A%KN8>tRO!n!U1nh+C$BJBEP*9r+6^#!?I(>8wNFj-w zoK8PWPld5H7kL^YSciTzZ_f@f5dk#XtPo3ht^}#XDxyNMx7MwnL~ZOYu^1K;{r)=# zy{`K_Orw`@f)Irgq>E4j=2usl&iIgwTHHs<&BZLchbG^ld*=?;$vGV4*m3s1$q3ag z{ktxY&iDhlvht15JQB?=_4c+zl^{5dGHT#BD$Ii8IIhV5ed5b@)%X|w>xQI&v^Yw} zG3o_{&*YlIkLew=zSFeVw!(xcXuZ{X=in}gMb!W1r%n_~=uBU6N{+Pn0Gf#lvW0qe z26pp|+zjBcvq^n;J}Z(nDW5Cdyw}xx1 z7fRbsLREU0{IF}{?`o6Ry!w!}@QGFV~($onzR5X2N37Liqz4KL8x--qGvB1#}i z1r9@ODt-Y(aXU(*1mmg9GUX4-lWoMrP12>`V`HIlcG=Z`xpQ@=C-|ZcsE7D1d z-QNs3kf@LKLlBcAW3*0wO(gcwkCDdD6OUw*YA{gvNU$*}@0d*4a6$@~bohQ%2&I)# zHpTM_I?BIEj`2qu2Tra$eEn$^NVukdd-T%ZsDdcg6whu0ghU8MAY8J*PZzihYV58I zpHy#eBp>loEBsnyoG&M<8js9%`p4(U%BBM;tc@B=gw!n5qV0a=0Z9zRht|Mm< zx}S+qRY#w>EBVUJ?C(Cx-22uEz21F!ZLl2GUmWGr8CI}=?)Nxt0MxR6OaH`sMxEF{ z=iauff1yQv`D%=)3QgTSd;p`UyBG&I;N+`?4+<0V*)cC}qST?x_RrSVXmke|2}b1@ zX@wOwXkhjB5aNqsq-_L6(K{Cm3sCog8dL2;(!Z!t;Q}I&03EJY;* zE1ebG#LZ=(an+cx(`2o0H&2+)PkK6+OF%(SY3#O7dDTt(VZTi}8k>YdmbY^??xWZ2 z_TY0+Hi(to#ewx@s_|>Gx~8elEnmoRsYv7>i9DT6Y&wVwW=mdXV?njj+tsI|^z}m> z6TYL#u>)W8$y(uBh&0|mp=qXINg+*s2;45bdco|^ehR0wV=Bub8!0MAJ}8V9H&!d# zC7)Ts|5Xr)dDTYL3XdrS)K;{A$kbla^n1NB>7;siscp39G`V3*LW>oVl4SiQL_>@c z5|<=#eEmqryc-(~)oY;iT*HGIV%0EYUOWdV{zv5jp0C?AqaxlC@q8+|YwzeK5hu|A zwYd2A-P3C9n+6GQt_-Ow&p;xT)DSY#M>TGWhi0-=J@(LryHWr9NleiOD9nOt`cVUK z@irbEH?_HGp;qGJ3w(Hii5{JBOL~fJR$GJuVkb8h$a(t{N7QzxaA_*^j6otEk*7(q zCS%QUo!%TWQZ<)V_5g^+BjuqhJ$wOv0WhW3WGoW$a~h}mm@VUtTsw(b#L8frZ;9)F z?CJv@pLTHe1rxwyii`*UHbP!Hk2vJubPvs4L7BgvJ=+&1O)q&o_~_p#{9;IqVHAro zN;Uw#RWkA7|H30d=)w;tFav8Rbc5?kBbDB%kA`#VJ|lcX9uKU)zFtDX?5lRhx%EGj zd~7s8qupsd9&~ME9gkVXcEvq7bFpioj0u`&vw>bC%1rCku{K#*rleZTU_ECEmS{PhT~t|FAuT_FcpMRRWFmcmY!B zx{^{B-Ll`7Fi~vgd>>MMgHoF`_A~eNp_W%A_GL*aWPL-GsJ_3tO=jfU7eLwQJ!PhI z#@oi zlR{kUF4L$rKX}6^7W`GtH(Mh8VCn$NK=)z6JYYi5=w~j$+C<2Cwyd0^1E{8y=vBt^KNE}J5;`XN04UtOuS80LhaD=hW|8{?9A|nOzzky=&E#`tE5x0IoW9ll z=O}G=fILc?{%&5EVHJ4Tzp}r;gUP_dI_waLc-FQQB9=G)c}GXi>!&JdYUr!#Cv;RJ z!g3dll3$oI3M|YoE&XPGHi&R?${5waj)OGjwR;}`pg4?c)4WMn&K8+` z_a5q5t@+q#a zR^`O&g;tk~?kFy^1kf**DF%3PHRnkIPHI~B4;%uKjAlVm-#-6Zw5GIo%I?jV+bZtT zj{+gZ+xTzbg~H&4vrcx+$l|QL`XTSFXxAB}Sqc4gfpf19EDbZmNdcHn1CWdipN<*ULokpj|WM4Jb`Gr?+; z&9}ui(!UjU-aDrg#%~M?^Ct~n!u=P#J(F>)IQ>uYf21*FvD*{Y3*l$V&T2i0sEW^P+3{&OSgjzg?xP93{?lYmW5rbjxEWd>n z2RFN%gp}Ch9GJC&$F$cOdl-KM!(V8N4Rz$Ih;SckaBoyFeURTSwN- zr_W~-w=W5*Vk)9A8RaAsP)@S6AL^?v%tSR=sxIOUq=vxMrbecgPn0eHQR>uG-=)QM zZ^8RNnytD86=cjwQT&=CTR**)mN7pDyRBR-ktSKL4fT*1Iz{}Q`~5L8PqpsF<1Qc> zYf6e)LBSw0yN6{FKK^-5P3V8|eS-;zp#~-ps*N3A&__k5B`H#}6Ik0jBprjzZ?3_U zIH(8Q!ke7OJEsKmH~5QCAuxTfYsW4if<6^{z_j}qFwki3cRm}?fW@Y{)fa1T;5-Zc zf)%85q7c{8%!-$e7v7zF7dOiVP6-b)c(k^=FE7fX68*mp|BB&UkudnXdN^_{UJ7 z=;pN+sK2xs79cxwSNk6@LyI#TX#}iBCu1%E(ZS-a|GQ}4k;bO+FxfTpUG2;}9Yp{Z zA|lCW2DnpP4y!R+k2mtC{vi$cA?2vXZa!}lm#On4s${zrnzhwr9;eme^NQ>lP=o!% zoB+b}CMz^^7f|#Nz_8wC#C1KK7aclzrvDh2Pk_#P45olb9ZJ5+evKfwE-kzygnd0v z6wt(3H-j?)+x$r^Ev$`SbXr|omyOoo!1DW^yZ2p|!e_CUh@H%MRhAdWwNABiEfNOr}w z+1n8Ov;G{?o(zdI4rBl}d>2{T={EMV)PIB{(<*&AKoPVyRv3@Z17YP{4C z7(9E9|0*^H{6rOxuh`}ruqSDc3pW*8nkb!=8BsFa15V6w<5P;d6@{cCspl}nI_(T4 zfNgoZ_Wx*m%Ydl6EpC($Pzf6ZBm|L=5(K0{QKVt$MrDYhlezw zj2_)4wW_#o%h)^>%&Q{H=Xe~xSTWG0f8#z(i?>zLHO+gD}mP8s)4 zzm8{1cwAR^1I&_)915bj+^l8_7S7h&_Z+502wehKp^2OhX26*xVpfLJkq8~3G_y4p z?&E~%T}n)3?Yq%0K#p{|s$d>`?c5GPnxmtl*pQ*xxcp!ROi{D(m7ty{Hd*8mIfgz{nc%LGsWph&+K~;s#@L3 zr^F5WzQ+p-Ywgrt5}d3%64lft?(C$#mGtjJ1$~DQRD{;}h31m(2?h^WsrbwXWU`SP z-QNQ`HfNgRwA+C6{NM)H$kj{Py7X-K)Mt?Oil%oCggv5@WM3gw6xlOOkEM(VBB{?q~#i2=a&T&Mjugbc^fX=D! zMk`FWn`+!S+qrT26u7(ZaBMV|qt$l9P21VyrkCJMCQ#6{S>VS8;&Hh#PG;)1dZ1?W zE#Sib!5dYuQWl|nb&hwVjv6*zq05gepJf)%-93wQ$}rem7Tzpnt!D%IE;-?&K9$zE z6A75{laF$N>;AG~kqqSfA;)j$mJN^^Gg-Jrr@@J*oG6Be{G+MM|;J9R&8ut4Mksj0O#aE#Ps=rx{h|S3P z=Xpl$4L6jwp#t1JxwvY8YK(6wVH3&+>7>V?Jd=Gf&;T_OdoB!jfMMZU>^4Ag3)vV^ zPG>sF3#cl59xcwSTb`&JJG+1K>^&zBV{U$7d3ePIx}T(}9XSfq@pInHtb6Xsq4A*v zWztrr0FTD|AN>118xW^_Iy2G4d$+R_$D8G(K0iPgL?k{-^k*+`JvKu`_>IJ`cSP$N71G*8m zg?-*MQY28Kl_AfAb1%RN(d?x9K(P&uSHaqpjue~d%<4Q&XiN)$3n7h*zOI5-eTL9M zh1c@KF+d|_paJ5m`L*-Al=S!0qcG;$|6bjx6pmSr&oUP~+u_kcc=|0h2HeI`2OSP}uxEoTv0`4Ixl z$KO2hh(%O8AC4$*FjNr)Yv7c#BMZ0+5b=U7| zj1ew2R@FMHe&pHs9Ak9y%MPr3l*Fi>4Z#(5>aZipo!Y-?sUmLDDcb)ZWq;0w8Y~aA zZ3O53#es%xAacF}#1ur)?-HTL3rweTx-jS0G+?|TgW~OG3dtvGSxiu4>=urqosQ@$p2>qA_9Q=+E}tpbQ&7#;$Y#Wu{F~^HBG8v0Jd*>~0ijV3U{K7k zX}y0b>+w_>pfvgsrY?U8F<`0+pzj=l0$8T+lS8L(O5DxZ2{`F5)&EPT=p(x{5k9EU zPXJJ&_%UwRI3b^Wf8!o2Ys=a5LEXA)kGBq{E9_x+Zd|fdS=y35dQxn~dTK-wL$?ms z_%EA^%fW*J;3*%tI+StQt8*ZhGX9LtQoNM)&3I--j!;MM@xH?#fz{vKhhvSLzb+p!2haKYN|4nNxmpeb zw*Z@U1)v1|VWeME;b26y8Rl!K&t=!C$ z^ZJ0tnn8oqWKq6@eHI-svjDA|9`bKsGRHk9%p1+8-%Xmg_OFztXV#AAso;NMYg(ie@dyylmf(2afLF8#vFB$B@D6Nkn`iGg*Xez8c4a3ZKxY zmth_T8`C)`AXirBoD^4B{ZWLyK)#VwaisVTjBv+5$o~sP`ad{- zMerv)H>P_G3r)~1c&zYm^43h`j9B|A<*98q(sU_};c`g8SD{za{r)n}2wi1>>Qt~D zWdi>Lg4;L?8`T?MQuS~yXYf~9el+ms;em-@1XqbgAJZ8v4vv)j9|pC8J?Z~2GR`d; zFX*1d{Uz=UKm>PVajvIlcI4Fg;knq6;PH`zj`+s`YFeMp&E(kd zv?~KR0PyYt?#|e2_gwcr<8L|7# zLe^e5sAF2tg7RmRrEt0tVQ;-i}rT2@V1D0ehOs zIV<288|WN$$YAI#?}E_=dF-!XQ}r1>ags7X0o-Zy{&|{PAc7_AYVHta2t>LL^!R{> z6i&xQT?PNqdQ?G|9*&4@PA=K+!gF`O68e-_0=IMr>PPqYqZ$IDNnehw zrZ;Lk1cYo@fmq_6*1FMXs@DCdSK_@-ZvvM0yLm2~Fx=}aH}3+<;!pZ*>6HK(t^5rt z$j*CtnqX6dzVH1)Yp=FO6yy;>$Lu*NIJZ3)6I3gtrFz8db61mZ+iYBrTFn*CS%?-Q zX#piwZx`E|`c_1jr&O-$R{k)45zVRjqDqmQn(ao=;iy$T$2ot(jVQ-4X=2{*8ouPz zlmP&wJ77@d=;!nlEg%ID1)LQ8B!MB)zZS?Z<^*RHp_UHKI|9&t^zik!U#wNf&}gXZNW>Dfd$I94W)m-M2!aww;4q7-6kapqm|CZy?hc2}d7BHU(AuJ{pJdw{0y zxh2{#rM&nnra*FhOwy)d<4?o$@*OszkUKDVAcsOC85Kg}^rbk7%*b-&q29pSUg6ng zm?jWR6u=-4)D%mJA^jQOM%BJN(bB#gFk$EEIO>Y|KD*VFHiH5nXz>%6YIREI@tV&O zGw_BMeG1vAY~_Nv<2j2svFS*@?HPvlbXEy7?}lxW+3U8YC#Ccv&toXWH8;yqGIaN8 z(ONfGqM*#(th!+k$^A2UTt|uO*XYjCy;~oYUH)D&j0124~xC)y8HWZ=&aA@;)X6x z2X1+7l{JsDTKCDC^xx{Tas`${Ho|A6vTdttwMsFD2My(2ve6mJU%DPfgNQ)>z{$S+ zNAv!n)g9@PM|01!v)R`5p?iL#;<*5u#7lykf5GRCM9JLzwu5rRUR!4JnM!~S!@#Mw z!6tW&qK59`^sv!d|Nd=-&%yxr`Lx6zVS`Wvs^Qxu))%;}TqsUEz&~$F-pF*7vQhde zJz(jXtXRLp7Uv$Yk+?9$?h>b>KBw*D5%uU)+lrUgd8H;Sf`#g+WAYQ7ObLeU%{tRfSYp`4I~@Cr%Ggx1&`H5{szp5%!~(Tc|UYda4(EOfg+_!<~_@55+idXvXM`8#=Fic93f7CMs8g$^4~f z{vwL>N(jm6kh4+d-cic5;)&n%^zjS(%;d<;q@>ppS&7U%JEBiFyH9fN9B*i|oU!(C z@Aqk&V(sd=gOfx$tc{mS(Bt!#l8;*A9ww^|l>X%RW)hxKDWR*NTJ;IA)_qfh<)|S< z7Zmxrj*ipo!@J$?xmpsUKiRqxXNfUuqkk#y*1O=GV$>Yi3rVCOpOY9YFt#c!uU4RdmF-BOo~ zVyTnl?jeK)B(rB6(4J_Ggr*42U5;~|QsX-+n3~QI<&M&}Z7|ZB@OIL3|4G;E(mWe& zHi6_rFRt?0*!0nv-Eki}+mc=p8@sPgj@HuBY6pcGthw5SyA9`)Vj%6lqj6_$VMHk5 z9#18+*(+b-o;@|4on#$oF5P5+S9#E{4BJV-=0Z8ZP$OqJIhTQ8%f7migjS#(9qgr;Uc#PK)M@f z3iw=sr2MR;W5}J`F5!>5Y1u+ckC20lrXHt}B`?>h`A(lR!FoiPcI5V+p-&a=$L{dd zZ*h*eE#=z(j)9H1D1__fE9V3&>E}kEDkPcsSKX5pt8b}_V$0h=gjGpN2?8EFJ3Cx! z-QaiTmJhYIbljPWRC9uyWZlPECT7Se7$aYcDD{IhuNGZP`s#bbXJ{ z?3WBRzjN)zW0YQ^jO5WTQofhH4eG?`S#g)Z)^ANrTf{lIo%RisylvHGCxH1d*4T}+ z9aMapo`B?nV=sz~VT@|Lq}u(#9HoqQ=L~uz-Hub7+x(?CT@ghyO34iHOAGC4e6Pn= z*zsUKtgo9Tz`qay`n11@rV@F{zu*P6Dnj&>fr&KfX50}H9JicGf`v99A&x0!*LuWh zNu;eNLyqDX1V`vajsXnK3~KD?!MPiG6aG#QC4$IL(aP@@0YAzu1+mn@MNX$fUp-6> zY>>m_Gw$ZCqyFcwjrR9LgpZa>p{JgO-Z($O2BMm)WnOvm_C= zGpKgleg$Tf;|4wa?cM`lHn$X@WDPFnmEu+iKHmr=OZ)R^bMtdc7;N(l*hVnJ2KODo z^Y1um;XYq{{&|>JTrAFI0D+90!4VMdf;nMyaf|&t|E25!QZ$4$6nD@Oj{p9J#qD-A zQTyo^BUKJrf9uNs^(_)KAMwGgP=n>t`B)wBfctM-THGc$M_!zsp5DhIfeY;{hucus z`GykDcYVIk7?C=ul|a@ciyn3y(L(3pIv@QnCc1*mMJLS7GE@W4CZQ&VacUp0!>J}w?RFnfdr_R z?Er=KtjUMytZ7h`mhL%4i~Rx+k@W2y97M^9Zsh+Ars`nNN?=mYRFs1m?t6ibxCda` zxY9kVw1zWK4W8SDplj^y0OpvxcKwM5*V+sHa_i!mP+FnxZ=*|v4Mw;m)eAviwgz0o z8t5z2NudqaFbC1TDq)kK~ac zIQ(s*whj2b|6RbZAT^zgeD%DaO0lQ~CLrKoE)E!5(-C`7pUJFYD{-xr$9scxq{1$l z&gzxay)otYE1(3+yDo+Ug z)rga&AF&7g$p;}Tb~W~K$=HB{$yaK>qpII1Iqjqyy)34m{Yr-M0WE^VpVVis1Uq5v zJGjo9I>7vuDW9c4OI&@X=c+?;X-lAb5k($zrQQ6P&+==A(v+CjfnGnvnh@-qJua~e z6CAvv0k5z{x)$1U1 zL|bN)kD((=9v0I5KQ3EO+U97;Pl&=5udeK{a;EZxiSO!& znmuc*!u97lUIIwUC0>rnE%Q}cZ>=S_I{7IN>vu5v&fa*x-R2U_nbk(@F7oc8!65iHq3>&e$9D0>i_5+Mt#u$%=J7&$q;=Fn(5(l{ju%pDK}FjP z=#s2Yc-u@ZC=%uip%c@M4jFr3WG3jV?DXlAM;+wN^>Y8$fP@UvE`hs{Bmn0aS-UH1Rb zt&7|8USA5--iT-LBAuKY1#d7}(;-g=3%#9W(0MneG#D+xNc)wCT^nTE%$ts&_muJ? zfgpZyFeO*X`ooLR1wul$Fp)(zL^WPGBDz%_k7a>CVlzB!I11ljFV+Vj+X|Ym0*u=g zi9|`C`h3DF14~J>e)&2nA_Tiu#}KyFZS;{=Lj|a@6Hu)8!pJB|oCI{XNEW3;pEmEVjM+&1!Je$T zI2-q2@!}&3_eac=QAcM7bIBD&aQL;&npH5A3as@O!R`N0RfKL2XZNkkakgVA59ffq zI(Iw7^Iv6(?iHhy|@il2&BE~5JbSo*K?c0ByyDWzKP@~Q?uWZH$u6?9rj zqKdAlwtCqLH_I|C#zu9>aPDO<}tf|U) zub5s5cjFbRNS9lpgCL+p$@r z{_Gr!t*^tZlha$@Jy2{|ek5oJf&}^9X6N+jK6lX0$)=DfkrI;->6^iJJ1a;_HU%S` z$ZY)@AEh`)q#L4(pLhmzx?Ww>=Xn(-HT(~?)=R9C2Z1NFCJGmpGvG!rX1lhPb}SAJ z*Ty(@gFA)t;?f1O3y@O)w^$OMR>VUBlhbZ2`+auuc6eigrCV_u5O z^7_7)4r_Srr1z!lD@fY#+H?uVRBGo*nMVSa*MHeX{Vxv)_?)?mwXcX!-3y?;@){B< z`spK!HWz3Vcp+nX8+|=r{^ot?`o8!A#qGI>wWyoK>0Bn_)1QcL5)h2=NXbd9yuWng zIHIX%=%6J;T=fQp7H&6B||L_&SCTwRfJ$I}@m1;s zFICxBZx-yX<$muiJSDef_0$l2)G|Xo;Vb}MJfKij*ycU(3VcNogbl%t`|0iaTt(Y+ zet|ua&c}(6*SQG&rw@;FzbI96`EhK4Q(i+PP_DO%hi6FZkUdVP zkeYVenjI-4#+k|$e?ld>XLv>|K4mh(}Th#{SscJXOPuBHs-nFEFFHG*M|KJ-C5j9d1lWMI{*nUDfqMSm zJZ>(Nj+xly5Ih-9gw4l_AB3>PfxF)*bx3|+n7C%xE^sl@Kk5fnenHb8oLZ<#r5=a3RZtkcR z3qcwbB+GPdUgxSOX)Gbfl_P&B&DF{;QL91IT97_=E~2s^OMoV#6Fm#lEf~cKX4XXs zwF9}Gvx!EpdwyQI%XG_x)PmxjtJDK0*XeNC_$4W?II7!A5C+~WeGMTSBj#X@wK(O2 z^iOyk>XIaRIQy&rTYiyfm$fL47T>c)f*>!_sOcG+;A7iEMe5EequS=R&iYrz>MG@d zCj@CJNxrrK4QQA4l+>gYK?N4H>%tKWa6mob|L6;7VQeGE%H=J z=MGz7wr@CXINLkpTy}b`$K-_nVG>P(rvM?s!#CW{@vo9Hm6{Ha*uG23cR>5NjkFYR zx$y-f(~kK`Iz8j{ST=P=elP3kMo!+iPdXUS%Y2$H8C0sNcy*Y;OJ4XNixCw8f1tg zU8r6te>N$<;@c&-QNxSz6)*Cht(u9^x8@*}dUOrtYtSt#BeCT{;OKVk&Q?j!uQduJ zl%a_`A-4JLly{nA_54>0-qb3Rkjy%JuB!?LSZR#q!Iai3I-+Gl>a;K2BovI^FVe?N{}kVzn5#WhRp zi3-sif)6LaDR0Y9Kj6U!6WqtFz4e$BN0Q!jW7yOv#-7&ydQ+2Womncu6p=0Yg}$Y{$RwVfGl%kQxI}3giaLm#LB*zPwS$xx#ZQ6&V9cHnWyR!TW|8N zsdacpDJdIzJCNuI6Cc>D)*so{Fgy>r{ZhL0Mg!Fj38Uy+1J#d5q5U8;{og$SB3K}V zY_>K;Q;LOAJ+%p1K8mkT1$Yjs@@$x3^YMM^*Tmy}Z` zbgL&xt&C!De1K0Wc9h^l9*`pnQ3Lu8$VYwi>i^-dgV#J)m=Sfy+lEPJBl&yoe?A?yBf4@{f`&^;*Yo!h0hwNnGCH8 z#UP;Y2P8Iv#nW7iYE{Kr;~Uau)yykzq0JIy!|2DYulLDzrFC0BKz2cT7I^q-?1B$m z(4LE(ps4T1ey040kN-xfVUccjC-_a3`EzE$?zo)pa3+#;^*^ zFwGoXJs_ora@(6YZNC0}>NtHqCVP6PkD#(iD^BRmW=wbB4OTtSZYg0e;baDWDp=jn zTIU2=!?~imBr&HGB*^l%u9Cin!<8c!Kgyp`@lKUD)41h(e!T*_ zg)s{S`}{!nX@!l{G<*ARmLI=A&fb1m@-vWlAp5p7Y2|e6ZP1BB_x(o> z7!T~BD7oGHbaQ_yEX!2F(3QHy8k*5!KHQD#B_Efl>m}s7GFns5X zYTdEOxv-PlTQ3b(+IAUM`fWOmWd=WSHKkHPp#`J}opq&cV&Xi@cqpjau#;mgpj4u3fORdJ|%-ds?DaRS0 z-RQn97b4T2a^iT_OHA~p4nPla{=niwf;KIBU`VMafI)>90;oFsK}~I^)=c*&LciYl zCjit~2#7_Tc>%Ve|qJa`LDqi$m6;>5A{0MZhqPZj19Fq#Pi z=royeD$+I>9Uw@q)2-pVF4;(AQ3F*2moNESS@*;31`h|oJ#(@B%8O}eIrYRhyB7^yWJa&w zay;+-NQ6A;Fg|5UKIk-G9=`E5ua^qSz+3;d+cw0%^5ZxFPN78GTYt|n5W(+fyh%w} zu1CCK7XwY~{Ep50Zg@kN=Sa*o)HLCz%goHosSsCcD1L36vRmF*;6ab*n%DuD4svg4 zsIX#S-eYZ~G;7c`en*WMN;JTvDlxEcN{wl{9p)1-v)FXn4{yu>MU!T6#%wg4JaFYw za>UstY_GL@ac9@XBI5aoW?f(RkebFy$e$+4!%eqomx(=3_kmHK76jb>muI@!8)&8d z%{#1Jmtp)9>Ed|bHW7>>cO2_0_iKC{s3ZWWd3zIg^t%V5^Pe}ziQxt%R;a2=ho9Wiks&kV`>sUej3cHzvY&iPw)dHf4Ee5;1*d^BZnaFslZ*c%tu@0Ick|T^6=p~dVdUP(i?kcgV_VZ(ox{= zj%Q$~>#Nf&a1Nic-Q%_DU=MRaw?n@sepB*sh-=Dz(xRP?~(Yvqof5RH~ zfAX9MVvhmHLO6hV5e%yU?R)pl6W)FlJJ}xS&(-Fk6+aHO^L+a83d021*8`=4eDGBR zVE}kx{eydlfHxNcDg#PerFz_0n4!j9OU@0EhbH=i9Bx(CIcNd5(AvrZ#*gqI^5`lS zW<}ke);n7Zn-ksK^k(%8mJM?b${TM)eXht*UnKLs{Ubvy>Fv2yi`&rFo@ceCO|*dRugY1s z16sC@y{PYdla?!ka&(~Y#w@%|@@TV^>DdurH1FY~y^kXy@4Kj)(Ov)vNs`S!|Pzp+=*rDGUwhELVCSLG7F zrbxZ(!CzN(hlJx~)7r5J5w{fSj!ZGaf~ifE~q7w4SW(%36pon3pP962LSQ zsd@`bQv?T@ExreVHkMTHX=a9EysdCq`r`HIGU`s*v*_AFMKI7u!fcYQMWBfT2e~(m zi~XIfP6jZP|K0*)Jj!_~4FP`CCV*f6-aVA1!Vo9sR#JB#(hX3;3`rpe^s%J#I6VCO zxR)2#5@_lDe7q6nQ>K>RI9jt>txykQ;$u)^a+-TW6Rv3=P{wlPuvam)L1$gUpq#smWY_L?A@X_|=|=je2yghRDdb{&&@N6E1YM z8X;^$2P|n_$4BXz#qt#B!?x8oU z3@h2y%c^M(zLmM1YZxt!e1*hDPv0u1CCo^P@O?awMq}>vJN&=;GOQbIZQVlfx3N(~rWy55?eS>M{!-NYo z;F*y?iBc_^mzUOxp&`sz3>WVJT>C5aCuO?W`T-6e^PE z&_c_NH$AuHw2S)K|2$i~moZ}tcCSG@df8m!j!uk0UVyBI!uTmMaOk4Gf44F{i@r5G zu>DgAt?;y%y~ESkxR))1)JjR%jGBZ_N%zC+oD5h^Tu)xGXMvL&IYcb&`R3|gCdp96 zTKr}826uI%$i%hY-yog*^g~o<9%@EN%hjo1M4cxfIX{NbC4T03=h2W5rRvgb!+7UW z)X3*Sv*g$VTBQ+Zx@Tq$%Qjz1ewsCGKZI7h(-6Agmyw&*H;bIrnxl2b?~y|%>e=E= z7oCH3lL-sWs(y`eWSD#zIm=yI3GR;HJCi7tG>bpkFzFD9Uy5@E>Zfgy45CP&rktec3r2he zRkQI{)8E0!c3f09MvnHSu@@M$R8$m&cRPmpRrIHEklh)X*|!Cg`~(-HcdjWZ7#T7d z(vd+Wq*gat7_bIe3fxsP^|+uHd~(As^&L)Ok8WHIuWx+GDUrlO z;Q-`WgoK7L{t_p+Ftm#!qMc1N~?{P$99*2RveV#|({dP?7{Ko?UGj zDRea}A0M)yakZ*_Tzk*UgXb1ukv&-@g(w}t?*xyi`<$N>LB>#@ahb{NAszf#Fy)Bb zV~KgsFZU#2j8chrE|}Zw-RVb!!l@^!;-*lLzM0qvv9y zqo%|mKdYFQjMkZKnVB62-va*_GOnxIaqM~U&SGi5u> zW-FUR`1)4qUpw_yKQY2A1fkkfD=I&2xo3@*3#>0$1h0Pl9 zJ}8YG+2wr+(jE&4?z9_niquX%uGfTE#(d=t=Ud7awZOdI%?)iPUq0Km2XPOolc8%* zRZ(n|so*mqZNx!D4ZbEmZ>tl7Y+r z8Tg#+05-JH29YXuLRY=Mz>RBCw^Ej5Qrd9wA<^cSVuTt{!#p1V+gt7}(@UUBP5Kqb zb%L7sUv2@UmK3N+1NBwG{YUp+dA8J>95;J5zVv;#B$KxX(yk~^kamr#trgR!aAMiP zS1Y^FQ-mF@>CSZrJu;;~@U)oBx2)=JXn3jT=jG~HPqK%XcXVVa02u5%P!1Z*G(^#ybX zy2T1z)Jmj7X88Z0bNPKI(c%a8*FuK+cf;31!oT;j9By35^VKW3^vV8H%FMpB<8?Md z)%n%O%A|y-cpRqV!q|vxDCFWu;;z7FS2TPUHWu?r(YijzaD(RexM=zd+ zTQWH7MLxm^$@Muhrm=VUsYtDq3iPX5yAPOY(UR*~Qu6u`+AZ1BW_&GDJHAbx8?@sD zIeG3+R8jf$N2iPL3(e9m2g&Lq3QiFoJnIAca3a)_r>g$Qy&aZJgHlL)!MOQiZFbY_ z>`nlYmdphzF*Tbd%cU`VB-Fjh#He{TpX6Fo$Ej(uM!sEkULiANoXYIRvD)VPoJ5Zx_ERrj)wPYt3|!Yuir@s3uhN@x8>X55Lf z`R>_;7@Lu^g_&)9)V6ky*p?z`ghp|;xGJLHTG>k!2xKKeITvMCF^ovB^(GRVMZfVH zCF7qL@h;ycH^$;svnD!5a2*jwy!&f7L`CltLUX_`%CyA>qn~s{Hc$j$2r8sM#_2Fc@V+y9p z;0L3x?3*N?Q#XBzS)Ek=nc1mTzC`ZO9HkH(p9dze4IA%c=Hx zoHW+$Zol{~sW)B9(V{(MiZA?m5udQCUWk6F`$;;fd;6f~6@bC6dZk`08k! zLh=(S)>p2gPK(UzB-v*3TnO|vokD1HoW#a(KNtAAR_+U%}#$;G#7K^@hc<)=F-Y}Mr0Hl zMIbXQf-jS1#6bzg=f^98bQ9`h1!0f=mlp%4`bzRVuc{)oCUXaYa(XJ~ma- zZKzv&8fPaKPg{ghIbH6}wc3}ez#dfnZg1>V8JEipt6WkcdGCHgrp5`y~@mxieV!8;U#_8v`*K{VOM@)ntUq;gpbMT#Ax{^fTqsx$e!{`oo1eQ;y zQ_rN%}lox)GDrK|qPEQ!`5mW@a?xy~st6>q1?!EV+ zCvL}ZEx(6m-}S))dF{cA;5V*1PW&?B1ZWz87zCehOs2Q9ZbN;PldCD}L*=J3^2U*q zA=iP?3n(E?`BiZtrI?RPO8Md2$A^eA*!RJhJNcMwo>lhh6e5$dTe^;8?_#2hgI08N6HLY zSCG%PPu>abYOXxE2;*mYCzx08POm}{2hVqxvx()-S%~mHg_J@&UW$#j6xV@`M@ zH@RUj_Ql5IPwYoH{(H9#swvtFu`E7$v=u}SXoAn#lG)UitDE|K-Lp_LZYu66u2d&5 zWbnX^eXmcJAsXPg|E!-TQzP!Hv^Ia7|1M_^L^4Uq*6&?LRdyfI(Znx;mIwvsc>hG- zfe#t4GfMix3as)XP}g{Fk^1ZP(_15T`e73KnQ<<1BMyS@R3lNBp{2{U!p~G5Wl2bZ zwm+y`n?rd)6Q6HqXG7+zdbE&x;s?fc^6uQv-@#)I)2PzjaIZ}^{pxR_3-7Dl@RGOY zdhF^`2HK|f9C(EuGqm_B$Tb!>O<7DH@P3IF$|FvLQP-AEm@E=rz)RqR%IDh0Qt6Q0 zdqcoT@KGSAdIdlA=3rxM%C!MsHp( z8Jvy1i+b`#f-1Xar0lH_qqTCj35J#!MdIQfUeYA;lEm5tRp!$+cE6L~O)R5+7VRKq5@vEBkW!=x+R~s;dzOuufvX171W^2{3CRV&iH#+1_8gxk^e!5qc ze+5=rFZF}f5+AQpDRja!D*Eh=LRp>w-auyyY1h>)dhcd*VoB#VFOthxm(@+C-H3sW zP(K#d{&1kqHRL|~ou%jjZV=Dxsp+xqsia{~RJW>lu(z6HRyk^|ep;U!n3G@3OBL?1 z_dCt3=49oicm^q93MT5-F?p;}dGB%iF+^ z1=1`kP?)B?T@jd&fsJ7Ymir;aBviLiuJIn2O?hg{aB$_bn%u27+kLWQth>D@dssV$ z4HsVc(#@18Dx2`!Xn63+AF75Tc6_{F^CcbJrK&Nl>a6ed@ruNr&C4L}ZbmPq++c+7 zY|$1K0G#KJ&671B>^D8dQKA_9ylV92ig9OzC$7xQiHPQG#m=X+%4I>F&-}{PKa!0I3zf(Zqbrpn-B9BmdfkY>Rbz6-M|fz^4NU z3@?dL)Rt1Lst)7TPNx%;9{9xe#A(|2S&{dZVD*nlm<{}AK1kc}egIsyy!oeJSZ4a` z3^y0_qcm4l3{tj|tW!id$>1hW3-g(+mABI}V6aJIGj7)>rpCWrimnxfxn0FP&!@Se zR%89X^)3mf6Xi1}e+5b0VMUDc?skRH4<{5~w0y9?p!ss5$u9SrONb7AN_i0yfIzPUmA4OYe?(GNCy{Q#{K~fDUn;AWczeRzRrT5 zUec2Xp5>#2$#+%tPu%OK_X=P{DuD@HiG~l9oF+V<-LOL7TbJUagfv|H(&{hER*=Ko zswlVgU1>O5V%=}4z~rb^i*vd&Z!(IME2(yrRM23}6b}jX8MfRa-8CFvEexwmX&(X zFG$A3O#Xt6P6!u1Qsz^2)~at(lZCE~@lB-M1SnrZFIcqUWBmVVp@6W_iB-k6#iP!?VJv_Wn;sUyk$=kZ6s2FZgq73;%y11gf^ zp9636cmYH_)C&2-m{}Hc1~B`5<@QBJ1wDejX=dJ-7n8UeJIZ;2xWHddQ}-5$-+LV{ zWFe%vhU}O=`xWzw)X$0Fk_r7W(x#gLS&fs3h-ef8Q3d+$m*9$Y8}Eo(-NfgntCkn= zkPZsZV|&6e6WE^C>Y!&`;khWK!Amf*CoK_Tl1z_p zuH>%P{%zO$i8S&yo02V4odRf9_+&mlA60Y+J4j2NtL6KiQVuh$>yop=H-F>xQdP8G zYuC`tG*N^Q#y+?HDaP_8VUR8+!xYIP(3GZG9^x#`b)Si<*E5$yehW<0tgV<{<4Q7m z0Oi5R?d-ZA@$thyrwpX*nw4^Szvm18Ui&_O{KT%^Zv892h*-&rjfx>C$k$^!O>L&j zvFKxXy*!(g-XVj3gZfHQA_cPA$AL_=_{wd`%C%vk#--n4pB`RLxa|GRL{2qbGR0u; zJ>x64cJkHNqZhY`#^ha3KJAp!`&KJdTs^I;oB*Jy!TLT$WaY_gYyq48yc2_6M1v{F zlb6M1k(ruuFr8YZDoO?5{vMJ~NYvizdS*)$b|0 zs^b!*u4&RMS7}*ls@gqD%isad!HJX?P@%I8LODZ^cy4eKjyc7I#5~=ZNGYw)))Lg> z26JmKf>`DYLM=I^tILmi*?<3`gU*7nMLs;n^~V<(X^2sd69B5nS9=p(EfiTomUzaC zobe!elhzKfis!Swznd?@BgbXAar`XPJ{m>r*X@#cfcP}5$A9(GScZg@W(JehQ=VHu zOPLvM9|ljjG&vEI|L~e(v6~R#HyP%w5kO|xt{x%zJ|%cr-lV#0=Y8~wZ@^qtb<7Ee zF#s&A@3$q6^hFdUo=J5pU3}hMrI3*>^NkBv%9_bx$^0E8OYcQNpd(8B*zsZ!F`0$< zofk@~E-7l4iM3qFU`lk>)5~vOz&`i3R ze*9B}RNU{YB_V9Dg&3B-_w^-6$=7_PE@3*I{a`H9hgr4yvZ=k4{>rA#klu+19slx! zMtq6!M$FOclvxsgqO2hkHO%4(?mlF_*HdMib$_Z@#ws9=jD zXXD(cl*9A1P+ z_`yY$If7)gRCe1hzZ;!Jli2v`No~YiG7U=HKz?WL+e zG>xyeMR%Z`rr3@7B^t7}ZJnm9YpmiossmvN5!m6_z-ZTH8*O@Mw$)pW;-jYjAHv=} zp6UPp8y`YBrUN;ZNQe<~K13lo<$ONIhA~9Q`Se!Gq2z325vCl+CTATS=MZLW!&DAs zhRFH!dwGAphuy>OLV#rEr$QLkI~ zfw|M=dXDQ!zJz!*k)oPB2YJ+{J{kU3nx>!p!_*wP=C=DnV5&|}~??bpJlYT4)l<>9P9sV?lI z-bzUePB5VuFTBi72%afP-ZTzISFDSwRgL4m!a^GU$jK5^Y@Kf)NUgx|#TP&*{;P6< z%u2}Y7T43vd6j<|M3c^(zpj%H*-4GVCPCurfX8Eh3O|GUN@A$|p7T82qoYj#15mwT zL+p%aPcIHx^_(noSh2S(SDlIOzIx~VnaM=xm3v_`TImWF>RQx>2u$yCbeW^Y8wL!= z(YY$0ph>+}qm-}-BL!x?3MKzX)pp0WoVBW^)FK2aD(@1-AL@K1M35wce*CdxCTVxP z4j|Z=3!Xji-)o-I-6BKeM6OCf^)ovW#pVV-Y|zw;bKyQ*XQPb{e)1KvMg zvz4DdxXx@uOy8&Mtm)Su2f^O0sc`ayIhjAP- zKl82B@p>rw+a(1S2k)FhoVxBHaNJy0E!fC*uo5t{;=~goIx3Xladyz!yfi04b<}c8+p0gtw(^*f0A^GMqkZC5P)iq5ETi1RGeI^8KCP zPGaOKU%XEU_u=%)WVx}hy2)%XSmt72Y|d{tQ`T>#@>kOQg6GpmJL4V-UAcJ)1#~g3 z%f5i{EF{lWB@?%;J=++xXyPzD8@aRtV6AuIC3epcdW~ZgoSuKxMzKX6(_D(&7!N0R zA9+E>L#ID3=Z);xU?5N%x9kE#uIR6DfV0VT&Vmp!die>>g(E*`Z!}C>;m_EpD~Pl3LF0qSOmlpno8wai=>WgdZ~XCVti?XJMsZqot;~d zh0W^BCBe-*6RmGs%{X47upo?>Hhgzom4BLagAo+4u>i1N1|^>zpr!Y+!vQ=o&xGZ4 zIhw}@UTx3nlCXa&07kHDMnWQ<=PfR!*h;C7=z3gdW zeH3f3flve}wtI5tQ?P_D8%u?U-S4fu~QmgswURbKXGtg05Ig)!neLun^5+w~0!-F*aL4RPfoH z;B>tfb$tU}+_x61eol@@D zy0Dm9r$#G#Qzr!3Jm+N2b8}wxTYqF}vb`33xecrs9SC&4`hPN)a{~ zS9-@j42%2xTDGjH2fo3x;W^-NNFMD;x zIu*kUXY5yXyN9kO#@pSygcm-#Zp9#RiCf+8;GbTCDAegF8D~3;&56|iFw$_Q4nT7G zp75}uy3}DV*xGj9>xzoE?3D8;6Gqz`Z|TqAY+iA6w)n~U|H(NBOrn;&yVb7!{>oD`dwaN+`as}tWQLaCMw%XjicU|I*l}OX} zE`$P)%(02S8$7H25N`_Y=QYawU}Hl#pMt6ygg3}Y;M(f@7xFP1xl!gvuZhQ4@p^FC z(7X)dkU};J-3Lb8meoFSbEymE;sWkV5habsh;1i(+yC9dNI|c`DtOpIT~O@#Z)%fz z=;pu7!DXIz_UKq)hW1+EF(1CRp0`v!pLdn*`UZmyOH4|=^yhfUe@|EYYC33Dl{a4> z`@KHVh0fGIa%kViUjyb;uS+2r7e=q2bW`0N4XUR7tLW&%aQ;F*g_ud{_t|O9=X*dM zzX9PFO?KA&3Z6hvJZ_Iwot37}M~KZ{?$No)qL{k}(!w1`GlN~T1Cxq_DYBfkc~cF1 z#t3C#mufNCyU0#hI&(Ztf=;(;%_Tl7S$rv{wY4cav))^#oGbNuqca_eZH`YvY%3Ls zwxS09o>@BF8cxlPSJHJ}n0KKw6=1@2+BUM8)oVQ@^d)znE!tO1mI@;!quB{o|G`QJ zks&Uu`q#>RFQ7zwyjOs6j%p*J{ae39hB4eUhb)d^<|}>suC$;#|GI>OrOzkPkvAbx zu_RewF0}-9!L$pSs+C*k^R@22xZc13bK!`!4{Xr`hHk_7yakiIV8*g`D1yp~kU6-a zWT`|fs8=+Y9G{3IMf#UL?dLXS?Zu3IfbtjQ^C67#%9aoPVs`fXq@3&P0l58EZq!{4 z3e+@01JtsYbl7tfvTwXIM2A7S0&hNK&%JptVqx7saXtLsMao@r;#;oyO zXuv)g4k$nVdwYMA4yKFEY+&3h+pa5xCEf-r_Wmw4xg?sn$?VJf5$RWy*!k{O01{jI z*3{Z+pzb$k>+Y{_PyBpgukJ#3d2rXUH*T}R!rX|2{)>tjI)FX5^K!4luAW$Ja}^#B z^t~d>LX!J}y?vk;pEiGVCA%|Wa(nHfZ)d}OeH(xx9FTsh(c|ULiiKxiV6Bw&A`JhT zT_0t_aW0}nSC>!Zgjzz596iH8bdj%)UoBhk1n`~=8XH_P=PsUpVYseiowh14dkRkv zm3(2}PuzX4KNO=wFp)1*xPm|DZTKP)w&6c!UDX8LAi6SbzLTHbW?RgFGhqh&8{iea zdqm;X&9{LA;naamck07G&R*Q&cc=gDq7d3N#Dek`!fYf6D1+G6TI%i?1j0(+_Wmt# z%Z#=N@Ppkn{&1?(ma3b;B=nXMQ{wZk{o8;S!3u)~o;u*g^W3noyO9Bw6PAveTnK@S z6mugD`C>(u2ADU&ey@xB6cptYc1FRhup-o!xn}M$T`nj4A8qhP?eU(?{Y`%M%j%oB zhW$?i!{v-+@oGhn0)Z#ro8{} z0@aq661aXiCsl#WEVU5oQTY3e_oMJQ(g}QcVxM9~0BG7z`A#=i!LpVsqm7+w>?MFh zTyJP1o36O^iLY*0TB})GcXmAnmN+1NIYdVXeDjIfZ{>~YOLjJO`dCD8yIC8da^K3% zLDppX|3|D4}!Ue-NOpp=Hjic2sNOn8WG{TtmX!mP}gcY;Y> z11(-jb|KZh(j5it^%}N(r!Xzfe}sc=QZ`+0%!c$BaJJHiFW&a+|H0@c7)zqz)Rm2# z0V>2is32}2i1~?%PXYvW(n0lY8fU%UE;5jKV=g%_Lm+w$#?v`R6MA>+IFHsNZ0?VI zs;}<>micre&yCR7FrzndPLxl<<wC1uKjZho3#O zxpD?Q=sDBo{Phu$-ai86j*raDp-e0vFAf|P@lPH@4-*%Z`eKyBkIVm+=l^%P-mBWO z2&#myFGGyLW7?z*{!|@wP1&Gpzmf49>S*phpK2+g+*G?~q>MR^L8jf|T_jI7X}lAE-qJ!S?yD_jO|{a0E4J>CT$2n|dhffjy3ni#Z@FEBD4*V~{ooMb z+-RZ&f6~-&LQolXa|$6s(1G&?|KmzA=-Q%B2)O05IAzX+mEaQAf(N`omf%vDFmvOn z(sxU|GENpLgjuPDMdi!9P8>EtVL_(&WMw(vj_n2(7va{tCmsj;58n*m0$*3%M=%&?LL ze;feQqjtydqXA6bHiFBiE~98E-c@g3Q%>!Bx$n5T)?2;7#YJ2~4b1>_5L}=;Uv<;*%;@(&Fr3)dz zQYN$n&i8Zh#-Erzy+0Asth06{z#oGFYa(}yO}9|qNLL{K5g_Tg`$JS!S7)C@c;yV8AP#^x@Bi`lsRE`L?yvj-&kn5S zfBC2&bT;Cpw$YVa8k0k6B~SD{R4RVyv>8cF7d#>Z%l11*ySOCxMqm{x(0q`%cdpiG z3b+yO2d?Sb)lIf%N*X((vQRnCOA+#a<~;#4>?cAJa-4dUjNS3Y9X;L`maaGQu&Y?; z#6redzsmWw03I_Z`uW!x89!tG#Y+Lk@BIx^KrMA=AxnMi?P7LHrV4b%4$8|(DpRe6 z6BUF7Vp1d`vA%0F>{fb;_=tnuqb_J5a&8Jn{gHuTfanZWun&?E^iA6&MwQ4#u73zw#T zdkq2o_lI8u{WlSOsdFY0pnT6qp3PnARNUUmhU40Bk)b}+Fu9R?zDgZ7B>zU0qtVm& zH&1>%-})ao?*2`#oX*w+OhjpQogR$;134ISx8eexma%!!Pzv-e#WE4&OFno#5{UBa zz$=XFn@wxmS;|RQ703O2WHOS0u6`X*%XevKqWGKBfWf+|8E)Dx-dC2_zE9 zzU2^}H|0=TS{kC&0vLb-P_&4Z8<^a^ij)0>rI9eW=pyNn&OiCIH%k7_8~*)&h84^A zPriqE&%fvF)TWnT$)z{L(n;^PoSd({ZgKJ$Y|v7XV4?HeQrs+fxa7MR=u768)NuwR zMKeb0MjMFsY!P#}Xi01Yd30bBSOaIak9;Icy&k6GFDmN0LrQKvo+TaVRYX>qu)KNy znxS!b{+)Q`bWBvKz-23NaG0+{MrKWAMl17y_*l}ee3x(NcjfwN<~EwNgT2qyh;aYL z_!Cnn10KMHFkT~9#oz^%^#Gh=oNCN&ZkZ!iV?#5#IE!2D?qjmA&{x{N|qCd4UZM7Fs>*3<;19LSbbanrSjW-x* zMExn?r{K{dtF2;cba+nPVT1|rPy$vvuye2LNNM}p|??FVeFn&S(Uq*lVdOREspd$d{qhTFWz^<)PZ2?(b&@P)>OL+ z@Ns2JsKUnq2GUZ zaW2#V%;cnG==n}=AH{3HuLh$E?Ilykoe@BLpctcMjR~YOyG zD9PR#@Gu~y0iPRWI}{W7Ak%k}W2>&l(F45rz}a^E@@Wry%dn5~pt`ES;{K$Ic)cv% zg3BRKoTo+H|{pA718W zR5N>ze3}+ceug!Axn6KI^A{zT$Bi+1C(`fzA;nf3jC+b>9UfaYsl5GbRyBVSUQ%fr zbYyPwVSX_5WCwQt0=~MLQK2wSVBqmVqn77y9O3}t_x=@HbRqJ)(lN=2J+RgV!&vft z9YO1e93W-g@Be40{gsqs+Ht5QuWW+7O__^KV_`Z}L$><(G(yf{kl)O@g~~x5SNo_= z0j|P-&Av2%*~k5x0Wxf`M(gTZV^6VkTTg9c4I5~mn%f3(pwPU_&QD_)ICmpV{;Zj1 zuITUIK@+Y zb*1Cw*KQ%2>gCsL3Htl~3+0soyxJ|ni8-DWBbo52csoW63yd6i0p$dU9}}a^Hq_4Q zi)Hd8Uo8&Ek+M72l#=lLfPH}d-qO0@8eY45ZGDKAbzVy3qmdMna7gQqINDvLQT)Qc zRi}O5+z7o#?o6JTIyexN!bQF8d3nWuk<;Jso77?%b58l?OpVB$0E8LB;zBsP-%ll)>tW`|^;!kWU`!?CD2#zDL48!i)&l+> zSims$1!u99@~o9&saYE8cd9i)V==!YXmx#YAMBneP#IxiorBPRMzr2?sVzX#bu1?{ z1gn*z)8~C?>;B`ub)D8C!0y5>4#nFa49E}n{{1~xitIFup#S^u8^3dRoVJkgzM_oG zgMJ*2)_gNICrGBym)Ri!r>1uE%vZ+-ukaLe*19(LZ^ z(x{EyYrx3doM-Te``M}As1B)d>6lpovRY(rpi;XEnK0c(5#E`--y2&YFynoaa-#w& z_}jVI-pA`i>#T4YVUb06aGuKBl?`Vo)2~xlCT*XO0FZt%&*ID_#q3 z&M`-6THyI1xnSr%f@&p-cGt!IofX4UDYWsz-MF{9%sPsL+t?rzujm2Ivgo&u9Vw3eom^j+iyp}L>Dlw3{O)k&i0D?@#A5O zk(pV#;4da^p%@02-&z}=>$wR)I5(I{xWauJdov!X6yZSO<$iw!o zG-!C2a^|$a6t|KLmHA2Y;RSB0Cx5oI6REX;%%lb0zfh-sGZclo(Ja&{w;Fg^$z&;Iv^+|ist3d0K{SOmpKndI zb|PjbX9xL9nSulud|jg2H^b?&WAByGJ%SbUt920U}$lncscG{hU^~ zFix)_bENg?`DJ94f64LZJ-QK}Ap2sUX~kL-b6ug>rL7$GOKb(HuM@j!lB90+5Cl3d z58*w#l>Hs1{kbpv7T?PM`2+y=4gi<7s&`02kUAGe$M-UyHT;wCd$mcSXKzkvGA*>! z0Xo;p%pW|kckRfL_IH)wKk;(0v`;-oyx%v`?T8zt0hSp2ir-mzsppm%O;QXGH}(_}&Q^Tf(jKJxTn>M@eRsHuFUWIi0`OYDV>Q$TG9nu8yf@qW` zw_-__KHXv&uy<1jWVRe(#shGkna_4n>gXn}4w4shCA!)Ey^GSm@cUiFp<`X~P-P8P zv%=qvN?0%S~Z#7iolFiEfn<cz$x4AmodJP z_P@^cfqH{`64EzL;zmKp0HTWxel(wrzu22xdj1ZTc_FQitMOiS$G7U+!yk*=zv^8q z39ra-8mAsuo_1Eb7i+)aqU)XqPWTZ!wft;!?@NfwN1E#%|G1AaFLV`upwW%;;fP);cNLds&#qDw?ow>zM{-nv&v~md&%b#XB zPm#2)^m_Vyz>ugjz5?Vs-LWwjTT@e}yC2u0jqFuj97IeMK+zlE#Rs-( z9UYL_-o95lROXeF>H!lWx4^>!+<}_sx^|ZT+b1N z*Ze_e+i~asQk6gLy&o}y9dOKzM1QWdDq|On9y_pg&}9k9C8JtM~L%1!5D0_;%5 z`D;~%Q%rT^(Oqc7dj=(g=edjornd@IkC?0c_kEYX1S1Y@EPlr=uYVS4ESV`_RUF!i zezwq@E9Y{VM!PGvAo6~zlj`gtGjAacX#y%2xa3L85Sm8nT9{ntPJc~Q*lJnudeAY2 z?%LF}RcLdnzWO$_z$|n2i#wTmUWK`r@-PlijG<(zAz$g1 zRtY|Do)2b^O`!Z-tA!Cgx7;_{Z_b;BI8`&?$|{%i9G)4}Pb)+iI~nmVQ<>^7`%@%Q zG{3sz-_IELu_sWE@qc;|`_W4S@)X96+8>MyEjhR{Qt!l%@YO$_v9r{yntZgqaDWC< z`OZwGEQ5RpC%R*S-OgUMFJ8~+jdj1Hm8Jv71S0M!m*CmU(_+HeXz^#LsfiEHS$bED z==EJBUA(dd0aH?Ve|X*F;^itu>bjNbb~drh6VN~S%9HPTd|-IifA0rJw?Sk@WSYYOoUkjJ@7aMCr4audw_>(8L` zvIfj#c2wTW3-lZ&>wW4V%krry$9>xl$!8C=q>&sW_h(wef)L2;z_=Q=foZwZ0Y-&i zTpzE`_1&v1f=unnN}LAfcF@LQK{ZFUOSe9vux{>5rJX=edX9*>C+mwk2S zpXplX?Iw70f?fb%#m$RmTSGRjJZWaqVYXq9WBSHsO7=hwYDK)0ezLT$x^46hc8i~! zW*)0@>0sI)R=<_%0NwsxMJaVd!du(Fet2*J#b|4OyZC7`FeaAUZ%k@cUe@niA`Gh* zqHrwrl};VRw6KLBqWtB#stx~!vvz)dv}hBEEH>aUWvbvAUj}Gc;OOi9i&l!6 zeJ{|w&~}lD;VtvU;ET!cKE1pqe5i#B5jX=Q`a4M4?2I|_CO}j-vP`_v?o`91>Z#>y zjSn~pVGI`034&HW#v0E^-k+lC0E|YUa+A4M4|uL8mB%GKsh4N$6Xf=q)1*$X>dpfX zaltvEsmBoX1Bqv0bZJ_~3k*nA1|z!6SeNobwNTQklZuuZPhMvjpEyeZTlSa8PJ13A z3(`FbG%AEDy^N33R!RS8VwT?EcL=g(){FbvxwN(?kwF8UReLnhL_3{ezs36M6(j-D zbhrRwD3ZFjYxFZTaBOlLw7I4k3^^qNgpOjfkT7hkdZV{8|GWIdw|H|CivwB zU7KovX!`aS%xB#-00~{+STmPpd-651(JP7he&JT7cifB2dN)NeAAi!-Ah3g8gRjtM z9tOzs@L(fTJNgb5sXP`5UedQcX5wJLSg>ny@R-1X6C&S zbmm>&$pgexCC3i^OHNID(fuQ*5k5S9@@hpx1w-Y)JCweCi(pwazDKu=uzZWEH2?a? z)sIIE(EXrQC34-DqLEBHxiQ}!IFygfbqZs|r2n!iJF3&8QKr_;<@*k-#f57VyX#vc zyOAfi({hAVs#*z1;A!H2pIo?Faiapa-8c-?L%`msXa;hlGG@h1`9C~)%QJEkrD|{b ztN+yLa|$i(51$*Ctl95A%(GG-82WO{laOkY-Qj8h5@Lq!z8PlEk|C{{MbjMQ%Afqh z`1%tZDJAT$WYz9_ny%LTgHHCb)=S#N+{mpo=tf{<>>E6^Z4dvo|ifg zYAby&`Z^IcYX{BGIakbB`{Q@lOY#Nwcd}Y@c~PalW#AtANf4@3bFLNAG#gSosKdKR z2v1iB!8nB?s6zQfZTB*;WFh`NTMi-z9!Ng9gjir2YM|5q&{AS^VsFOU-lWknU829m zD|P-VZWkZ-87bI1#}y+6Gy@DfO^{^fN1%3{D*Eeogeq3~+h(PJ*>}Auw-@tALCwe= zE@U=OD3^jEhmB6&idsK=b|0_}0JI6Duz9Na>34CclYo!kZF+j~9B+V#dpN0B#yHUz za!MpAu5PY1ct64NoXK*;5|4C3jKysxX+XLEZ(Vm2m1<`^<9__1JU6hnkJMOd6#Lz9 z_{m7!yW6w#wp#kgQp4m}r>_!7d|#Zs66Z)u65_5y&Xdjgh(>9)wa7;wCoNCSg^CK& zo1PcvKZQ}V33pZYf42Lq;x;IRvOq|0xCGeZE;~?LAIL#cct8v z2@3!sAhECG?T6ifRg&8R15SKC$N4NqsmG8ROYb_}DLhkQ0p+M}Kp?dNsVh{m9C^ZOrg)C$5b{!wkG{M7$SE zvMOtTU~_*QYEicP!uAEQ1$;J?ehb`~2&4K}!qFpN_pARRz2zM@zyE6gaHG@4Ui<@Y(Ka8+WgnxHp3O^49E_D$Y{A|K4*fcauw>lBPiu@SkpMZmpW?O zCeu|-Iy_)Kz1bc^%D;CsVL9LfD0`uzo1dMX|3Y*;R?^;2EU&}q?seWkX3Q^K;A|vw z8%2TUuGRRKJP9p)?ikhN`@&^a1owDfv|}i_(=8&@x<($7QRe#bkS#oxvCEOfI@4oRPQ%o$1#;YM5o}1r`3}eTOM{*qiiQ;B32ot^j8Jn zJr~wLuk*VtK}c7Nu5?>VU+l~xf)W*!(TBShCgbm{FSpo1*^OTvoXS&@-<}DI4q8Z2 zDj^K-3_-`yQfDu8US#)?)vPaKI7{1(>=rpZU{nmJ%VWe``cyCY41WKq7jjAA@?p6S z!`aNOUAQx_N$fi-2VXI+)J5J}{yM2Sacbf`{qm!zZ_>1T{2h$SzB%RBPC?F0QoKxY z{(moX`b2ggNjbMpO7%P{!_!eS{;;C34|$`DLZf^lhuQ<$0o%eKfU&N9U5b6V|0@;N zYXgt=QYTvNFtHYLcPr{xv=r9lSdi=H;Ejji#h}&gg^g@3{xQw7yrsN^^u*2%2v?# zU#A5Y8qHWfy~s%)@q5gFsc2Y`PvsdD0JTq&0_z^~Vw%#IiLH-&)Y7m62l`m>%bksw zG}ip0Y=2jAQ_N`qvU_a?t2c0YbhNHf)I$05QM1T`I(}pG*=ZExjr@U$Qm!jei)HFW z{B;Y0`f4SW7_+(s%E|P0)gXzH@vJa991+JVZ@jz8UbLrW-!pksM#w`4RUtk#OSmXD z4$I;4%o)tETQoM}BS{W(PRUejk%J>!Ca7^oV&skKcR zRX|J43!%BWSN5@SL$Wc`-VC#X<0g-v0@T$P7azzwJs#18x}3maDJN}FG83kB90gHX z8W=L*cSDL|Y`M;6nbVJmo)ykRrVrQ>iW=NwC4$y0_MYsjxaU{)=F2z)PUxU@MA{SU zuTWV5+^E*g^Xkq!N3C~GF|)mkzQcFEdzrGL>S~!(q)}4CM}59B&BpaBTh5Fq@`VfH7`t?*WA>>kp<=? zXX|f3HO38EDvRQ6CI7clMNK zh=_7N3FBRx=#RJGIDHIS)M*k`XX*5`WU83(4Uvq2Lo0ajueE))dFv>w1+KSM5!~)h z(dAWY2&%vpAt?Zn8lfnMWJYM^ky#DTUtqwTcW1yPReGRs6?XIw-Z^J>Dt?zJ=la#V6k&reSYy zc8;>b+Q{0_j);3pMDFEAAEpr1jy}SV`JLsdTp6_q!)?BZf7-MZE*^} z)vuV>G(+QMz(+69Vbb8gx$Q!}^q3WGKh|7Kgp9zIySH{U{T)19F5s;z*Gj{RQ_LNJ zj{c(F$)sEt>1+m|a`dyVb6m&zVe~$onrz}YLribAu1X8q80!0O`t;dT*U}7V-mws< z^ZT(alfKX+=D!WcUk+z=ow7?ZnP6d$tlv#f#)|Ay88O!QFM~nU;W|pDS5kxBq%<|n zga#KXxBBj!b8tKQ$}uj((JsBMv*Dr)$|?8vsrod)W=g(XdEZ9XUdwn@qz+t9E`3vx>X5r@{e+yn_hjLLW!qqEjP-07;X3sAVc;pHqe|X zd_@)^arq|_n@19;U@%at3YSN5?Fs~3f`d%8e(el#+4^LmNmW;TwBNT(HE0S`3;*zl z8`yzGk|J}P1`h20`e#-#_ zf%Vl6lZTnzM-ui&FS^x6>4-;ypws`OR&1$M|%)V*QgMd#TrYYvB)vFE3^QE-cV8ME$3Z|{5 z)2R7G@8Rwh32VZJpLZ7_>RFgh7_(x5LT49*Gs5uA}r$Xs<_ayoV))M80va zvFM-D443CT2RQokP}~pH`0%5ipzu?_4lqS@dbB-k6|k;W-6_2ujK6kyDT9S-69=MS zCgZ6um@owl&dpe;@m$>_;L(&K37Ch-kdofNGKO@;AX_Deto*7iT?fU43$ypzNM= z9OHc^j#x{U7preAI*eDLSQv)%?VJWdfi249&H`g-e2Cog?CChH{5xP3E1Xx%CL@8 zeQKoo_PP=WhCIm~slN64h124uEkRE0%eh+DYu!Z*W!tZ$i;z2T&+%^|F#@at`1LRe zWQbvoRNwSDJAfu|(n;Mg2RbR&c{+Eb>~3KD1|p^}|7U~Q4q~vG9MPXWr5}5VbhroYVB;A{1Z<0?1=Rr$*XbRAfV^_bohBuOz-j18naJ$XNs2$2`A%?kgJX^u>j+H{JMSw7a)0xP)5 zPi+xvyjwE|%8lX_EUG_wPhbVvVT}jUa(^~X&)$Nt3;&F-nBN-u#-2*4h(UY}V=3np)kPaF3v3wMDI`HbNW7HI z*PGrMpom7Cx@=yyVosX^x?p66H_*yDXoF# zju?!!2S?SCwZKQxN44AcWdWdzCF2C=>gR-!&+yhD0<967z4=agZIipw=G<=H1gQ-b zQhv(}9?R%V3Z`qjJJlgS`jlrFHO(ECH@Rw;-qZiiu|@N*R271 z2^YiGBes49m?-!~NA`3Xcod^EJ*zi=3A=0A6;#)gNXlnLS1P{3 zVsLR}|BgcgyEs&3R+_g80Jx;-VL+gnQ|&5#mNCTs&r@ftryCdtT19CjdnHg4enQ@s zPuRYp49dFRzHXH3dW2Yw2Vo90(ZN;9?a^>oDNgam3BU&I-^M~188W0L@U}PHqmN#V zF5X==sq#^gCEaNC<7$T`a=44sQ@RUu`5y1>0mb^6#4|<7z9Igg3_I4xkeHI~L?heT zUo4(ZvywP^J?1Fe3;1=7Z<#dn&`|jIv0G5J^UW8I{20{sb>zah;6owNGW$@pe@V)` z*yH5b@vig2#4g{2D8mbQ5IogcjOhlH&cQu`Nv?%BzyeDK@`b$F&4lGZyf06B)zJ`xaCy8&_R*Dl4%9s%bQnm)svSzj;u ztHkx6!QKnL8-lUpP?GtQYuJ{$=kDnNDSKvYTU;~#E90_|LkZ#Yw>)o3SUXv~q%r0@ z*5YxDt!H%ed2wBCg-tsLNRo%&z%y$h(N9*BCcgpfV=)>;j$}z`7^;E%st7&Cw5X zls761y+VDTw@Rff59bYq^(A=(88&a^95Y?!j(frF7cGsJSVtR{P!=xSG1^MafnL4t z($D(Jo3%m8t4DUs#HL)3XDM(j3(eT2RW&BI%=+=E3f0v9(~204!FIPGs-7$sq=44y zBQJJ%=R{#-q)H0D%w10ZnR{~Dlb?bWw2f!)o9shlG~l%|T95l>Y$Z@&AWMQv^1*Kg zA<46kdu+>#nkF6(ky`0roiWAa)v8B#G*xZv)(yapF}_PsA3P4>X^neL}=kU?ETtK_M~Q5L_L zKx+k1R;4{jsYBjr)RX&jJMAu@QR$i$$Ei|jm)geg->2tqFP9KO$hrZ1~;TX zsDSq4@idapYYK7M!J#cIm<`|8OBWx}=J0V$e&!JbPP{#$**(ca6(`a!Bh{b#FpZTm zq8JRLD@=0V@FBch|AHOXAUEz4Mb_7)C+{Jm7e0FHw%LEvwQ@vL9;nGA+5#2k{IC~$D-z3_Y z=1n}dFFNItZ1HUV>|||gB4#G)B&5Nc!lDdo%o0bam*@H`8|bSP8p>?_B>*2T`z0|A0g1JSffu*ONHIl z^kzM{OsNBT%UAY?S6!!d-B}wsEwB4;*W`0kTv`rS1xlpo zWvPjB}L*!YE*cWbO6e)<>)H3*=KBWpU zI?%dZL$?>3>krLM8F6e}rZXt2vmi`_W`|w9tBCE*EU@CbaX%~$IKJ6Hn^;}7&Mysp zAS1GDft&k*iN)tmP*0BBwRxQH#zX1M7VjM2M)&Z8MQ>7l`;4D2%LDjB-REr$ zAb&CC0gwyxp94ELF~!*wXIS@S`Iy=(`rTxx75&#Y%%gF{LRmb+RLD9 zY;A@2T^2cGiS*9o&%I9-kOZJAW`%j@bX+e{iprmB;+4!B+A3eZ_=qzO<0$|0QG)_Qo$hgMUNe_xEbS`lOY5DA~&qGznHgs=20_a&} zV`(ogCfWs_8W3=}OeuFd8s(5_LGw0Y!Ol0K+TgYHN^9pKNC$ImIs~`4%t@nrKs(yV z5h^@H7QWU3)9Tqod0Tng1{kp(bma0VIXG?0`wFsW(YJ_lCy!1G z|CoF8aH!k&f7lW#q|KI4BxOm+o~^R4ErYSg*oGm5EHk7#m93DSxGOu!FoP_ky0Z-u zW1GP!AwqfpYP|v#8cM0Nbd?u$s zTnC!X6g|51s%@=A(Zp<6)KNr!o}2w*(Gy6k#tGHWG|& z7o9u|u#{n2lu5`;?E`dlNt=nRJ=U)_1(e_tZV8H<*;J)DH-`{S@=kV)vKL}T!SDX6Kt(zr< z9vHQDvDm~9Tc9`)tnQR0Sf*3;bOsVjdd7~)&Oyou6ZA!#4rIx7<;-2;m*3Syw7rie z{}GUbDC9fM$?s|MuKIb>Bt~3LPqzZ#PLEaR56***D_efrtL`{4&P&W`dm_Y{}oov$!&XX*X* zqVq5(oN7XItTPgd`u>a3|BhcO#la+$74>E_%gDq;u|P!T?U&cOviaE?6y_RLx@MSAhv40E2JYu|jj!ZxXHkS~E)PA66p z8ZqzFkP@`i?W);}_fKvH&JgzyY3;w1dzw|}p_bW`gWoVp@FrZI41r44z}HP=k~!{R zUZhf1SrjH}#w=w$4?&ZG(%7 zZRxg+FO`1#Dh56Yl*vKLw|y$mjqrPZQ9=k9c_66sRM1kR_;*s+j&)m;%0wC$q%!mC zs7dGHdszx+Oa_}%ebEMH*>diN#(CFtVpnp4I$Fl+)tv%VrZh-#5k0u-^eNt`ABOJl zfGEXN@to~kpC~EJIoUsaA}d^lI3qa~USLl^aptt1ZFiw4+fK2%`J#xM z?8OeV%$Pj>XxskraKZEQs+IS)Rc4})9bCw5rZKLZbu8yZPF)ia`1P$a*-K&4HD>jH zI3(VD?R>ZOB@{}i<#27N z*JKwuDM+03K3_TD{;J#O9^QgzB3oukSAbeimCXya5r=p8povPjN6AN%T<2xFcLhjO z3kI<=#(B2)OmD9$OBV+{4lK&Y9wo5sGCktcPdlF|?96MZ2GVN!6Zu(KPEwaZ>LqW> zb-AB4B<32H%d&(i#WYE80;f7vMX)?@x=ViM$&_c?sXgxUpW!-%4qu~fn_UGvr5@hYH zv5|_F9^PKpIb~7Y;U7%(GJx)OE&ImO7J&K7inhaFRy!zNLKTV$J>O^UJB=< zE=8$@(RNg#Lhw>EwQJI-tHbA~M7zQglbG#K6-$(5OxcLE*unC0gD=+W@Lg%^TC7H7 z;}+U(c?dDx)1t9mF3%ri$ScMCV4ZWEf&9*2>+Ow`tN#d8QwubG=Q@&D*`!5=b)LFl zWOTrfB1Gu< z*!HIw(%!jSn@ZaF5etv(`?PkgI-I0OI!yMBd1b@WtI4}Ml!G1)9>1LPZil5Ffmd7K zNMv&eH?ig^{^Gfrz-1)ZvgA_VUUgxZe?|pH&yp^A69AiuG1L&e%}cNXsdga8>pf^- zcAV>h0i{25*452ux=X+(My5UETy?*GXC0yWS|)40j}amz^YFymjrm(1$oPd}(S8G= zVM8zGwA|*&hUkr=BJM!ag#&SXR*56Kk9RVZJH1@ty5S2vQ#@j{TL+a!Dn+p&DS3Tu z*#SqGCXSIl@Yl>DvK82qraS}Fx{O4pP=2IXCc757prAPVtD0|xjxtlCub*=`^9uAV zxl+|;#T_#2>P$frdM>OlkvSYUP%cNSoyCY8lshy*J_**NdDw zLdq$F-h6k==Ps@EZyvj6=q}m&d&d7*ROfb!5&Fi%FL)1CasqGWukz0`Y!qgbv5msD zSXkrkkA*e`V&E=|uB1j@wdgqVPj2+x&<=jNA{Z-T2`xEnrE-kf$l zfU8`Fh1bF6!=jmo6A9L->_ICbH&E1QO%+a9AJ4h+&&p$zQ{fVyBbq5BLlU7@Y2B|0 z@7#lJCypr-puR8r3tfeBDH#-7SDK$19)ww^I2M*h78lyEXM2`^E$qDF(Zq)nIbgSG zX~=Q;L9c8i<_yuhC+uZ_p+D+t;Q(!-j>`jk_a-B9}4IDp!J= z0~4#MV`CTGZq_*tfP*Ly27o}oVbnAI^BxO)56k!*^=l!X5uzY8f%q**=6y%*h&a+x zLpHF3g`+4{+K?_vlk4yv_VH%_`BA(qi!TR8M$wY_XT(0)9%ZN zwOwREx>Cm=t>A>iSEtve+r7fpSzs(p2}t*}He);T63x>U+SO3*Fpiq3bp)@C^Htdn zCq7@_+=8yHNMt&ZzPU$MhCsVeZDcgD7+#pK3>~}%yMb5jg1%bkx*Q*!?5Nm#{N7xD zGj+V7xjMe2dd^FmD3kw_CPq=)Ltv+BTj1-bCyAuAo>)nZd zlqxwrRA!Vc)Fd~p;i0`HG`~?A3)FRD)AwOJd1CgvCO+mD-z?Ejnc3xu9>RV?P&LdxTglWme=LOUv? z>1XD+rAJ3C&X*o%q_<{04qwi2(m+kuUa}%8j4!>G4I61+k*K_VUv1Cr@=`#7tWZ;q z63(rOkA@zf`azm$SO0=reRtctVLy39Rin!F8_{~7=)k{Kn^(pAP8aK-3 zx9QsQAs2_i2Kz>QSo$WdTq>LKJ+{cIELpjuabM8Q)W(y&vr2v%gF9)}?rF@9t!R5L zJfF-=mBRBU4$6b{_ibWnZg0xlL*#ubOEaR0KY=q{bqb$;;2d4%0}{91gnyv@T)Dyb zemDy6GVbQ;0KQAT8aC3GRy*w=S?osbX($h&+K`GB3iGkHaSDd>xvH{wiZ8cuZ`0l`Z3SeJ8G#Bo-B*QfXip0K_B}G~Z1;%*`r;`f+p!k2AFv2g z7G*W`5j(eO9#3f=YyPuC!{(3!zfu|XEC`=`7}sl{@Bp~}eGi9l5?)2DbSS{uk8YlT zWP%upqtjJMg~pq2&5^HWxRC>vmkTGKYmYY?ypht;-G+enAydCU^Q42U9(>S{gQS5d zvWB{J$>GDv(0c#S138+L@rtH)PhA7P>G5|w5k=YbJ{OJnt*ZuldQO3+kSbF@d$edi zRM6ftK~D6K3Feu+$YwQ_(!UDwt;}S*3K`e?``5qruWzl^K@EmjDgBBp=@qK0ScTuQ z+16y$ z+Gf2iD&p@Tx(Pb|8QCn>mVg?(Bk8v65?+FbFQScmx<6IxgG zH+dz?^d)KzJzvxDFH?;u>%qxdI+((A?TTLnmC^AqU~?=4|LHMjxBrAhN5m2xahikp zGtf~?SQgu@wXBKzC=%WZN?=Q#dSG6!>IkPG(%mTP<6j{XATuP(Hp7f@{bkCyKHa}J zteA_kZ5d9n(DK?a;(cS)ePWjS*03!5266O!->!L*`DSdq&)MW#bOoPhCeJlDY-Wj= zGUiC8>(k|+xqx2c)fGdv&C$RD(07f!U95Q~?89tf_Tgr~{=$T3A;;t{oEJ~+JHU5N zp=~UfgaC!| z9@5YAZEm1!YQ!T?iINAZH>`Z*5Ea-vU6re!a4HiiX17t(Z=T})u3rCA)7q^O@5-j1 zEv!5*8ST9IWNa*7+-!KT>{35hTJ{t4iF|YVUJG-;M>c)|XZtcM*?w&I^pwi2#E04E z3{Lz1adrFGn`yUBpA^?m&CRPl8q>9bTvW5h_AK&2apxvRyT^d`vrjT6uW3(foY_^|H|1uPsryJ1%2ycN2%t4H>`a6iLS{k1;nf zIStN(813nZ-yaJXSe8ba*sGmDAIO|%BOOsqGq1wC{7Rf~&CJ-^@vyQS(46o*Yx;uS z#w2L^bD8$V4+ol;n^D_c{mxJq zPdSyw0@JhP@yPzQXa739ebB|=_5%EEByw9Lt z+u>_Hh<-sv(GaXz)FlR$-A{iHcYLKUY(Jsan~h`{59sP>mbh*WLH}1I0Kg(|0QQPw zBF&}T_13NEE}p_Mr^2F|0gYQlr%thRv2Z_gqrPgRQ(t+H(dbW)i5nll5Q3)2FNIXU z|8e3cLtabpw;z?Lez|f0*0L=xH*KI`*F;G>=RD0Qq<_X8bT#10pp4d6kBA_y0@2B? zuHxC-!enW8g!!8_6$vYS4GMeZp=ai`@_JR=y|VngM{XZcdl z|58tI5Ll9;tlYjFmwMjGf*E|R=wpC6IbyZ0;3HcMf+ojCpP`wbh=di(>zosOZBO0^ zpQ{bS&3v)8_ro@MTAG+aIN_m$$UXX|3dV}LXjpr9ndkO3rl;`v;V`{HX*|d!BD9!m z-wbBz?cn^#gz*aWNj2nt`T z_g==>RJ$iY$_#*WcfNJ|ev(8?1?r zeY}$obD8id~O-rqRJQMa)$3 zd&T^2$SJZZG$#@q21=lK%6}fCgDTG%k&}{k%#O@OFSRCGnRSv+RWKP%Ysb4~Z2nFi zdG5)4H?$z>%*GXs=S3)wn^){t8GcbR!mxG#BvY< z?2Fw@+%KYluW2>3IUn(%)MI&8(${G+Y;aBr^z2+oG0o)9ebxW9ZyR; zmg{r<`QNW>ra+{C>m7M`f?`@5E4+fY>@LapoLfTRajB3PS^tY?l22j&$|1Nn{fL$3 zlDW~+;nz%UB~-8pto+h+%eGXi zoH(hS!5_Q4qTpPR*Sxc0qW0sdIM)rf(LVBJv}fqWk+V-uyAY>w9X&Q-IrN>bRiZRa0rwGJT7?{W^*$KIxN`19H@}#;)9e-NG@rr(UggYru z!7WzJokwdceh$jt{F34OT5ls{w`JBTuA&WZ^ zWlGvv9C8frhK->e25+Xyuwka&|5=jzlSJr|&m?cd;i-;CV z?ysYslf9Xx_fXTB9$lL-vO*E_`Mj{XlIR6r<1@bli?QK z4fZbiF20#1XStwaI30A=#0&W~-fdC9Gx~l_OE^V5VfMSo{b@Jsi#=D11St&(k^@Gf zp=V0TktqdnQQulTbfie^D=c2e-lung5L{mSYl-n0xy-Hw#Y}9YbNtf7Q9{3)rg6q^ zGNGv84olGek+jnL;57UR{e4Mp{O~ZA%X?C0XfOn+gfRw?w*SP+W_iG*?eD#zlXPVB zX-%W<+jZIR@)Pg|Qrx4Vl`N+ighc6GJjST}l(63|6jR3G$dx!f{I&=#ZRugWeI&t4 zLXi+Ii$>^(<5QIlwmW=9-K~fpT5fM7xs7mC4jZ6vzM*j(O{cs2V%yppe2w-Hx=JpZ zgB}fzQSkzjuOn6F`;U;%Zp+T!o@;4{T#&>FM`@Z^wJ2J?Ee@GsngThb)A8+NX@|RI7 zoHGBDcfudq!k;qwzR}Me0q=aL=P{hS0|H4e%?fAV}Ko6V5^PHQ^Ur zHqjQuJaC@fSUn?1_14}CmZ}DYbZ&Fz?q91e7IU{N2;*bF*$h^4D`8@5O4;BRYEvf_ z3;&V@fCJYSqLqr??${D{l2Y9034oA^2B{8=57ha1!ovldtFq(s{0MQV|IHxeHc1%xMY2q*?u6iGZG}o7`)T_dw$)YzhXHJH z@-iFdJDSB(16gC5bGhVB_7&gQ_&haW6AO((JS<80s#i_4zhF%#ua-fsfyi5ag6FYp zdK9(1ve4=qh%nr>TeW@VL%)~&R%t;-LyH3aptNv4dz?N3l^m*-1rtF%nI*HDmDzHi!{-;umy$K2CxuJ%B2{?z1ke=_#u1sQHKwhz@5ih za_aLn%YG&mu{PBL-F{=nFwn#Sd$2fYJMc+a?yf`aNmk#{GZ)tlq>3v#H*90d!<7`z zyF$szQjZ@WAoPe_=vx*^vQp7^MtF902XlA6R`hJc@hN~5KrL)EM-!!dog$X?Os8vo-IUlc%mVpX@_di|Qasxh zYk{B~=@&hR@k|5C^Jd{xLnM8$@(N=CN__K2Dy3A8r;lld4Mr|bAF&KJz{;pn*C~;+W}?JI_%2)76vlHps%|Q zMX1W{JyRg=fuk-W72}?)`8uIoaLg|4`K+XLYlOpVS)qO>Y{J5i^8PEO3H!8G23%Yf zR0%9Mt1=kzx1fU8C@k};yYVw+{~za|gRrLZ$cv=fiZMSByBI9Kc|C?p@CWMnlLsY` z;18}|i{ux>E3;ynukJ-8W+Pi)3v|gnN;_2p&C)HqMFHFXZ#F$i0M&N(KlDm7yi4knKUVB>k*Q|y7-hipK?Le|1{(%N*Cf`B*KhEGajpXffQ&Lg zj9a3x(Z4|H;xyPBNwXA)Ycl|J!~hKp;Z@AXb{l6Zz)K%ip|qkf%l)k#mE9otcs3$# zdwm|iLtP!SxZ?&NXx!4NaPKX>lM1S_?28KpMj4+sVBbC1-#E9x-BTGBc;xB5ruaP7 zkr1x;%IjBPn%TMCIS!i98b@+Y|PDv$MjnuiUcIA;(LFo!e z40!?sW;$c+LI}Vqz`)=oq`?ovuLIF!0#Z7G;u||jSZqFpX$YnS>=ncsm7OM+eT@_Q ztaW0foUxA~Ad_jjo(#Hi$EwwE<9!>0b8y*Z+kg3S#GNIU)ERbs6DFnDS zZ0R734Ft0Wztn1CIuY0V)jNNijZLy*G}NhBh?i_xI+w$?xdam?s30V-vS5?nNqw-W z&$d>J4ud@(^p7I)6mT#Ri)6uXHN)4_{F()Sk;rdkS>Og4K|91!pTYUEvwgK=w^P{( z@Al@7C4Hml>DQF2D}Sgk+*6rk!pN!4*Q^3TpKKRsBs3u5ZrKoK>cqUbT?;wnW9pGf z{@!QI0{6zps!Zc!hV%xTUOBk=Brs#lBNGJ)iSj_k6_| zv>8D`;pLH(wp!aeVU}@z%OGOb0iuLp0|G^9UmB5*4L1|Sf;KcJvu8eIYbx$+AdJHS z=&0cBT2OSSjV`5CS>Z}Su3tDjl@wE0llFaH1HU!n$?d^-*E*uyKa*9Y(*ftgnvcluJ?~2)8C6X3i175M| zE{wxUWv|cmJ8ff;jeP*?h!lCbbFAXr9_>7&P5(-|gAe1 zeMG-GqE0WZJrU5MbbdqT1Zl1Nl_?AtV^?m|;-n81H{D5VlJD1ft5ga8jGY@eZRAG) zEsPs5yZav`NYz##B_Jr@>Z|USG?Quwda>XR85@j4glyZUb%mDs7jR(yI-}%0ctz{+ zKI8_h+Y!Y3JZ8+JY`R$tSF>>pSCdstM7UW2TK9To(h`T0w4DZdZZcjEO45+1c6AU` z|9MJqp?qfOmF*PvaQm4c9p!G3-m1V&tdLV;tyD76>q*iAzw#i`{ z^8wl%3%_0gA09HxX&$A7W?p{SQogpU9Xcl?sP?vc4H1zF)~LsJ^gKBtIHvxl!s`xg z7$q9zTUl;sNz!XZdGQEtd>>bMqi05~UxlEHT`5wos~Q7o7)oW}blBdm6Jf3+Z9_$U z-hPpkW-C#9B)#22b20s*_;%+X9sLn)Ox`C7yON;fhzoJVffc{9RbgNk@W^ z+u<)UmC`uW+8@xiN?DAtZzb-+Z6fyNvqYY&-dZRA6<-1E2Xt|SXqYR!~D_ud_$4ga~e47tK^Hxj*C6 z2MQ(lJ_r%~WiQ?uK`g78URYvMfNKw%@Fj!}QBrxC;YFF0fE+ViCCdh;y7I)l=)&4k z1D)1{*;t2pknCWn&yzp1rPgSK!-$so@b+H5nfAX9nTyJm?O9|?zS%}zV9m*ItE}jR z({ssNtogy;m};%9&J|OD0i#nC$rZ5S_FnM_cj>iOo6AcnuAL&B?~iPDth6fa8>JB6 z$(Yt&eaX(@KXY^IQH4ta&WOG>s={khlR~c`Cy+&4?%Sv6I3@z0maTn~AZC2`Yxj>* zn3KkG=aN~^6(p(lD}Pl3Y|vm&;cu~$AZgL;ZEeu{TtqCTv+ky%*W7c3ayMDQtA!RY z*JuAs?wd1iTb2SwQ%bqAO6O+Y4kU(JQ(#3!*w9(s9`v7Z+j-h7A4SYm*rkoHs^2ObRj&E?a0OWw z=r;QME1-7FKDBG|V0S9pV81wa5s*`X5tQ$FA~KvYIGK^z>`_Oke||7Eeq#msMm$%( zeReNTeKN^)r$S2k*CZzRSOZ6=^GN@*p-?T#%Tw+zFm8^U!JDazVu|i6)gQHrRzm*rkzAlGN}h4 z?Pk5FGI|PAKqx^3#VyP&*DaWmevuMw2i^;l1~ndbUfEpsh-~8$Qt@r--5lAQcqtB5yfJ74px**Gg+Hn^a7usdRNtfg+&gl$M{Bt;rFXrpWYbG-7iCa;E6H}B`CI&cd%sc0TBe0e zWonh|caf1wmpW8jagCTMtMI!CXpwF)&Ab5XTM)?9LRWgGqQ@5|cw0w~XypB&QW0b2 z;tDiz;`F05l{9X6!;S@P{9cFz1ahYnEByC_g{9;8<@|?AiKQ1T?PS+hZIbv@RFTbG zmZUX!Qb*Hh>K(|KHbe6oUK7LMVeiT#sO6oAdiZfwJtZ(5IV-``w>>H@e zAMf6l#uDqi;!LxZn|v(iw^+8gA;5EjNTrqr0Jp8NGvNFL^U_}j;d%Gno5vJCmPFsQ zvP$d9QBzd5)bL1buv!Pc((#($n+y3H20@q0&{*HGIbe?@OvFbOsspg>XmN!_>cL3#;e5+E-FKyW=lX(UkjikA?h3sQ|7fpM#_%SHo9 zn)}H0e=;dBf8cck1XUxjqNVn&e^X#LgM4dXIj2SwdqMpi9OJ{>kM@b1TKf7Q4~I&n zGA#Cv_V#<)H$lSiJqDNjgU?ts3WR_&R@c@$jBLL*|CDNRbSNlC`%Ld&br^&y!NMCe zh{Koi_dKGBx4_r|(dR*BbKoX`vPS^pQc&s%oL*}%X;3u%?|d^rRuFwC0Yg0ns{4VI zBTyOq#otA?AT|GNIF!Yk=H`i^B^m$1Mo$7-T7m-L(L|TOUkh9_&?ADunRIZpuZs`> zWd(jb_FL|d%xjNk;{>h}!x#q7{*!6&%Ka-Acnz%mCy=kT449*Me-;3N&fqTK9YX$I z*IfqFDRTkOcv+0Sw{eQ3z{!STZ1XKRe z2KW+;U(p7fU#k8OMUqwHt=qS!R`7D^N#ePoj81IV{xSQzH0Tfk6r?_{7|6gnAdmlv zlm+U}7{VxJ^H28W|3thHV?j;e22-w9)c;KAZ}D-Ty7B_Fq-gk$dAJpU2GOhQ=FSjM zq3-Wgt^Z2b#0k80@6Kv10&$Ge%ODay@XjF-1WZk;x(Fcp;F}n!p%+h4FD$+P|ENs= z?>ui?cEv+9J)R$WrFHpE+qO{Rpv7msFl{^aw;!3597F?H>Tin0_AonF$k=S-loD-PmG(?hgPDhn%N{Pe|&v>*2T zC6_B=S;oEn^GBuM*ExI@8FTr*#2(*m&=-jx01}Qlm|a??Q12*iHM>ysZ+;yuf7AXzzvPVt6<#t(SJ zSFOG1#@#M)g}o;A0*@{USS>#5t8Xg1#n`0J>SGt$(dVF5R^SSdn0S7th_e7P1?IL6 z(btcu@gqCJ2TCOvK&zjXk#z5RO7WY-WB}V3)6rw$)-IC1;3}m0Uj zt$mCfQcX@(9P+=x+jB=97$|3p$1*P}>~SRdX>Tl5528HJuB>|}*{-5oqlW7I`)MQX z;Ajghc0W!1ghN1EFl6Un^sB$ANLpqik_AHpVw&eYDum-C^;Q5;^o@ z#duAHj~EtkSmE2Zb>0x!ePjF<Hs@M=`o1+`J)g(?At&1{Bx#q3cU9U7rf7^fsQatcX9b%1_t8;;9uT-UQ$S>4NKgpVn z2d*)gqLep!24k_4y%c<+iuH)94g;#P*?vd+`yC{}xUsj>=({Tdf~9#8E6+@G)s?)U z7(NyTv~C3eC`UrHJ6)e&U3nS~OMYYd(Do%(59WDY()mOE(cv^03P~D@7?gegA?f&G zI{D}bc5v(v1pNi@NvZ@mLSWbVJ@ob9aVt>r!V>-VinwYv$T?s{;<|jLWn~eyY+6+p z*7Et>3S;AaPcSPrBZn|HqT$MX7~dL9-A!0agIKn8oyMP~2jwl~hx9hBE5CV!x;*25 z*vbOW;0fJkyeN@vr@-Etp@6Ijd5bH~2lBT!W|+A1Ch_Td$|pA~?{u7KW?CHsk!m-9 znUgq$bm_@almBvXi!*ShM+pc3w}d|LmpWypbR7DjZ4b}CJ+V8f92nKl7MklXcfRvP z{(cOd!f5Z!K3~~Tk~DG@l8ichcu<}46Z=j&cS+4;*YB(ICO^p|56N{O7EqS>BLqob zA37gCytJ7|?+yb88_j z&zwY$^tBg)Bxv`3t6~8IfN67mcT-`_r20YYfoN5na$~E4j_GtoCvc13wZC85e$j8? zdI9mGxGLdD$gItKp+CMJ&bi=yb%JK&H;tB2iKG*&^zBxsyQO)f7r{AU5C+-Qp3gH1 zY9Aw&4ZVX>+TV(ZX;hCUlt~%Cy!dpA-L$t=K!;pnTI#bO+xYYj&I|{PVMLu2-TTE^ zeY`4iCrC7tW)%g+m}}s*8m%s=ZX6|3B;ZV4&$V}LbP+lF;T@$AZ{4o|2PEXFEW3f* zqkq*@=(zbyN7fiBzS6D1`$8`;942)A)YG@t-C^z!XqQln9@?oB{Y%Rsi(28Apz{Ky zo962VtRU-IJMr}b(8l6@0?JSI2q7S3vNZO%AOcpGAf(b`KaWBVWjW<-D&m5gTCu;C zet9z>;hI_6_HXg@jwa}5ZjxLcKeV=YtXN7!KAqJL@7;y4pIkG-HRs5PKYXB8Wws(<#mX z*@4FDhYIdfN<>CkgykXnHvEOY(?^j96U?jEU^x1-fv3_>pIw+b`G=HHv0J2oD03gf z2LV;+WIrcr+>fMoxDQ}ALuyZd| z#^tS?#@gGz#BV?8tK4Jv+TxE_lWY8a?Gp|8*D(Xbrm%I>Jx*|NSH!19I0>AlZXi(Zs*vZ87$hx^4p#FRE}wxxraW8^^&}`!VKN(7f{O zOirNrC&9zRe{zVP`)ZF$?)R9Ft>GzNcs8Uyn1V3#O;&lQHWETxCVn#zm1O|yGTjCo z4jkQa$L$`il=tX;@~f!hNKf&@q541%wT)p{#sEoyUp1!|8pgDs4?J#`8@oCnbL8jV?$TaFWMt?9(YsP< z=+aPw6OQ{+d7eMefE;1DTJ*L4^C{{#8On9{J<{dazk^uLhlWZi_tW3je6W635*qIZ zPMR1b4kkaC7W+Q_T2WtfYTk_;a#=~l* zEGmekT&5*Q=0F#!HjcFE!O=NKBAI{Pf#}M*_SVn5nh%dSj%nImGoY^g8Q7h`OKv`0spi^osb~p@>^4Hrh(3z! zOq5iq8!xRkEIoLf;KD#z*Q1unZ)m=+^+oU6gLj0pko{T#;EoZwpo1`nzQ(Q}4Zl-c znW$|Um}cP>T6ER_^JBDiiy!?tfc!iY5>!9^`34*^a%Krw7U|lO%Q@1;ysHYSCUQ*# zu&8hYk-Azg)XFDk(a^&jLEcEK|cUd3tJ z&wGyaZMml$nkVH=$`>W%jn^CZsHtZtXnorNfPPX=%3L`t%68ig4If~MEHN{|R+F(O zs^$*EpHYkJU&SA{V33KIK_+SI3R{o*(ZQesJZQ0&w^!N~uMgf2G*Gs^Wy6f}wf&bZyFAff=GL|Oy z^qq|L@40*TA6KOzG=P$~yR#)ia9`7~(j87H5;>bKL_XP-Ak?PP?hCPY^lxPmsn{Sn zieW+GNA~UG`#M;v2-$=55g~I&>5{cI_iD@}jLHHO<&n6Y&moBoIms`^? z&A>mYzL^#pl4DNGn}j4Mnc*jA_#N&i`8DW*Vf=h#3mepCgX&W%tXy(leYFP*+qKnRqRF*=Dxau6<2`V`dVfvz zAjk4ICg>Z4C0n(n;p<4!kxo#xkufg}#>+zWteQmS`#CQ7MZLCvoy}j+krh1N zNVs@V!x@KsyKAOoQVE%lFh>wYcnHb#&9S!H+y@D5zczu+y(Ri5#IoUunm!}9atC;p zM(t6KqmUwVhjOTrYS1XP33LQY%kd6~wt$>#V2y+3;hBndN3qwv1KKRV8+#ktY`_{UW zyOdldH>j+12}$AsXu|Wpx`a};HH9T+?i4|QEtV~fWx>o0UA@V71!?&9wpyp?{U*EV zW@JLn<`VATGZnBXfplOfLtkCxr$5358RWwL(4o8vU%Xi9 z%3B-_EA*D3wxf$7adI!Z_BR3JBzOG|V2d8Ru&@uAe;mh*j$R$fOc6)ls{z5@IFEtf zWXjS@2XR{?U;R=)p(zYVYsii_!I7U{EFSv|s{rb|)j{0fpOSp@HeWfL`|qbGbWM)F z76lP*(xwW7Wp(ohtF-A}7WEB-%gF|a&Iiu27_V%ySfzwoHXqocb<9>LN+5XksDyt_ zp%|Quv%p_9?YsrzAo*44a`llALsU^%-%o7+=}3uCCQ-((kbeT3i=PN8z_D{~WOBrU8)&GpN>#E?#np+Q6V{xgrNZomfub_7+Dp$sFO z(b}i<)nT^c3P_pN?-OR7wvx9Vl&BUX0?Q0H+iqB~i53U=fWz%0@X+fQeqXx4EYy+A zX0@f}n(??!^@^&x?!gOAd{K8S?fmokG*+UT+X*Y0!RM;kE=yd&Qz zc>nWprLT2lgMT;gN#>D5f*11H8QVVP_H$Fm{}>%HTlp(UQvO@<*;VE9h1U(rqYIY1 zK}r5vTzEIseOJS}qrUAm z*o&2%XWs6SNgIhN?sqFX3WIjTeZpsG_8}i9YCx0@qziDtkM*#a+Zuts2VBvD#(W3B zab;DBn69j!XXIM6t3;*QIqznh%X|p1r?G{#?b6ogv>D4N6n|`IKSBp|@`gi^d2X`p zzIXGw)X*|+?EYL5y8fG4)mYu^y&_m`>3lp}2M>`UN(C`+gLr{sAr-b!2B7!j6iDA) zMW`48zFE%S7{0!d>Mv(6)<+T9(^|sxtiK&WD7oR9Hr5c*p5U7~4k+rBf@g1j9U88- z;o6p{6y!@MasjOFK3{_~(;GFPsSZv}r{h@+#U z$Gr!LU-{%5eR6X0fkC1SIamC}e22EHFq?`8haFR*FQAiEzkpi$OOYRcjN|3*t-5>w zA->nDYN8Bq$sfL^!=g^3y&g8A5bQvGgQeA_+~io4rHhof&Gl?2GCR%(*LI;Mjn!83 zIH*|O8W4)@IikID&kMx=?4g*h$jR1(6pf|E{@U(*pya+hvR$B>Bqr6kt7rrwFRJ+F zE~PbxVC*;YV*hqp(05A|!+Ui(Bw&pK8QGP~86Hyb1Xi+kHFVUh6(!}VZj*v)@qW2I+ zFo+sNFC)Vsx)HtHGw=I;fA@awpUKCE=Q+}f;SQrMJ7=*IXGMsyqN1N>Or=W z1G@{!ZFFe{>icxCjo)WKG~OyOQcFM3=Tkn|4WmlD6Mi%&9VRPXvvCju7~yKOhos(A zU|W_UClbX4W%Fc%v+H=`Ot3M$5%P`OKafm3id#o9Am8G+FPXF58+KgoF?})aXYq;8 zpiTdd7k}6#>cPiF;__4VH!msAaVg8>lv+0)ZAtX(|L$B#+N3h3Sb5hj9tq8!=7inIVcWVf8KLYlK;FG|-c0j_CYv@4YgslyBfz^h|AEsOkx$waOg24Os7#m2|WM^TQNo7{!P* z;FRDWg(si9Rto39!@r^SygNjRCe$BZOFY1x!EB_-k5(@(F0N)oeb&oVY-s!mGR$}2L;aSxzVrFI zYiCMfVyS2St?C!TAL3rcQ1>|St)*ZVk1$@%jWL^CcFL&I(0_mLM>j5!FW*ldS(cN+ zv~Pw%2jA!AeRUBl0_~ii|J-XlJO!ei%APAD=jHHffb(@`?L^9hEq(Bzh=xhuuN+NE z>PkD*7;r=o?OTdJ7SJ)4B2fA`8hUZ97>W&6~Jf}X#_R}sNX$PnGMH>UIml%Hzc{SQrS#Sct-uEZx+m_Wm5fC z$lvM`P$nZNI3%nYr0FD>de-?gz4{3Hz_cSIEEiN0H_kE?gaEC~}%aCn1_K zB-2Kv>x$C;oXj^VX&7Z-+yAWu1{iaQQ7WFFaq)hbnsVQzEehO`)PG)(J6!)R@CafB zEX&L#zV{f?A%Qg^cIV7r+V;Gaql)%VwX5Dui;*nqhKA-%IULrv1PFPqj~lCRo;bj8 zuX4`#{fjrGM2rbOz4nH9h|AKRc>eE;pDVKC_oA^B$yF=_@oMA@Ljl=*1U&JX5ZBhO z@0|2wc(m(S4;UTF$e-q|3!JJX0hIOjTYs4&-2?=#*((e95ab2>kZS3j%51`*b+gQ2 z98L)5W8mW9V#(V=qdB3k0u4dLjWE0ZJj5yIdBauvkoeIGzz!Q71IPWeLH|*o#rxL3 z?kD~pBR$J&TSs`0SqVUCGmK8-32buIK8EC^WM0;^M_nXy+&-;?us~KDPO><=6`JwN zIUCQ6h+G5TZNQzf@_fkW_LAqPxsWrupEGalH^Mq9Ij>yMd_h1fHziU_*gec59(fRR-`ggy0WazyIuP4Q-Rp024<#T#m;kLM#m~N8{jSA zJ%h=AIKgK6uaHvtknEo!aiMble}7W8L573Uz8(oZdOB9om8l8Zs-t5oIevBWYmC7f ziT6A8GNLAvwv&M(>AzbD#*(HI8Qb`s(HZ6+kicE~;y*su=XfCOVm8z%G>c$A7Ss7> zA*m}Jmz=AOSGp5)Gdy^%%ef^1hFKl$;JHS#%EL_SfYw)MSMN6c^METkif+;EJ9?ma zs8U?P;d@!Y5*{F=)Payt>GM!5gXVBvC1_|@`Kc6CRyx;uje}<9>!yGH6Z~AERiW9w zMz*XXRiR9zS*!xV)#;f0WcM8atMTd>Uyhn~Zbp0Ks#Ly^JB zi#o5V2aTWpv!cw-i!PlGuPVN3zaI$E?#u!E!C#=jZu#v)5002ZJ%$2ZAfCeIPcl27 zhHcvp8BvJRgR&L~Y19p)-?{wi*Gk+NDF-K@tnxs!P}qHn#pAc`(6<|#N%O+sq=)2i zoQ@83`9DwPMU2GD+f6l5=tjsOzz-Im5#oHYE zBq~D){_j}s-n)R10x8^O&cy@gyZ>I4-*Obc8oQPK^A9QW*A#>A6r!(J6?vTqE0edd zrc?vn(bHPlQ6;B;^EAL~t2%XIG)GZf$vnkv-}H+9;q}$kyE@KFC#e?wTXeMr8#dmW zx!2|ndzn)hxCJh9D(%%-j4j`)Eo+taKAGp#nNH^u5e+GyUAwIx{|NlH0`P%EaukQX z9;IH<{5jz<`Tq%~Qu=*`}y{oZ!I zWnxBpJ*O+E|C~W&pf9+gk~N+2TGbJlvaf>C8SNRM3z66HP6vRkfaALHu^(mTJ|VIw zdKFv_{u%)2tEjw4ZYzN8Kl%x1Bq`p5EH~2ZPkv99%J9T{8_8QK;E*X70eV5GH8PXD znH9*J`8G|#WTk`^u{d2QZu{%)OU~YDn1OYKa^ue??;E7t5AH7p=4uel~GkfGAkQ7vBDKcDLD3q$G)(@HV;W@rv9cDoS`WsgW~+MQGGx6hSGeqbuF%B++(Qp|UYul<~VJ2i@Fj<~I$=R}2)D%qI} z{X0AF+tj!L_@V$p`kXTaaBTpNo%YebefxG7!8H)@a@4^m0`6^RDV;x>e*^Tk_mPa? zz6X)+Er6(2y^pKw?!ypLbMfmsiHi(R`Tk#p=>PD)i|++Br8;#FeQO4tLh3Q3#c*%S z=E&I;W`()v#X>u+q3yJ#?$xgG3rp3-iTl^G6-O)QFCRu*^ewOkE-tCqhWv5%|7hK! z4wR4JIRGUI9N9+*H?p)h7_68h*giCa#>oJ<1DNzP4iUipNEIHJuf7s`eB1P!?=wjY z6?2><|GDz8?DG=Ul0NEIQsZ(}X-{Kc-Lr#Ko_$*qIcB7<+={;NU+b#y>M6Fto`(we zS5FS_8o8vnjYrFjcPdGx+&51J?re%3?b=GCf6rb3$q5 zplBaygMJwfnS4_J&uK;J*DIzMdQ9T&t@X)d>bGPf^-`rELh2}F^^;S-?_J@*+UbYh z4)Z>hvu`c#kSA!&1)Cx_?a&y2`oKXO50F_%8qZktBj32;4i6ff@J%m%^O%E2K-ht&|w?MRQrHVy@yZ3*~ zK~cG=>{Rw-+(XVaR3Pv}BTr7Hx^|Jf?-z?-na~N$&=2+InxUB}Wv6z#-!LQSa zRhnf!N#=E!Kj0u{4HUP5F|Yy2VoqO#gwk!M^|l%^;CTN zn~(-S9L%Bp@356L1Lvj7NgRT7kO1Hnb<50bwtE%8vAy>Bv>aFu) zz;-zi|L26YAmngTJ{IX;;`N(<*{jxTpX_z;-(s^-SEegF$?XlF-fUoZ^db?<^0dxU zIXZ-tHM$(6H_j`n_EC8bR~*NwP7qEls#m6We3Eus*1iWydU6p#K2%W=W0(oa^m6Y+ z@-6E9AeHpqj}*3traq07tSRZR@%HUJX;*tF9nVbQY5!E%DH8Tz)3O9``pFv&&=V!3 z3qDNmWDaQm@l~$Ie`DWT)5+u)s;(%$U=VyqYl_RWF9&kXC{q*GG<(w4ymz)}4e2o@p-x}6}Ud~*@!KyrK z8+Xzq*}CiGXf13m`{Xb`y(I063F!TeAmd=`UxW9&zW^8fhe?}t zs$)ysZF}!jo2EbfWRcF)!bA*aB%k%x$}eXu`1Y=~@%?;7%Bm7*9{n$*Mxn;6lrAOj zH@``g$T-ot$K<4VlvqED!gYlk9p;PH)gNr})!qoNM&Buqsa@ z1OhJzd!Q8llz)}lx#KzYAcfzEc$c|7DvCLuG+?0d8qn8_$phy@n*shInjNy8zNeR; z9r_i3abwBC)?`z=4sG!)5u-fa1mJdblMJ($^NthTu;5>L%ingUyw{s(GyMu1_WwvI zZ?+46(2C=?mpcdFUS(LKXav+HrwD^HR0UaEOFQ0n(MUcm*Ra|`0&KGrN4=`lbU|9< z!4m@)Bw{^1ds%wNug{O?EGSF#&J^#;+WhwZ;yn;T9)VmOQi>CWn%qMotr#RH7 zI#}&{Xrn=5_VAIT z<;BY(dyad4$G~5-&iF&9|d<$RIef4hRZz%=_S?%@^qzgZE;MmSgsx_$S3 zGfUTaYHk~=hEz9|p8+w?G+YUXmAAN}jpkkNJ5Wyn$O7wo@O ziM~iRn|!^9J_F`zA3@WhxZN9JOrc9*%gcvY0ZtNg30nNJNyp&28 zJS*?ZHxQt-1RXOc<-_&nnC4e$)?SR+VaI`#PMne^*0+W6BVdJxQD(OVS?NxuPmZjb zLf6eL!;XASYrXAqa{Mw0=M$tN<&E>Zqr~bIO~=ZRk*cHfPe{ATQl*A$TTwvZ^WWKs zrXB204K-7HxO#BNMrqXJzRUS>d_#9pBmZ+Cl^p-Z=IOB~Nc@pIU+2+WnZ1_wQt2A? z{zCab7gALL2smc-Iet@roHk9?T+SiGk=4HNYftoCVE*>1NT}VtDgG@~_~i=fT_^eN zNUX=i`F}FKjc_v}+mvb3$o-U!yEa4}HtAiDN6bUjo1x5oueKZa&~BrOyZ)h}HW5_Q zLDdfu1BV&d1~>crmc}t64xwe)i{&vl>k~cQMk|)@k!+bn6MOa_Ysk|CtJiwv*bR{% z%9o}xnR5^g5~H)m&$$1go!m&FETgcekG~KiX(;@@aji+98;`*5L zd@6pIQBSdsfFmRK*EJn)!ZNR@pzsTBf{^$jzD^;Fl0v2Y-Bq#qPPIqMKCvhfK$v6V zb2L|;4=Q+vEG6l#KQ^dl=wi@F6rG|t*-OqN7VT5Vep6V7l4usMx2*kp;Q_Z2sw<^w zE7uNVju`_r+TPhUR$p{E>hCJz6e*Z1>@xUUJ7xRL=42@}UDrxh>}}?SC8Gx&ZPRKF z)>3hLR+E3G`yK3O7|yVt9R{Qz5G>5^ZA6$gHW()gd1M2B5{qm<)8geFzpTWexYp$- znh|4_kJy#I`8!%VgOf@Fw5f2Zo7~wpEp)1r&({o`w4Ac^%G$K$YkRtvF1`u+5zC(4~*E?4sSX1Gh&%y7ZHFo~u4OWWb%Zyj&A-WU(_2JZ-D`YVn~rKns9^eH^&X2%;eziF_pVNiI8f1aaVZjq2Ns?+!_SoMR-0}E7WXa z-?|2Jzt4{s`kHJzf+FRMj3RKB-=4r0T1~EnRU5{9;pR0MlX9~HR2ADXYXL*U97TEY zhgbA>M{Ii`_dJ$7zoISR*ds;R!l|zoQ+$Ud>t)~NHAWxsFW)gwaiyaf`!CjA@Bn=g=Uqf&?k9}Fm3i0`mGb-o7yRIZ(aWs z&VFA($zc5g#A$aOt)Z%>Wkrhva&zOtt~yyRt(O96`7onGRDDJC+Mh|33`oSz{@hH)xiMG>X@g{rWV}Pawb65l!hkOrgDdC~c zy6@tL)C}+xTvJ|8d7(6O?Rm~YftB@a%B|<_ zv>q$5MjY7)uu4lZQVd?Q)G%0GeG<+NUy*oF2{(RX;4ycYhv%KKQtV275oBrOJdmJG znyQWyx7eog0&Z;NO8mxImVzu5|DwRJ&yID%NK27i(*z9}KSu7$?OPiAqNeIw$Rh21 zQ3ayu_792T%bjvX@}Y9Zp29CmA-7s$ILF$gODOvLS3&N5*H)k&r^o`wqasQnC+Fx4 z8PWnPhJT6WuJ+q&-O7f!QFK2!@+F+3bZ#HA;~D&}D`GSZKp~L8D5`_$(sD!(_agj` zte=dBOwUH@bW3+~IdG{I@q;*bU|S95>EVZ=4V2Dat~I7UzwSSPWiwjgfkG!$qNwJ3 zJ?VRF9GVF-%5I=qN_QQ6^q}s&Otqk@sMpJ(Cgp1b*1UMGTu^u$461G1;8Q*#e0!F9 z0=?r;q-L4j@A-57LG^yz26d667h2a`7I!2D)LT*&fc*tZmW#_^liBrC=G?(S0&ZKk zT3KJzzXO~pXBTqg6mwD#4C~*mFj5hs6c=;o^f^t-sj4cp$5rq80*B(XFiJnK-;Sv{ z>jEdcFHB=sPCI%jW$W@#TtRSVnmkqu`)R+ zD;qSUQj1Z_Y*8q)9s%YT-TLPYiPF9=p|8lUq4S9depIi*UqF0;wWVqO+I>Tq{$eUm zb_iz*Zj(JH5xtbuAydBu+~(=<%nX~#WQw^I#^Ga#`WxemQ=X!_!9$f7MD(%dsn zOPQv#A*oe#s{z`A>D!WV5LF8vgjpG>c9H#?H1?7^-5sUOm3Ls#eb1@r@3s$qR4<;< z%qv>1>aw=l)U-PAwpUR;DUY1%irYz0xsojLp-Vg+`*d0u9t#ERL*Y@ z0}xC|Xr+vnbZnuj&VN-aFjlfq+1=d|CKAX#@u5tR580bRT%l^Ub(Buf+Ffe8Mi#3#*O`Mg9kqnMuSx~sCNiIE`W>4xmG?;b%JzTnLzk9C+YBI&)NQf4VfGO zRN;O9aR%-cAf%XK{%y7L-3$$B3m0Tj8y>uuAUyigtY=@+UZ(EaD3CLSQ|Ty8ct%2H zP#mjY2#;kG*g$``7}$SPJ~1vdvlCMpI|lCbF$1)Yt#8F3!tK^!?L9Uymt~d%idkV9 zkt7z;Q_g|WORaj$O7YLDRf@-}ejJGfB)pclbvLNqYHA{`{8TBcs+~?TAa7QUVbQ;> zs|^S4?0r7LUV~m=#({1KJ4UYBBRl;dtB4?sBX1yzyR(l*hT>Ldt|O;~XSlKloIo`W zSGL%}-p*N87`8BiT!Bzo6l09GBdc6f$R!@qs6Ho!lN~Y}9)-B1PWgTPmTVhu0R_4e z%Yx9c=ZimE(>sE#guZNj`{867ls966f&uSBf{qeQeSlgfGX&3Fs>wlMB{0JF&=L~} ziiHwsE+liTtnYgJ@;vu-P1IQHQdsg=zMgn_jj7>!L(lbkI#p&f9i>B&oyTg^s@uVK zoyhmY6<1~M15Gd)@s7R-W&kpsasMJyEb~B~W~3CQoE55lLNmzrCGlAsyMk?}k2|XD zFf+6##**k33>kaGZ#{tz5Ja#L6g;U-nzM$@)(xOCPKKt9GU7rzNA)+%8pzsw0Fg;)fQwX? z19EiU7i&cfIfMeEW!X+9BY>LSuSsZD?cT20px|m9Zcc5{`W-AYZyrV;1`W`W)C|M* z=IHJ79Mh|dsh`n?`Dw^hJ(GSG#(lZqXhJH;u)N8#-?^)fzVX^Skqw^dlkGe5J@NO= z!$t=3ByYd_K5uNuL#D}&3b^}?T<0XS+A{%mKz}~mM;zlB0h}?K5ogg|+>q=eC75@m z%djGpr;*Zx0wQYi&i)>7mO?i_B~X`$#(%gDHLbv&<-N?Sisgf)q0CYIngX6QFER+< z`TVPHe59DI+bZyy;eL6 zXI$BnNn8@)YNltY=rIRmV!+ZHl9n=K58rr&{+W>QFD~2ECbY8z?kLdz9k~ZEfxX%K zB)v909tuJCm!6k)F-wuUS9)Oc&k>9Wz(elk!uk+mlrkW%@Uf(BZ;NZT)&mYJe`(jx zwF{>axmZh|qJh5x$^n3yM}Go=(mLBKRvr8`6KuB$(K%F}grcs*!f+HRlr5Vu zC0~@%mKqU?^WRrOG9qc(SfC-bvM;0|6*iJ31mT>Nz&Cp@17B`)F>qxr8!3U2L8nCu zvi8OUYI6rhy`3X9I3BCF@6|7|1+$%_J;y4CSYHAo-X?s0IjkH<1(oW9zlAGWAK!VS^+h+!2d6n~3UdI!(>( zdc;yHIdHXo6wq$WJLwl#%g0Y3$WEie#~iIt4df{$2bQH$hZ;B@94pbqeaa`-4?y#m-Qq<%W2}OiV(AI&zXN2M zy^>OEo=Tc!-qE?);IHu##uWw*Q`dn9%M(NW<8>DAhnei%2NH=NvyBZ>557K}MJt?$ zv-mYm43-)IaEhVuio@V$#i`Pl0LgiF2MBOOABBB{QNN^=yyKGm)evGLrXEUb>aLqi zBr` zc)kkBd-gMIW$xS#wUugYT52|iJ^+*zAM`WXb+_HKhQxoow@W}NoZhGdb+hFRoJ1#~ zaI==f^VOb5kFY0N@ENH?0UIq4K*eEbaFZ&og}vQyjAEp7!$Ae5227t4?|0OaUb$S; zbo&izlba4d4L!i5iDSKKLI&YE6Hbpa7CPJt&~j%vmxXZ@uX|4)4NO7x6w^y_;cS;^ zMaO71ag;ym8($I?y9WT2Fv@X$y)yLVh zd44CaOLZE28!rIoA#!(hGu-TrJ(S8TDdj06ddJX^h&r}nE1)j&&V5~aT7&;2yQ*gK z_DyvpNW=Y?yiaP_#93TE3J?NmSOvXL>QjH6I#gQGTv19(+ zFHQQkOTJ{OzR5pLre_b|QdYvXbRCz)lUS12G}&OXa0A=c1Q>&$P+GI>chh$_FLaix zA*9BIlTuI&4iL|_tj7^w8$u4a=g*d0!)0|>i3%sV$#@aED16($+lE0ZDteI{a8t?N z+_))}(jCKS`y=(H!91!Q%V8oFFk9b^{=#7sN2wI|fsb-Kmz#dbXyC%CWB1PkFr7{J)E>d@06udz`iy_G9Ch96d* z6RZRlBn5#*vsfwwQt&})EtGXDVUq81!6fME{b33Dp&F{op;z{2!0a^U8=flCc5tfy zxZ>#yscb z7?x;j@!D>u8QM4m=re zW8(T+^=o7E=zB{x@y@^37p?PRQkMj;O5(+Z$gc7(pTOvzMzzwB2WzX7?#kU+EKDktr6eYV&PXA3MU%$Hq?o zM^8D+Z5UmC4b`Ia)zjHc%@iZVD}|DPUi|A~N=p|V`3iX*WgE#~McdxgLHS$uBk2HA ziJ-v9|Cxk7N(*)xUZq`GhpR0?!r?cu>f0~I3UrCC1NQUY4*c=Rvs49>HOQ>`zBNDP z>KQAhTOmm7Q8?mWvDne(w?QZ0i13OBRdC~m^nq4TLyIyD{a?10F%vj*^Dk6X^#&2JC zJxkY7P(|q;JPd8MWu;-LW2i-x4A})!y%dHa=|k|cEuA8E{xISb!I$`-H_L3fXm`(wEkjnWQZ1|FCRW1em4C+hM`A1Y|kS?2KO-HJdPgsghG+w66(y> z{ryu-Jj0AjV}3P6$j-LeBZ1|rmC%wm(`khO*Z>ze#T{0HTAa!N?R4NgpL`r?OIJH5 zW!ZF$azPzTY@*?kNr42Ts&*+=&tA)RkSqY>h(0=XR9+KkV`&P*Pf&(HE4wtlcJ)VHY^~hD6HpdD^rwA~YUZ%xBE_jJGWw{9N%g;+N&<^M3?m z033`$FiKU)Y`ro3?v{A5tU=PE{vO*S>K?WE*rlj1CisghGdp)yU-Dc;2dTckq4A-S z!ZeUOZs&|=DD2w8&X?xR!Q8ngLT?0Dt?M0*W2ZRBS`Ac}OPad^&|q0rAMWe}@X^_se+y-m`-z@*2IV$kx(VoEc+9EpsR@Cg4-79lX)jJt;7zKuxz1 zr(vgYK1eed*(UTEFylg1j+mzZVf+>te$!=ii26ROn$p7ODrQ?Y`e&%nMUe48Kjs2P zg5Rr6$f1nl2gM3&c*sDWUiZ`P_os5ckG{La*Tb$4ZxT3R$`M*1UAbpFGc!@n>_&Kh zdT^$G<_v@eikj%4r3vJV0q95iWzuk0vP713)EBdeJj(%d<#2NuL$}4}dwDd!0-0D6 z&9eDK41&F~Tu1I)=6PHH+h3G&kga0F@!~usZI%x$FX00#h8@cn%PAcxbY8r`K?QCN zBpj`_-E59VL9AMg`7B&okhy9Npv1caGBrHki!^*<1E`qB@Y0&_PCVA!uyglncGWQd z%4wO=Hg;$wd=c@$T4I!g>LFSiK7m%IDnbjPRWKg7YdE?HM6AToSe?M3=)%02$U^gs z7I5n1_jlbrVxSqcoX)%1Kf~w|6jnQ(MWjAAT&br0{P+xgVy8q=Irv)hDfvQlujsny z`pzRG76v!~5dcZ0HQ^{c@@T>uZQXW(kuY`1bp?gb^mqg{6#Yx{!GkiLIAI^Jv!xhm`xg(iFC~Mz0^!Q`huh$zY|ZqEGr>RjZ4J)lP8cNR zMl~{~e&y>;&Jb7;joT?g{x~Q4iAu|NXOpeLe&qCpo8iUT&tapg3n^S{NijZM4sa_JU&MWk5k&y6bcFqoLq8QZUN(vp zL)pF7JX1DZ&VPcV+wv0gYyq8WY4-QBr`eKcX4x0YRW&twuEatFQ+pkg8aV=}q0V=V z-SaBANktYT3m#%if5$+ydmulqv)%KK`D(pc-H|`Z@$a)37Iw>XetJuU*}6TeuhU|e z7&-xKi`Riys^_f!*&f9n9(~?$PW>}|i7wEB+lZ&akYnT2 z@)(06CWxeI9fI$+O-V?Rd4v;=UtQcK)(dKK=9|EwQe#w?YS|xpE<*rUXbmYkssGV4 z%H7^1p2YWxYmMop7`s}Yi`%a6A1wHAX>ysW+^?nwIihr)?@CAVYZ6;Swv-x6A?>Hyg&49)xLvIfXLp>86~w$FN8BWzuZS|3 z9wbvXu`ua;s{IX*6uVejTe2{&qI0)Z;kyY|@ApComW!dLsdIbTH~D*YH2lD5jL{I%?x zS`H1=Mr3FkFP00tb115kvGL5S)!v%IcHv-)HkdwTM)t|RI>k9z%Mv#tT>~Kxlim0w zr^*%J)uS3c((@Qn1hfP0u%&g}U3REMx^&D#`aTu3=0>FN;Tfj(f%s5#Lf6(c7V{;W zyMwAaYEaUx@E>KiHvO^RY`T~bbq@hANLyr?5_ilh@A>epAj0jl>{`cO4703~d*hp5 zAG>OVPFMO>wXz5A@~<%kURPW>zu)tG^ZaSU=-%$VnM2B%jr$wYtReA%ahYE$dIu7= zg4}2O+PF@_moPyMgeZm{3H5U%ep%_ZH?_|PH3fWi*5i`f$;?|_9`m2yoc(xA(bI@Mf|TH4pcr6kbqX^&NUd-$k~D>k%nQE1sa*Zds9>uKQGRf&?PWyIcT z{utepNIp%!p_RNwWw{-$Z#|G0mm>NI7jzg&Sun*1r+iL=WZgx)^Z#y8n_kZJPi8!Lbb8nnAb*0dj&FM2Sl0{_GrUTL!|QbJzlype+;Ox5SdG!JR2I%|5F z_{Q+uWDzI%m6T90q}JnULNOl$Ttl)=;f4T_$QY7A>exY;snZ_&@WaHp7)Dr#r}jlw zpe8kb^sBY8BbNjdBwdNDbu5lk|K`eYf>6nz4AhOi4YH{VZq& z;%(5^hv&;F0y=}i8*E2ZT@LB*c>qxjmbX?#FqPm2E?dx|$0x8h7`#u}G8a1+ zK{?gpWSR4X<`ZeUYYlxtga-fMH~YCmTK9eV-o)1o@bANvQ)CwEC>V%$lVLh>T9ZR% zm|~qd`?;hxgI2l{#2pPiyxGt^EwV>h^Q>6EvAmRSn7M(nfnu109%?~Z(Zpne=T)as za_?B!NogxPK<I@R~wXKt9r zRL-^S3Q2YBJJNuPh&>}{RRMCMjx01|gg=%a%98|dJ0*yKh~TAHr%ymr>gjz0AoB z+R_+OUn`+1peEwqZ={`r#lat8BzR@Y9BPVI z6l+4^TJXDYmkK>r<}Gm)L{tg30hIhp6>aYihyBfaG++Oj5W1mVcQIOq%W- zq{?E4Di9(_Azj3Pkd7aoaxH)7;Yk9(fhX8%W2J&$*EYuYT-qr1-#!ze`iZ9AvV-s) z4ceP{BV&DZ?WK@VYi7sh0PJqF$t&myv+^vfJjeQFmh`X7v@++PNLuUu_Sp_6^g8(I z+VSh283asIDL#F9>D|CDJC$XRkzN8~Tq}wC>>4TZ>Z8N;?dzk(?utsU*zdFEr0`r& zb(%R#P{5^O3G)_49D@{+C>hj~U^t#3C)SG^a!mstJi{VqJfj2&fIo0$FyvVY!w;7* z4o3obiJZ5|<{MCI!5COWlDJR}U3wg4Bg!Wej|n?IQ|ohS$XaGDLV#OVdeTz1hX4fqQkRIj`w--- zf3FzIN?kZSneiBIkt~B1LY@<4h|s;QvPVG8n4I=xzsxQv8zq-M^O9sD+@#W&ezgAG zx}{8&`3C+b_Ic?;3abc4%mvJqh>MI4Z%%Ns!g$IbC6`7s&N~`L6ARy5V%e$h$Nuu+ z=C?k!1oWlN8f7uIb*t^C zN;_manokR@DEVb>OWrjhDDbw;dvgP2rZEFybM$HO`qN=1 zyf7EchVqW38=}LGa43id>SnmGwq-0mL%mV&tXjyn$mLjPHB5*xh*7@n0@>n{s8NJa zQ#n{Be2X%2_aGXow_fxz?Cee#m>b&8PtWzsoasG^%bxpb`5<$}Vrp5S-|MOf zgW74YED-m?7k;*7`Lyqe+@k$0)4+ZiI-4fXRZL$J_&s7HZ7%Rk;uAmHJ`!x0c|MKD zJNRav2u#t|RBOkpMN$c)+-(29I z0a7+)adbx)HM)6sb?bg`2u=m(TE~10Rl38a;wX^Kk`UFcaPts?Mxr5OUc%33nLx!s z{pP7q6`uDee}kG}{0N-@{dtu$A3$J4|w^ z3=yZGis>#?dhh`%2$mZohQ_14so~2I_8(&S0)nUPPl{R3)%y5kdQOM$tF-H}cAmRw zv*FAWZ+BKDWKdu9Q16Z;O<|SBs+(Ig)duZnyH7;tuP~p5dCrIVy|kFzoZf8gm6q-K zWgf{NUrnnfBE8e7^JagOrnYwSqqk{!E}HVD;-xslp_PD?%lsRkREL(Fjdqv~U91a9 z9+raMZZ2(a`L=Sp%oVyPy@PLa6wGmdUnF8SZ?UJdQkhz?L%4Wdd%j9NWB>6K_ogz>)Ge0JuAFU<-l zf{r4!^akXs5SB}Zb^Ugp!V2^QWN+(X5hsMAjk3VyK$bv{&xRx{#kk%c62G;z%A&-$ z9BR}&rs78fbsi`;(hG5hTnx!VNCi2R%tv*ZI$PdNip4KiG3R&A+6hm3bV?VM+X4zH zbYE&d)9l@$9k19eWTx#kS&x!SO)#YeE|xS-h_fw$!dbbvq8X0fP_wt9K!U0#0Bkx~ zYPctOq4a5l=MNJEw%dc4cVUV3JlhuvXBdO403mH;KhpO1o#XH7Hcckj-L6W?k+%Xq z-F^HaT-nbyp1WkMP%w3Tsz9M-BldnwLC!^IlO6#Gw7a7-AtiSj5E}Q(110xHr9J2w z-vuZ<;-0znMK`RpSv7tDQq zJ9JrzxGta^r%J6=AKutfj|f5&P-elW>I5qERKo?@O{lO)#u*~>FsHg`s1TCgV8Qb! zz{Zpnq&YRtuO`MivYdFwa}$1sQrL3(6sPn zC%-2z{^@yQWh2D7aYxGPl;hoNlkOaXzFBGT)4_szbK?iV-(RSNumvX!+)4ZFb*A8UNC%r+B1 znBQ$J^AQV|S^m~vb*_e+U2+XB`1p{aLT#X1|bg_;g7ZE%k5VGd|XXIJ^)G~6pK_1oz*qkYsD z?lKShmYS3iS|XD>9%?`0Pkx1BaOguOr#yv9?A-<7bM+MS&_DzNplVV`t;+?XpE(*K zClRg|v6KX=xN13f&ls;zSU#*5df=zAFx@4A5RjXL!6%HW>IS8p0-?To&rN_aK z4?}*O`BhpnsJyAR;YcbM-6B_Ztr)E- zI*UCeeL)$B0|eXhG%m7xhbj*KSof9g6qOfO+&!Tx@NCg21t8r_S>xPFx+!d#auO2y z5s3P@eDaSH^R(c8HG7?5Nqh&poWAcTYxdR1Nj#A znl{MdQhAv4c*52zay7(oS@h*^*_*sd5t2n#Wx{((OVImt4dJ2i9UxK3rhgY6Y-`Fs z;nT*~Y%`zHQrdi{xhZayl8j}m0?&``z3=H_F3hwTmvB;R0K`@lPKg^>gPFHK-=tAP z3-L1a@Xe>8vI$h^To%gNaDR9m$}ECSr^X*7l}t&tBkY(v+;&r+c0GRVMlDI2g05-%-un{@ zJ-RWdeb;H%Bp+zN4Dadq)9(|)TLWjgg?8=v+=p1N4=^Lo9yCv|0YOd^s2g;wXp11f z;4J6?u8;)3-N|~omrJeR)%l;*Yv$Db-n#@Yo!`R_9{|Nn~y%zwG9{8x; z^?53S3!dpa^-h~?Y}}ia@}+1*Db0~0F*5E2mdtYu#*9~oQFt3J&sZZc+@920R3S#d z#1|;sDntpgO;>C(-WXeLO z!b3qfXA@i2&(9W}x)bXGl2`F+zD~~9w6M;rt;~=VU9XFG7{k=5JyQauE^JP+=CR$| zc#>whcuv0|2BzyNvVI+%@dQ6#=Pd0R)K?T+ zm=b9)sg#ftsYMR8p;9OtHs5H|B>5iol7UcBwS5o_U!?c_NvqOfuwBTZy%zgn;2oxA z^vFpEw!(B&~m)CzIz z!JmL6DGxG2#$?YI-`SRw%LUYpEa_d;WRHNFY(L?Q&f#N-l;6<#Qs%)8YYLz+;YKQ< zOi%$>(k>RMLjEI#)8th@>~ky>`(vpbwL{m|npOtTFHhfP1gnPgybmBnmK<_UNzTdf zo?HQ%*EE+e7iqpx^#3%&$zV)~BfHqG@7V&L{ceBb>1DHO^aRNiFWf5PMS zq811+AN%LNTj&78BvxA-l;>)rK)0lXw`aXdT|B5BbTgIkoTSDgy}yagFITPP-^lXO z#%F4)eR*ToqK}1b|6udPG2jvX9h;9cFsyXiy-TrY&h6%XtVSb>MvpWT_M#5o|FHpW zZrvcMsWKo}M$jC?ZE#EH2bTpNZ+cA#3pGo$)2XWgBb{#St8LS4c7p)MqZQ59bSe7; z)#SZNGhNR|?up`~(PSOzYYNr%Qv)>-1hB><(YXGErWxC%p$guo)!(1YU4+V}faHjS zL~6?;=8s=ky`q~~={EnMN)PyJ7DatYJz(0#Ds-yh3ZXASM-tev{CADCUjC4UXpBW>-uHLaSbiaC1)RkY^%?tt>S%=Dx02vNG*>eNLGb1y-K+>z`K&F ze~Ck4f@dfU9vaQz97>rb3tbGg)Zc|bQOXt1GcZWhe#lL;hCz=oND{v7mT(MsRmis5PAB=z1T| zX(B#arQf+h;3Z4=z*l4=^4k|yKWLqI3!wRI2+Lun*WT<%2eIYXbLNAzYWyLc zUBo};b?T_Na9UBg!#V_5hE30`35=T>+9ORld~T=?o9?^m6&x>@5`hd&V{Dvi%Ca7u z_a3TU{_{rD<<|Bdqt{~FXNNFLI7IFrloSMomR^L!Ungc|Nic+jtWJBgOBg| zy>3QYvhXqYVpBU%>c{KV4iU?WLQyH^lW)im4RZ4)I}v_>4ZV0|zPpZ(6cpr_w~+*V zlpDAyk=mt^qFAbskX^3qIpNdS8AmAKB2-+NnU$6t0Z<(za>URrIPVFa=ORFlBFhR? zUti=y4m?lDv2K&KB&ESC(zM>C4N^3|;vK*kQo2kkPpQeB+omAM#5=rV%9PUe~#8n=mmLa96$1NFurC z{c=IbWM=(|AQygTA(L6}tBcse2QIUYkA2VKmMDo}P}x2(ph{<<36b1I-s5|X6f|k# zvB+WOM%8Dyy~=bv$z(q|2-UdhR73c*k8o2p8D)ZcW|6)f28FlrY*k z*%!Po4h-SO_AwgQ{%t|uKLSypI~jReU+Vslq;i)Ox^~>bgt>yzav|k-QgIClKctPM zjC_qN3RRHToO?(6 zIXAXWYiz|;PS@mV%~M;g+AHrq-0(W0ddq2K&Dt1`1IjA%{;Q*$p?v2BbYKBC z3D!u_%K(s+kMv8k>K@EdzwdaJhsoNcFqws7J2}ZzIK8X)4_$%3!!2Luh!Vp?1C{J4 z^HMM0u5o>9a;yuuQbuc(aPF?2rRh**!N8!SFw~b{JCMC7F5_oa##LO zunO5J9TZpbz`=BD2{w9#7>7}`w7xL~&3~Xw%nX3ogq26TVZB80zyzmGtz#R*b6KEB zv*!y_Fq^062*`CAxBJVisotvKD=Yu9#V4Uk+9Gx_EW?rPxR1ln>9KVOU*8c$W=UDe znOgQy3>(#8?{@S^vp(Q_kBA3|gVbF=nB&$V2*{G8nNyH@T4KbRBM#90{b;FhUl$zd z@3A>6pHq#r+yBkTR;A@EmqThEi0&v5oxSIM7GO%7)BWo{ zNAOu@bb@X=&J}uM5idJV!9#Bw*~8u?K;J+T={+d=xcE-aNXPhVDOI{;tBKK@wAN23 zAtjQPH8m%7t`*;No07>~k)_B!ck+@SD*jV!+Eh@gvF1>U25?(l0knYx9N7QRpH#4v z1tr$U*?g7uvd6gg1Ay)4ZzJqjm!5W&=h|Ye_cUeHQV3dgeL7_=OuCOaOQ0?<(uDbd#1Jrb-DT*_lbQ6@z zfS&t2KvYi1ajuWVKrVQ>Bc(V+h;L+V2S6dSR5n;U-3S&i}d+shG{}0Q1)U zmXmF>(sTY=yTJ*zLOfCFT;7oL_nhXk?-Cn#Pvs01}2Ivm5&WxIDvuyRF_CEl;z@kU=D%{687t#r) zOOJ#e6dH1ZpMe}px;b58g3yX|N#sO&K4p)02g!|n@oP+C@mI!8rd~TM3+aTxg82=( zt;i#dv(wmm!ophx(~cdlcY2qh#kaW!0nFfGeFl%HBD406#8u4JZki<$mf$a-%_MGX}7+{;!^g zUOQ>$LOOXHgI<*1#NB<;r$%O<96o;_oKfMPPx7N0jp^I>isMOuuK`?S9TOf~6x-I$ zoq()YJ44QZ-?ACp3bsVq)fr4WmBQQoCvFzxUrN?@J(Q!FP4X2!YXt?$Ui0pi64wY< z{I9JrBVBCrKeHDq*`5_=O%OX_D|dB1jTIr_o_Y9(S}=HydzLE!TxQ;KBgigU^C1A<1&&(;K@xAsJFA z7HSep6s-Dve~sOy#S=!zgDIvV7($DYlqUO@l>pc@dcl?N_^Gcf|=!xe9z1*e%r$yDS zi#@EA>EGr%ZAP;wD%zfbc6bcGZV_N0#{kxQEss^cWRf)uCJ^3JV~rOj_%=1V7B z6{tf5p*gx9(RZX=At|XeynNjJ?9i)XA7FwnxR?)-FKK0eoR9v(rh~yMkY)L%MD(~; zI4p&nh*!)gWLIel(d4Az2o~{Yo#`a`v(|*M$(+&%5%Oj5%7p7ol;Z7Bi;>nP^Drw_bJSul;j=4rSYu(p5DW?**2 z(PCs5zleqUDkHe%9M&e!Lrp-keOFd~GUMMAigfra9NES}4y0whUz_ZVgppzyk8T=S z0g+SXK(mKr7r6++&>rLbF@Y_6&F1j$B{d9Atx%%{tw7vS<+BW(~NA zdt?pL`#xvsJ4@f;SEubT0ZyEY^d}0Xa-L_>rFaQY0_zT&D#$BN7%hE7j!7u-dMeZ$C3m0#o@WN;Tj% zM{zrSEX6LMT?@b-KHa%n1po3d6-6Bx1-iP~iHMLtf%NW|YgSzMKJlJIRzL?rl}fbf zaepB0@|!1*!k!7a+1}{mnrx588!D%8;S)S0*$4yMVU*}QBP2b^UtZ1Hk|01J_q3og zd^sR()TZ(_)j|)CMLZbB-RU=cIx14Ep(Ykj;k7RWZ|wx9B45u@69jx!DC@i%Lf0X# zqTLE@hjT}urKD3#eLE5t-OMInj_Yjc6gIw~0W!$Z!PAVD=Y+3Pqur;43~h0K5V8w- z;YZoLg~sdSr9RwTdjKM zn3B7bU-_$KMn&zDqU&n)IEcY5Dc^O(`TSay~+LiF`g@ zko|F|&1j+gk(u5vT4#Agvx}2xU358yjm2}rKUL|^p2zlHNxTqMf2KhsNM;Qumv}D- zP9x{zKXmf>bR$Ob--a#x+!-hx1`F#8>SwV^9laJXj(~FdZW3FPX8=)vcMmZTr$pCb zH-*_~CE6<4iC!#dO9Dme;Kq{8fqWa;i3-;4PMTDc?>Z}gT>vg5#l=n9g$>+MyeQ?5 zrJS&(&JP(Rc6@Vk$D^fFN$uJ6G7ry9(w#BdMt&na{>7LB=>&0x zdFsLCqP-ZFOT>A{%%j0LDvNj$AA_~uFnd~Eu!#?Qc!h^%M zpeb)VE~1k%k&JMwg%01~vlC&CI!N5X-Ah+WQV(oSu@DHxb4y}(s>p)TfC~hI)t&pv zx52I%ff<|LaVQUY3(Q^g9rRV92kXI;`d7IkodyMmm~oWEI1iwUW6BVQfpQ2v~sS8;O>D1_cN3jTna4@!&e<=py zw1r|i}?O!Tk0v zTT}oe+6sWw)d}UUdlj%N^qC3L*zM^1(0OM|tZ-%O-+_~_maGguVh3&mxUzTnDXB>M z4&UH(FlZ0J=u^c?y(`Z~Ud^-dU8h`^Cqmrg{=%CsTIum}r_qg_2SzoDr(6e}C*1}t zwj4xSbM0UDf#hCRJ3g zh5v}@IUGS%J1qJToH~!j>=1yXCQx|4+PG$S8!Ij6Ca+oo4xEjqs&J~W2V)ijNxeJ1 zIg4VN@DV)t#2w0E-kq$C&8`2{Up?JD?Py`_6I-^XOv~1|??8KyrAX?NwUUY^~(u_kTJIeJ%#XO^&sk#3ZiqRx;bN2SBzZ`kaL`wF~s_@&V+} z%LD;C2-gYOyQHls{&t0S1tEEY67oGh2N_f@o?$$~OR-Bo=KF;{f6L33u8$vBuDC+X z+6b+aM?kOPf2l0~kiFnAzDVD2Y^b)zpQEgbP!Y=YI!}-nT!_nzaFs9xBxcI$Hii9z zwVibZhS!7M?nB3xTis*bB5~Q2HRf4c32R9n?ce0j9j5807*BjX5_MbUTlT$o>qkJ| z5KH;}5#5jPV<6ShuFFT+^QGc_8@Ul4rnXMul|Y;T;*EmvR1t4wasXd1 z^?>|kE1j+N0vgoRQQT(MCfvF~=wv<7u@&tG`U#}i{m$aW22y<8gE8mQdtX^Mt_%y6 z0_S}CdZ%lw&p-a$-YdsPW%K)CR$Z7>XmHP1b6{`9!OJRVrdvxxC+!=~efXyO@s@J% z+*XC!qEjhJ-xbL?g~&7N9t65M?L$+FNq-}r-N(b{A@UbP2tFmZnB4Ve*WJ&q-h`}LFcwL{O{r@6w`n)vILw#>hLhZYD55OUE@-X_r_OhFb7b%6 zy>~eKKz?@v8eVC$AWOaLGV+lF00xthGH#4njgv&xHo+t5gC>d`WXleDKCFnaSRbiS zGQ8EN+RW9*L#volUGu!nx+p7o_7`0Kuy3 zAOmtk#@8EyJmkCODba3J7Go;s9v{ePnZ21?n-^b{K0Rh5*O_g2`$o_r`M+CzOlvCK z%_;o;d@*o{oXALIue&XzBoV~Z!ADP&4ra0Nij#)u?*^SQ7TH;+#4$l)hO-C-)R>6i z{&Ifr77OCLxvS+Q?4XF1=pg*9*%zhC=pfh@<)-((Nrl@mxfsmnxbOF4S2L}y_m^pU zBr`4aetfU^d#fpnS;Hx@lw}HXa4xQgNY}^l(8x$;R332D0mDg_jEVv1+mDAWwwK7*U>+pBsxk?VKslN*b@a5wVV~GYO0E4wM@BUW%+AK_)Ii6&VZw0 z^*JCpilxjEp{ImGHVJ5xndH6t()1fY{_B^asg7>!NbZn%XAR20ErO|nNt#~mZ;o$~ zqz@fFJn-oOm(V5h`@T~#5_^F~1ahBHV?lG2M`9LLQzdqNJTJ2Asb^3Nby=)_pxT~e zhlfMw<7SI|`)?Iu*QB&^ilMI8-*5gpk@)C|ljcXjDa%qV#}f7$A264`UnpJv3M^Re zJgv&Ub1twImihZlMQeY-4D2gIC31Cnbl2~Ma!w;FKF1)HxG08@BVP6HJ|s-oTg3@G z7{Ace|CBRgBw`HzdvkMgmO+zo(p8p@L}&)lQP$W#ubgX9L0 z-a1l1LDMzZtMo>wk3sb0iO4DWukxo2Poj_!SN)2Lfg%%J@_u}};aVaKxfX?!tUQ0_ zI?$QwidfSOEin7rnnHtNkL4HPa&Q_8)3{@6u##AFAm)(Q7`#a`2i>pxq>bsX7nr=7 zj(e(>(_tVN7DB3cF|?F8GAjXScJ!PCa$W{Q-R-l4@QV@GiHrl}FTum~*M_v0ZP1FC ztBIfi+#ZOPtV&9YKFW8!vzY(DDX_&1wCNip^~n1*3|pxhCkVm1r!>JsDoR=1wmi~@ z#%a&M=UVq^hWYOB@1_s-0F?lGi=7Hp?HpL~wX@`7&ute{lmK{m`!X08zjMi&FqOh) zG=mM9N1MRFsLnlXIr-Ah{`ydDS=rMJ>L9KV=N>yl+lLe2uKx`x54&lYj`;9zgK@ob zN0*5S2ozmkU^q95u35eV2`WoQ(xkVW;hb$%@PSN<-s*DS^e+XItbx}LvV=FUrL^~g zupypvn{1<^qc=~{3NN44M<`IVaXz#D5(6lm_%wSF_rzr?Q*YIvrWW(2ee7R)tiNjq zdM^vuO9b)8SFbY0q5>98W6YMITL2T0Qqnr~}=Iv$%s2 zF@kGGdS=osajop(oMs@v;149_A7dT>6ksIGeUg1Q`xyFYt0Bl-^LWJ>h_<|@Ljy>w zGoNIBPKS4*nb(oAfQA(Los&nzZ9Lw&8$llvzK7e=#d>R6@;M&~gHfmBO|20kPbv;upiy-zW+BpSucpXecwSjKzAD zUdVzIIA%2-R!Ilet?|mxi?{ysLWm zy6bus?6dGmrhV6&Swh)HWk<=0aT}rjK({vx5%T&G07@a#@NYK2Z_Ydh_>?oM6)y! z0!c4fM6mlDc%UU1g&^L5Bg#|2zx!~U5O5s6wVqHAs^<}H4=m4 z#^7@Lfh1R*M^u7WpmcUB$%W2%0ZWUxC7F;OHc!KzMz4-npY4~9tfU^L_Eid2IX57s zoW!#5XxW84jtp94m2NVRG6Q6V=OSnv={;AxwW2es1J4|?R~Bk}HM=4HZ=ppWvu7h` zy^w)hweAIqxQvz1RkKp2Ic@AS)QtsK)Y=pt5hpan+!UL6Gfrk|cXp!LOsIfRz>Rso zm8}3a@2`7RXF(0cs)ELLI7jpT>KEL#0P^JuQCsE%HFC~)dE>TQN++4P)cWqOSTMBU zfK$Is346P&D?fTjgC}waSXM3L%i-%F%*b25(Z?t0pv6XNEvn$$9B>X0TJd;Epj(^0 znR`V#nm^v-DWtkQ=Kt>M@}yFMmMnD>*f|p)<#?b6qD9X}&>Bi;aSOkQ@xUK2m2ADB z>{=%7hVTF^4ju#NYtKBe!0!aG7L9tCbXZ`R^l$5Je;GEvIEr0AEQE(D!3OpW=E)oH zavxG1RjZly*5U;gcifTp<7;a5q}eyOD_3yJCdPUX*mEEU82>Jr_{W*lW)+`8Z@`&Q zRH&Ez&{cWisT{&{P+X!en1cr{d<~orb|ryh{D7V@sW{~{eNb2c1mM75TV+%b0HPwh7OW5kl(^5g{5bllA`_{l( zxo1A88@0B7{aPUPiF7HICkAA15zlBAh<)0^3Q7Oqb#FRK>Q$yJh!)Sf85xY>P|N0d zk+AA|Vt|qupqQodp&?0O<*wrbn%p)(RT@m!l)~=K;kTbHKzSrYObga(B4Q~M|Cuha zix(Wp34!(bo`qKKYR*x_ZH24!?ooO>e&2P;Dj+;}WCL-pY~3W6a{%ofFbVj2uRcyI zkJ$pLlAebJJpMiW$>|)>g4c8yq0N~5+}1Nu$*u+WaM>#c_zik~1oq4KnfKdZAsTEP zo>@||-snRvd{tr73N)3W_y%P^poJu10%oDYZpyRp75>o9XO`~sZ<~TTmeUh zgo8XRekV1UR19+?o8hLkvt%FBD$^6Dh7?B{4e;2IP+|n0d-L`KSz<;GS^=Eof(z6f z5jtf7wOtGcE~=hxA1(!(-KR%fr)U+LI=d2r-&Q8*c-JD8tY%h1Qi3TCyq57bQ~_jJ z3f#_$Jk${;xJFOwN=DkS{6Z6(mwZ!Ix6dkg&BxH=;k{rg%#|Gd5l&-F7!D|pO)N61 z9mZyc*PCFT%f^1MFL6<_F%a>jd%(jxUDo#{VnV)iP+l{G+CJHHC-$Xdo5!N5 zOofYn{;lr8L#`EuIcQ0aOqPU6E9Dk3gj4I^k= z#K&Pue0*U9?%GS9p`ppV%^5y?^yK`kkqo8g;C9YB2$A-9TkL4`$MgR6b>E|>`zkBP z#liplD(qJXIzpdXz4+pFIx>-<>dP=KUAb- ziVYxvr|rP%#K37! z&E#`Yue!-(b`cploElRHrv@u-Gpn_DbX>bdq)ugp{4C%xhh{~7eUiT1mQrZiXKbOa zsWeB6ROiyZPh+3<)%0Ex?#U+G@D?kC#)E<{vRTnXAUrU*f5vV=>liaV!fX9a+|@47 zajh>eEF$Ke8M-=X@9f5m^{Lan%FF0aMUKapru`;d$O$@%D%!UAc3Tt)M^oXKf*|Lz zSPk_N-u?R5Bv6sZqfr=SiE9YGibq2M3cUoFcAcT%jWCw_+yF zmb8aSiA*XILi(R_=c-e_>o85I2#C&o{3K>OM;3bL;}t$CsoSMHS6pI?5QA@(ivy_p z+3(j>DeI_BTlt`)xz1#X;^g}$+*R+P5<;P!0TZnAl51C!xTKF9x@%6 z6N%<)@bggX57}zRy&}}{x}LMLazUBBND_zhT(`ptR={`H(JwupR!*Oo4Vp1>3-}0tDI>=VAyXT@t zj16Ue-|(%-(c*FiD@p6htQUh)L_8}eI92PqYj)_lo{1te@ZB}VaP)e^9sK(xn%|Ok zqUc5Ji^2pVGqWNc-JbNgNGCp_o-2y~z~7gw;X{|vvK8$;8)S>u*`wK@3imjp1&jJ* zH?_m#(ZsWcNa|r1s-ix7HksR@vJ~GM_SE{AOh?X^PJRziJ-Yj2e~0RxVDIPtU!R^6 z(rn&|cnV)d_nHoGAFx_$OR|Dh%rq`#i!nKx)*zL3>V2|uz7Wf)C+MZ4(17%7+=JUY z^;^e+xee*9}MqP5OKj8V#W^>IA+nk=WK(J=B~pm%3lRU zHSCV6&j}Bhq11E;RwnGcY$Qq9DnK)7wW-4QDp986p%C@hbW||e8conE`9Jppx=~p~ z$BM{khY6ARFOR9dJu+d}a9lI7vDtpYzBx6_Os_u|xEykt@caDi*$QQxtx#@iu+=nk z;-92Q({F>!JJM^{-y#FK0?FrL+2B9wwvy#ov%XlIXU&W^)y|vutFC7Lo}GadY8ZBg^1(0Y-Ce25_^n2WjN-@Y2`3i?K4{yq5qo>6UZ`O(Q9_ls|I-obm7=Rw*N-Opfv1O5(KQ|ru46?t;xz{e+e zH^1oPPWWcd_A51du=_2WpIdgzm)_qjo-ca$0o-1}+*s&~oyp5hjhFX+YEJ%4iMg|d zlJ-!~Iq`=}-gD+d#+OA48v|0YcR0gFn=2kWpmLv4pGj!0nO}1~W~!!+>U2!yu`MOGzI&hoIYEXwj=?fgZ>QMMnTiQfOQySVmImCe&B zIodlc9LF0o@iW8um8O_MLyh?D2H>bw8wWkTm0-H+G6GEEP>TmY6OTKT^J)6k zzBeOLo6hzW0XSq#o95Z3+}wuoLp>RxAS#jPpK9`Zi={<#nTtvsDR;Nhxb~k|m4y_S zeqXKm`oKtkkQt+|vF5N@pWHtWoAC`y`7@PTOSbjB!y_0+ z__JW$tH3=AJkOfoAKp2s0B^-?JyT!Wlp-2guYg-kvZ_f!UeDD4uYgvKxIbK>>N})h ztdS3inapDzS987|90~mh*1T^zg&j1#<$OZBg-e!q>a!qcVR<(@AG)MgDtW_jk2*nf zy6hooH&@=h{ueE7^BWayjOBXTQY_U0k>TgBnhJ|J>CV$~pNTG^dj(sCTp74c%Y9BH z%fqNYc3t1KDLF*k148U6m4c|( z`-860^3EZ*lv!@%%fBoqrOqo_WBF@nni>%)_8MZ~8{}_9rJN+Os2H`YdJM~WK34__ zC`+o~APFLx@sFK?z(10vI2ctwocd?@0p;wS$k5_3|3|3`+mArPmgKgp1`*fnxiuhS z_OHI0P7j(80s=(!ikGi#vuduf#!PH|C|WZD3hazi-cPm*As_B!kr+>H;KS(*b7jOg zTi`K4x@_LJCf$=w!9vMttuM#Z1@ZHJ>|JHLQ*l1IS5u#bNJgNUK90mvh6%Q z>>AYFR_5wQAOZw7IesuihU{j-6*YCE1Rl()!8V*2OHcvRqqc zaOt?mSKma&$cnyOG9$`S^)`8p>9`4!B_vXeDpxFO$_b-u^QwkHP4Aq9^<{p6C&ptT(Z4edh3#502-8lKdi3@V~_4o+y-tG5D9-WL^pw2X*N z+xkQ4AkbYbzXikG8&(O=eHQrF*_sw6^_!L@_M3L`+#>{2KkKv*-&J3rdb^=BIVPP- za$}9u|P>tUJ{nB?7<+@@u1J{P$5PI$|7k$nEs4^0tNL{ zhf5{J&XZ_<$(5zk^a#mK4g46=B0!zOQU*G~Vj<@|E%q%dz-6vtd1u6J#{W%aF-$spMR`PB7 z_gxxrIYsw}7e5qTVPG?inaE3J;+mwC)UjV?ROMG)8ZJ}icwa%Ek4~@S4!E%|L7UXqG6kr8%e;sBg?57sHoOaCXSR-#ffw4Qh zVZ_C)C%^qY{`)JyYLa*6#VW!HFNAA=l{Ux`t7SZ!Y(*UK6d z_YM~3?sPo5GL;s5>6Eet4lR0zU`!ju_zvz9*VNqKwyikKLI3;cr_q)k@;)!oIn8hvc0L$bnKX#e^az!+nqsD<03EDhnn0A1y9{^m;W z11L)W!@ckli>k*a6BL({T5Y4dm{TRT6v-bbahbH{yr$)Re}gNw$PTXciu3(QE1jwm z%^TK0DRX?T}?nj@S zs>YQ-{9!*Xz!!N(W&3?7KlmoOYs`eR@o+qy=9)i{=#jL9H(Ugp1$$fcsD*-hHD@<^ zoIJGaL9yKq(e5MlAGD<IOoNPq)Ki^(jluTbhiheBk9j|tk z$8M=@OX-QgTtEOVxfRK=bw$Ii^`Xm3@q(kHk6w5@rw(_#IQrWripnO(?GnLKx=go1 zP7+MZ`0VN>-d3A7NTu7;!v(1oa5+)L=>At~@RJao>p}>BrIxf&4Kj{@HX+H(cCqFt z`sx!}-tKKzE_Sel$ky1USG1Kt)~UI6035vYt=v>!$y{=a82^u|%|UfurcHdxxh?TE zsL6V|?HEdCAy=1k)ExyOTYn{{CLQsK+xrFc&c*X%BCn`Y$m)w0UU2DI(W|MxjOPB| zX#n=?v+lIVe*f!~q2Z7=@uY8hZ%S*uvYPV6-4wUo$kihjB=ht=V093PL5#igxMRaxHTn}kj6SX;bs(( zP;+$pyLm%hg#XQjsY3sHPeSDA%T+|8x{M+dZ`Ac^z4dMsHCx!@kI7jzWV*KnsozSR zoOfX8tLLRs_h<})3HBh;)}^}{Hxp=RTDj~%I8A;yc(k0vLdeaJUj}z zGm&V=insf(ieCuuGQoEF>!NrzNRm@kG&|Je4dT`IF|xqR6fBh8{nS&9hrM1x z*SmNqDb@esWVv)Zna8)t-%;TvEcHAMheai<=_kr}6r=i+o4VnNYjpk`Zx71lxI!cz z(W(9O{_md=)J+t#fP(a#f1960vAj@#6qTI>T2<(LNoB>y>-Oneo){coAuRLu&~%K~ z-V0ER5U@?h&u*Ev@&6%#y=Http(2iqG;C; zJ7qdDvhGRw(lwTrIwkaVESDPDl}hezRRM4n4;$H%zWtLSmST8oIe#}$N1)V3ja?_p zr?KFdZRX_(vi0ypn#X!|s!ZSAKerrrP5*HZ!o1M3!noNVGYLhE1;3Wf61xvX5a4sP zm&I82CG2K)dN3q0nhZaK{y$Hdddz27^n^dbyru)h+I*Wm3~Gnk`?V1)_mn!R3QG4< z5j)gUNlpQ5?z5A?FQDC_1~SzuB#celu=5`^9r5myGwj{7x+ zcxQ5R>%^#MDWe~YIDtd_FO0zs9A+%$Z&)!JOX2o~sqfYsv%W>5^-3GIyKas-Z|H2l zs|gQsNXnU4I@CFCnOSUff6M4WQs1MJwkoZsO6LEy2vf+3cinN%cu7rb(yc-30renR zV+u+59sDvc_nFfFGQUpk2?eass>F9dhSc<-Q4{Ktd9Zc}fD%;;9wHwQ#-FM!Cg)YX zR!Sw}RnVV1tx{canlDvPX6XuNcDO3uvsq;icn%QpvI3GsF(HotWz#($2DKwp;79I= zbjQ0oCW;Y2;RBRb+f6AU4dVa51O14drw`{QPlMhQF=zGW)qDK?Kp8Btt|!br8`JY) z=c?Hw=ViV>9}J@ZKtHs|UNUVo5mSGw?{3xNVLHp_QB;?wtT7DBLD2U+fJ$BC_osn! z3V=z?OpdAlChDAYhm)U+9PypV+l^cDLJ#F@C9j8E9Csc*&7;9W# z)2<@ZVVQt#zx%DU|3MpjR}7c+r;6-nL>W=+iXX3QQoEAZ4>9};&%E9b4}mN4p@@^} zuaohGG0DUkk^00oH(DN^UGw4a8I{!3 zO6-1ciTK{{(SHvB9OgxPQL7`Uc$)N)94uo0r5LnBEmfJ{qSLDM$S{5@#7FV4mnW5Y z^xLfI-2`X?EaJnqIvBwnxY_(k*_JVK&1mknW_PNXo>^4kdCRNz$5b7DXHLM8B6av{ z?&4*Qhecd>{+1#xLt_=!`a_OV=bfQH7TP+!dd~B&C4?ukw$l zDw1RzeJ36}Q+$^N^<8{qmIH87MK|w%$!WXhL|5&54UAs|t&(%(zaN7rB7ae5eEaPk zJ9ImK}B`bH{QWWV*SxSc@f9r)oj#*tOY9hp!izH7>x zWV(^(q|pxId=g&1We%0pLZFFTot2Cab6KRiMX3PAZ@=ew(0l_`sl!y{_iH>C240j^ zk?Mb{AipFS3Riehv5|@w~d2P3n;F7z#C$H=_aMnISAK^4MhjQmPJ+mt{BEwIU7bmEii-#)~q3di#Ov zvu)q+=B&N+tQOZ?m-FKZQ})a7RX) zU2gMN%5~nj>BGECRG*HM0FIZ`$z`$N`xP~hvz$>cS;eWi!8FnkjDYhnj36ouhA@qW zTWqIs%KxcSYJU8g^0vWvuqF8)7xdTaxJ4x7s3=q|4y2JFIK!>B%nO;EXTSt@lV3g;L4Ztp-2+v#7b9 z?hz?D?;nfDzYgVxq70!q78lr@z>Y|hi#1~|8li1KF?b45dxNTdx2nfn8g3rNhoQDi zhQP@*VplSHzMq~ea++UAIsP@d|2)-7aMPKu9?1Q1r~C{rb*l!3Mla&K35UpNY=z?Q zU|CT?2E~QyC6&Ma1MxF(h?2mHc)`L7c)?Tb!~YVw|5X4lR6`(3z+Hg8D&Xb$=RWnK z<3P;|r}q&Q?AyOYo6L=UjFpyT_kZe@nAf|{T%@4oo|s+thh{nmuWl$Fs(ZLsI6?)!+CHasIYFHU67%OX+D_c*BL0kUi;=E zSB9tV@r6rT7)$pKj@V__;Ztgb1sUcTP#E#o5GY)zm+|l&X<^y;$E@YfVGHlC)j&+9 zWZz$NLi`NguMIZ)3Tn8s*8(vOM|r^&!G67c5@{L{N*DsslTn7A+MM*6jwQSzFOk^H z=Xc>`QmlH_9t1N8aWYYqC3kL}O|~i}^vj*bWFk9ne9`m)fV8oA+b~p(7)--bq^ZRs z<_y_Ik)vRsw(gxu{XA0sqkp4iR_+emF zlm2Ie{~>ku02*VtkO38@pgj5`{NZ-|I0T{#!1^Dp9uk02;B8uhWCWYQVae_smoOIMp^LTk0MYBJhqYNJt+$@ z^es@CMd4)hh7CU}G~8CBjT%vVokq>NGHdPZ?8}|wSp`dm$IJT9(*2i`IG(Py%l^fC zBW<#VC7@{Yu^M1F2kRB1IX4*WN&s-oeoNlp zWXWf=%Tja$wKdI(uS0oV)fWF2w)iUY@+IGUc{)r=O9QzYB?WW{8yF^9mED4-8X(U5 zKO2)S!m&R}8(d~G7`5vjTMptaZJ6EL+0@WIqS4r2ZIn_!Z(9I8!oJ-7P|>UwTq$~l z&jrYs=dSZWUJ)uu_Uy2ugI}YZR_j zF;e1v2k%7XZ5LH#p|)6$zOgD#q<)*>lG}~4`L!@|H_aJ6s3Rw1!4fDsk1m*fcdII@ zDpqS*?|SR%+ss@h(FY8^_aYKN`P(wdxfI)h98Aa=vS@Owui=ZYZfz_;Lb?gGVig%! z4k80*U|lDk$Eb@?7^}x{Xe8w*Yzl;?H0~`H6@^ktUNabc!QlU^{@x?3W4-|>^t{~O zAk^S#fN_5i@SPUv&>cHq18TnyZ4O(>t@V&|rF%DTH!X9(RvU3LWr9W#GL{G5>A9|Z z1CErG$4r%2)(dav4!DvZ+ zfur^eC~^O=vt=}=3v||Gbqdvn8y@ofdmx&7qlOj;7c#R_BIpkg|wqiCuzZR*p~7*m?m;~0q7 zZ_$7_c`O=`a6m~lnC*||D+)e4b|zX=jh#xp9&L&cay$lJt9@4f3Q#);u>1q6^kCk) zpIXy^-vFi^U-A|3C%VVE5`-(@D8n2Ol7P&j3h1sU)I?$1Sc4n)F!{0uB^C`{e=I*( z51{cHkc1a8;CfAfYDYUG-A+_`zcm9_60islBo<6a=PekIF5hvV z(h9yd`g&9YBF1U@Q|uqpY6!gq?(iZKJkK07v>y-!puI9+cWp6($%Ua_>Dd1LcmGW- zd*3|zfd+M-J^#7$#}4s-b^|=^kDL>Rf=a1GwVu$;9B0&4VSc9iG@RG0=IL*fWlG}+ z1-%C|gK#HwQf&;nG#xKz8Mu~!EdEkzA>|$eS2ZC>9Syh*!Gr8n0@rgo(^fxK0Hx7e zF!|Ip*TFfs$lsq8iy~O+q0U4e6@tk+UUmO<#lk>tp%ys%-|a!CAOn2=H6gSFTh;(( z??0yTALip92I3!Xo&eM!P;L3Zs?WCbLLm0Xk_v7;3w~S&@~4_w1;bo^`1a(&CcsQU z-u`=H_dn3;=mVPwdju@VKaVl*gNep^Y=wPSyUss7CmR0Z>3=^#8K!Y_Nl!=S=32r( z%?pT^*gg9CXxQM#a2e}AOBXhSJx(0;W+5-_7*qT&69l}h7XSUF+I&&v|Bn_&;9_Au znqY~sjlS#uG{jmMh@+0wQdN}>m}}si{;#5!#AosH*vf(_{tsEeUUQ=7w>1ABaQ{EA zDE{r9{-^9m{Y%mPUDp3?(EcV1pl47la9K>-j^`tT{5-&oeT>0BU$^N>1FN=BlV?6t zL$Hb)&4aN(8A%^%tv6X*bBd(JI=Z`nVsF#fImlE;Dp(^JdAAFD@fFhtk&~QuGK=VOrb%V`CGdB|GbK`1QpwT5Z)fx@%ro z5H=Q9sLSyA9yLUx!VJssol6}84nv~LGRJZ={&3kSk~PTZ(F{Vx5Qhv7+60x{;}7N- zOB;T@>7N{o!vynxRUoK^KSRZPRuThVIH0`~U`Q;raOeW6U6qMFNTY-WzJ}VWML|-lI9jfI$1he7 zs(=;IH_aLOQ#FeHgouqwqo(zrkNCeVtsXy1>AqxX<9d;7PjWIT)h$dektgh%_!$UoEvFGveY;t@{45-^p_ z_IM=$xK~dG?Z?Idhp=`pRBk6!Kk(Kp#kNXa<62^wCYP&r=B5*)?6i0DdPa8WP4s#S z>1_RC@8kuLTX$k6QEJ!88N)xHeN<_cImSJPuNoRu;s$OQq0BN-tz2TOif4%k? zls7PjKj0f#_Sgo{*nlUx0@#y^>B=5So z{Cswcve2-^G?9<^gPKgc5-;7OHWqL5OXci8eVToaqC)nIO)B&DK-dz2_R7B`-Z`FK zz)Q4-Tr}i#HU4rivEA%l6}z*MFDD}@NW0>jki9(_9Q+7yFZtS@r*~sse6@~b8Spsh zKpv!D-&zZmL?;ZKJ$1CxGDv|LOo+mAB6xnU zHeiIQ$6Ntz20wl=FaWY*25;C#s0DB6*}Y|X0$0cy_nr)NG}%~3;tw|J0NyT@+{EY= zSskqimUB5< z8gv;+{+i(-+L4~WEqq&LZEfe7sHz2@opFm?Z>z_uxJNS!LAgPl)7wCDa{i0ufEGY2 z<(~6)e~6OS$0Pv`3IHyG+EiDDnNdH2?Ry7rd#inb+_!mrktS4Bdz^d#Kj(UM*K=8I ztHze!1j^SE`KCYy=+a|9@LXolEn?t2?I@JLK2)38{77$=PK>=_HCL0|^wWOGz{nzz zd)@=^%ax}eruQ|k_+&=fK53P1Qya8B>qr%i{dRRSJBci)X_nWOv1w`&>|>l-BM%Rf zXHjyNAT0L3V8NOGt4=0<<6Yqa005o+b>kjijSZy<+ zxAd{bM!rOjp85vxg62Rha~ZB4Rl^MI3jrN=P1>7DvxLB3M1y$+DVl|uG9V(TOkx|! z;$^&0HhX+S`qmmu7RN~!F znL{N#F;fk2$<{j>L>^V`4ayx3ZZt&kaf<>@$fYvqaL*03HJ9e9 zjf;ztpAvRVjxi0Y;dP~d1nc?&l{;FEFCr+FugaST{VJ>(oW>nP2kUyk(9!!nJ>bfh zP#)R43vn1rP5Hcz!C)_M(&#Zv^<*Mywg;T=`j4t&iF51I>_@*w+_)drJQDZ3p+Q_ObJq zlYTlzJmR+7`Mj*6bH0l3VyZwicQ0fX?)_pvQIw^O@4dX}@xzK2!+PTN#mn{K#DZb=nb z$GN&7wyIUwt9*n@D4*Qz@z!ltZkcLa@6l*s2eL^<>-oU;{BEyNP@a`2O?wb}1*mFw zM>&6livk~RXmw+MfeKUmj)n%YYYiroNRqo!5mO<3IchNG?2CH)JXDgCdXnn84|E9t z8#yBYm*n>>EHGP4;C0Owr-U7<^!g|ZO1qVKX$8g&BW)wey4+d{fAK7xaI6FZoj*PX z#8{6{Pfg8?TA+0c7}jOnPFhTAM@8b-})yB{x#X+4xKHuaYMYjv3!ihl8v=6}aJSx52_y7GY&3_g0vX7=evpp}5~}Bc zEQNk2-^%ITKP=9E=YSTu^TDCC`5T3+Dw3BzEc-CeDT(9Z%y*M}88`h}l3NaD9m7B9 z)6?l;yy$1nJ?Egm31_nFh6=!K7z6p73O>a-yXCNG>H~6jjqyvZ{FI{YPYnu}z}v){ zr89JoX5?ta60~~y@7`8mHK>zLB73Iwec}4BQneFj}h8+AF+-dGM8 zOTDzWffspAU+`TyhXV-FK?>+`OJk+htM_N*iqccUQh$2Pm_JsH3Utmcd%H+Rvp|_n zYkY?3Fv#Ya8U4U;&a=oG;Ne5>0?pFU^Hw^HLP04`gPyooQ;Bztm)JI4+Njh#HIsG` zwe>E7d0YXVdiTBBry^Rj`J=Bc#^bD8ok(R5U z-W#g1D+jYyNA}O_qp?Em+xc+^`Ax`kH9cox8dvUr-BZmmD`$Nq`%T^I&NCic201>< zM)6IPKRR@PN+H2}#8_R>Kq;!*O+ac^f^d)qOvY?J5 z-yI=%4kO4U zb-y4^phGnH%scE+^xvYVN^~$A5Fe!+mfM;i7u!+_*tQ+pxWu`(KY*{@VELJz$eF;n2Pq}nzM5uAxuln?1Amt}^D%XF- z_j#?LF?)(L@~rH=IFer~zZwo+XWZUweE99%i+okAaIds*6l3XH(B_a4S84)DBDoa+ zrA0<9dqWF?`lD{I*gi0i*@{#%0o(VkKCPpgn$PW5o~ai%!)=hroJ{657qIrbbUzo%WJ6bG-0 z9ND@ihp&8R^9=V8kR>mA!15ygWe&ZDzJIp)p4ZpKm#x6^X(TF5uXy$!+ZiikZQ{8)2a1_Iz9hd_Jem#UZsm zMDOO5;(5)+z!`Q{#z5ww+JGMYQL!inC1)|~&G*>q`zj<(?H9Qp_|;#dR*dKzdnPR% zNq^x!eG}Xi>lPN+eC8cMB34V~?d;kAUSi`4k@RutIA0xbjD6E}s)Wh&Egu%3p-DyV>+9H?GFI z7P~a>&h?`u3u3U_E%-x6iFA$01`^-SdzW`LB4c~~(Hyru(Kf*vAljmY@9QA#OCWm z8AsH^KS1eMQow=i_yIiLVl42Y1`pR$)G-xnb1HIsh=eMZ&=}&OBAb1X(MHb_<4o&A z$?4>be9jfYWIax8Xoby`0W<_Oe)iU`HV5P91B!b-KJ6tfj1qWba^i zODTZG^T_=GfYltkIVi?m@!nhdcM)0B+FbI)Dtt}^)P2>dFeQePo~7{Y4Eei^+yR2$ zZo1YzZMip)oR{0@*|{4?j(N%LB(bV8WKpJ|5?3Z9E`E89EJ|=*abAmKqr;Vtd1QIo zF6hpb=1pmd&WXxoOFzdqQ>?zsyE;chM8-5UyIKNn@1f*v>f>A>R7BfDwHb9$cG~ir zaL;x%C%!4m*ni#tLkw>KJYtY1Ss1(!%DX=-T&_Hz2m3s*8+A$oYW2RxdZq7^$}ifD zLkC*F@FA03bx9jS$hIZ1=AHP|XV)MP>_pgdM^|1z4zJu&)gYRV$53B|*$wKXLtU}C zF1ONfj0v|veAMA(dHhj$6_qSgjcAP8CHG{(eLGy_t~_86bA`E3Ny^9sKiPTPmALMN zRz!?xl2Fk6Emm~{;q=oI3T?0&BSNCX>C2o{&&48fgvi9;p)qj@$UV?3 zg)G@F2-RymvXDcCcV6f8L7|gyHsAA-OK9zWDP(>)!hn^tmMB9;CH1qs-z(JT>krG? z!~z)*d{GJ3VNw`6?2%ed-y=`>2tq+Gu_Wbz49uDpUuyZTc++q+DI%XGidC^7@Ec6# z2170*@sjSzQH;?7FFEh*K1jY{BIGxz6S%172+4DV91)VD=Fg~70esJ{LUN**l`~HHeVH`X0uHZ<_!M!rDjj;I*0VpqS6v*L{ME&QmA2C)3-}@T@n%biM z=}o}gSJ|>YFf!}ESME_h+I}PF{C2U>d~418>B;$CHGkj9{Ry#`@%pVoqn~ojn($gZ zmFKyBY2-f8-1xHFeNR?cGGYV-QwOf{53SGnNLfF)jQi1UfellLZ>6qgmb-8On1}ZC zC{tn|eOu`JUUHV_@u{YaeQZx&cy?Z%O2f=Z8=Y5GL96y&#Q;EiV6Qf)T+{R zU-v61{uZ>mcJFfU`s+qZViM#TjpvLGyjIPPZw}VF1}nVW>y4iI5|Rwi4@W>CXCQ~l zS+Qv^Vu>$_*DOhLgg|pV{{ZrI-rZ}fz)=@N;V*-}9lV`p$NOHM`JBb$k)y6L+HnYI z9Rt?=k+$arOG+QuZMEeNH?A6V?6>U1w5WF3te3=(D2E430`j5gV+{TF1!>mA!?SiA zJyY_Zgh=T_60=(33Ko(BP!V@}O$|p!wrc4Odz-$~m8tG(2ofj0*igvxDLU4|cvkEeTeOeZLhPP8~m(iATWN{S4_g^0%+B~}1 z&KydUketdqDa`UvmX_F}*l)htJ!4-WE8pfNsW|ZzGZ&jbBiw^Eq~&9#=Rsy~W=om| ziz9tNP&~mgi#bnI~`ZaQXZKq1jU0)J_&J$`yt@NZfrwD$c1lov!t30GC#b^@F55yY*e= zXg#qrfXiu&6ldoi?;+x=YClUz6f$VJf<3txS;bbhT6OY4f)bK{DoC0ODV7kdd4gJV z3o568Ju}T~sGtPeADJ=L+^`Nw=B*LO{}JM>5aAlRsnc(V2?=toPrBtipEIZmpU9sM z6G2|9qdwd9a^b_^!gt|Y7)?xnbR08sSHD-rq>sj~Z}@7cy~``Tuotlb6dj6fO|0{c zkw0T`uh7q@ZxIun4|{UHUMCKbKO?d`em~RADvn0h3QHWaywHbRQas>=3WV_evX!l+ zm2Df*$qdN#3s?KH5MH@Q ziL?g#xb)CHh{fp4I3v3G1H3eA5ZMTP`_O{^;HloojhMx8A8pxtj;x55DvI>6j$6%N zJOqEWEmbjAk&L^df+TDEIUgeH_~kSjrYfO*ZM%{oqV>EF$3<9dK`xm9AV?BUXxL`2 z<_~`AX2}Rl4MtUdjEe8gOU`1y$ko}QowVz*o|1=Kyop*D8=XuTjXW8^XS}O^v?W#9 zb4VkzyDnfI3Qum8=bQEI*d~9o*CF_;%XSUNHuuq!Z!MZ{oG_t9s&F;8ooc+ydZ zgVn^Li&gwW;_WR53}4U^`5NzlS_lfOBI{XCUtagPIW%xlFA822DnVEM_{HZUC(;=z;bO=D z<$OxNLQ$7NQkfPB@%{P@knDKkp=8%}B|DcZEw;~;Z<7KQkxGuC_PG)BdAo7OpWB<- z10v)D>U0%+S${FReBkn?uQs0_7Hr9hB+(oYdUYv5KoNKyJuuqPE-2#jO;{{UtW7Q+j%26j*=-uZ0jX^a6VCdwl4owYTw@V${PcX^o-K5 z-AhE(;$pqZvr>tEKRwcn7H`*2pLalby%nX?Z&p4^$QJwRIasxoS9&Y6(vNTNP!J}S zpl(5vDpTj+5f~%1N94?%cgJS?<^9ss{&TD}2tHvP>f-hHLHU~;L!Maka~wiGDJHlO zR^K39EvZ3@gCAzs!DoyUSKOd>56dy>=;3D~2;@(g4EPKrecMqEvQ3I!G}fA~P)D>X z#(5y}8yD$dx6%ZdXbbgc-1`fG1L9ycQuMM$1xidZe6oXoPvy31>n0iY^uSI#d%PyC zDv%=~&;{djN{=kksTmsGbjt@hg^U?)zrHF+&m9ndJ9Xh>i_b4Y-oW@B-c0{DiEki6 z9tBxtRtu?%k~KF{c~ee6hHV8@e}O7Few-RbN_KmFxVc7Vi4G(87aiEq8<|szRkh>p zT@FWspZb=Ozrs7Ac5{7YWmfV}ms5=$$G$)}Xnjh|+jW7rI;Vs=W#D-u@M#>I72~xE zXlHI8WSz>ljohy}QiMfx&9mn2mxVjC8dc-N*)L;Kiv>2)x|`uM#vyd)p1?en{Qd@D=_ zo4Xgu4rFSKzj;H1g3DP(TMoKfo=V}@W%h?m#n~VOvU6IPQ~RH<-1UHU86}C^Ly9{fTtd&=HB0SGRrW--)x6!BX7rL_^_T5j4z4ljqr7LK zUR@>6p*i|<2AEUAem}$Mi9YUhK0VUh{%`?NXwAxv9!FJPWT~ldyE!=EZue>h|&HI!VeC3BeEiMTW_1$=3!r=yD4V2fsd$HI$N*ON00oWc1ivdB>G3$ljak&N`~E0 z-4w-kC1nn5+bLBUCHG<7bZ6&sHIfbGc3P=^C&BsBSIf`dU}+P#p5db8K73IPsiX>J z3)2dG9wwjE{YXhFL8SfVfg*p=2WRZhz%j%6*Tv}a<$>4pP^Y>)Y`%3DM-;wmJZ*qHmI-&pC`_+|B5P`j(@g&L= ze7c(-ORIXCMO6DL`CQ}Ml=ki@^vkHLO98G@kBja1!~N{}>*I@?@JIW4neO{ z&j`}0X@7~{ATg+T;OaG~C6;@@RR*bL(5uw(Ox_!MY6Yl0eS(z#&XgeLb7D)n^^ufg zxvKx0UEi0jCQm39Dtjl!98DEL+{ASwvD%Y6twteBPi>ZJ=n>@zG%rGd;O-|w@5$f_ zEpI)oa=cH&eZMfPh>RoXu8sw9;#mtWCne5EX6quN8j9(Vj7<=;NMUf)tL8qW+GE?y z9X*YT;(b1TO3TivJ(%;3aiQX5J)h&Q`dD9eiPANU-{N?gApf`y52byLcwXb zKKwFR=ZwHm^+hN61ByVKo>zhjT|V`hDo=^#Z+WjmzRt)ThVH(Jh=8k(Im8y6IZfMD zm3H@ZeQfjl*3~_6QRwAsMapuTV>&i<82kol-i)Cq@Oh;ne z4=&U|dS@t5p)o50uVB3_?wL-Hu4;|B%SL+rz{%lv!|+5zT6UCXqCWOMhi$4l97=w)8lXqt+jG|9p=7p@jwe+S4qK{NXYyB&W5lfvJpl-5k-j z8|oG`C#^~ya|#kMDo5S6ts&+g)ND!uy-MRCe5tf>SqwxK3Cz7YB z=F!_v_}0g<3OHzn_K_GmqM$tc-4}(l7sS3H#W|47+@~{-GTArN^G%z`LPgveX@pG- zuDnZ%;+(nQoVm-N+bY=mf!HkNHs&^c6@vft(EQIcW8u=@Zo1R-`x)Fh;6FXU_)0@c zfyrUgN<9?pkla;sS3hmgsHYc!-~s!~joxpB!#&f!M43+6h54YeQ;}h7%Zycm395-K zT%~-T%1U?1;=FTyfNYB3TuLS)ka5CBpuO~CznrlSeXPK@`f_@67|-gCL|YR(lN+;C zb(GT*iS&4DwqFrhONTEbm|+q+E}lz`s^Mjl)2Xf(u)Xa5TK!10Na39p9GQhiXL&ASuT!dp=-0->2F8!;}a6TD@Rbo-HW?# zr{y{F?B*up=E1(P&`5PthHr_Rby&w~k$RoTL~dz5^-q`6s~FEdkgOp;ctDcF^RW0l zb1mWrT$R&=ntw*BEg+L+?LqDGa8v)Dnnpfmrw zfcxY@a_KXiT$@0fkXc%}XKErR^N$%H8Ite*L2dQVoTPe!EyD2`ygk=2?f6N*hPLJT z{mN=Go}Ugp*?89@jbZpW?x1Zqtr>i;=Kdr~ttUKTRxZ$<(*bOY#SVdd%^w1Jln>p% ze><&jb>3n*X8>VegV+}`R&ofh`ZhzKA%>_qpK&u}I%zt2I%Sb2?ta|z-s6(%^aU06 z-$N8MjJ{P1pRgqzwqwx`nH+@KKWxMIRP+&2C!7-=Z@hW~kK?CnAV2Nv@}9x&Av46* zR{qm~RTz7Mz>H+5Fcd1wNZS^4h)XGk8j^Qii8Z%>rk-ZhyS;};BJnh}aA&OMReCcH z0h90@l#KJ<4RYJ&P+NrUbK3;obK-^)cdu(tG0Yj?tw^*@vkgG;o;=&*zu`-h_`a`g zHh3z~x*;!bh-ee($nHo)Gt}s9{un+)(wM&y>R{{ZD!t9*OxokhoV=m6knLWqOWO5; zDA3sbrL42;uh?lBeCpN{HLX1==1B-dNV>`rL!6Fb_%J|Yd-wGOtV_qd-)%a`)01&U z*Q8xO2`7If;FC?%c4Ab3n2|a1!Dg#l$7;t!0-QL`EN*hX_$CW(w|6|UmBl?YfRUJ? zyuUD49lmYLt(GzQ=@7*-C3DrM(YlJ`d)*AftTBgT_e`57^Dt;ZFQH(jF#A;iY}|vd zvRA!ET&kC`KP?KG&{>~!xUVUO-_2~htslqT zxXW5E*YB1Q+&ewLEPPkKkMjNLSUCJ?zA);dI1TJcSkK$X-%5%U(!vK8Gb<%TOcZw0p*2 zVL3O>Gb88B4r6*~gX)Zn$-S+0K?91K zPD1YZv9vQPGqKABP1jMHym#vrx{iL|5veQrfLxO?>r;3l2zMu4b#Zr-g5i=UnUu{? z3k)1<=JBnJL&9z+n{;^`c900s2dnZNY_$6NbYFV$b1nVh4wB02(_%fFGja*S56;MM zN=w{!IvO0draMntQ(QjX6h->W2fwy09G}$_IZ(2Im-YWeH0O7shm;GEC5_^|vq5l=ON$hvPbBKTN7C^ORc$b;PD#$bM~dt4ML8mC^6xkQJ3S2qP2Q+_RT z>M$6q?J+s8j`2V?^8^}4Q;)1gMw#r@rTD4T^$Kc7xexYWi&=gHi{+;+obu5NC}tNe?m{us>d)<@p4t1Hcd zBe{DK+cXFb`w(k#Ne1}qr*d05I7P^fd&->tQveAL5~l_}kcZ`vzeB7`x0U1ej^GFROF0zwAu*yiw+`}4ZsKEWKD@knf1n=O ze0{gKK3a`2=SHCMUd3DsTy>N@vEXw@E=A?9^n>|Ew=;)zk7@h7=oPSzy3VI vrW=3#hPOW^Wa<9(N6zs7!&CBwXgt+k$$OF4y%w+&;7?6S8(OM(C*=PCgmFCt literal 0 HcmV?d00001 diff --git a/docs/DGCG-deployment-template.png b/docs/DGCG-deployment-template.png new file mode 100644 index 0000000000000000000000000000000000000000..a2adf6388c8f2d4d15d239dee1d78167bf4ca461 GIT binary patch literal 70832 zcmcHhcR1DmA3u(tV~^qxQ5iW&WF@j!q7D3QoWwm4;HhKy_( zk-cSo?@ztnpU?OD{eJ)c&UKxutK*#Kcs%a&cDvo5k8j_)c9iNQ6$*tqdR=QJ)C^P&z5!bw#0QYLGX&!SS6Le0ai5N!Lxo(ZbF1zKc1^)6-MH+QHV< z^uCk1fTN4$%LVC^DAZ}xb;Zk?UP*I9o?e>f`}@nAoPnGq=~URy!CarubyAXCz;bKR zeRmJ6Ghj5Rc*k3@pk_|`=e-eQ-1FDCY~^S}Y^Uw%*wfhHVGh#HzP9^bnq&(raB#yolT4vqT;~9%f*#Jo>NDH6l2Pu07q0z zA4UFO_J99xjQqcs`gA>aFmgQQeIA^0t^PSs?%8P_y=E^ zU~X95d+I2-e#fb14#ofbO(~n<-Z&kcXB z=&)s=Yl=iwP;=Y(`};Q^J9ccZ!gJBVlLw3KZEU7$8vh&<6SK8Yu~_Q6eR1#ay6H%j z-zV#?M;#rviHL~$ssrS%MY5SUJY{8(aH;jK{{7pk*kS0#K(W1bZ!W8`-wR3i+2HEx zBJQ0qM&92YX+f*9FU1@!S`#mc*!J)ZtlzC`XjuIfDX0Ekljrs8*A$eL=+U*Au9g(J zl-O7XpN+ZeMK(WBeZ}^te!SN*g?EyyU!wos-{{!b+Dh-$ce({wxTu=;cBX&>hNe5t z0Rabeu%rH4E0fJfpFMk~LPk!0j)aV?K3OhcKYVR%jYmFgNU1zbFgHJo(MGA3-#WxB z<=On=9UmV_Rw) zys9rnpO`nl_+I7r;Jx;TZv&3itzSc^tw*Y4N(No1uRUc+Gi!=L#qwx83L>Yy2rJKZ zD@l3s+jB~4>R;J;RVFR*LX}Hjsr$nQs{Ew<{;u7JwPBI+i1=`?;b#c$NBX-@{Ht=KSr|2^nJ2ER=vAIt@U0LyyhXT>=w>4+53@@X+6Wt z%ekyia!Wp*kAj0+XsD^Fx4TmIH81kPKG<6fF0$?Ag4ZzhuAOH= z?x-t2;(!akD;BHXzzwLT$g<+%jQkV8Q7i{C9&9drvikl|*6(jexw~z~&6ih$$c}Dp z{fb13yZy$T617Y9+u7*#%Y~hTOI2JRuR9@X$2>n!GTn48g;QL-udH=z`4_UHTW~!j zgr%{Xez^TY%k~VTYX3IpvzKhTM;8MQkWa``9Y?C9_tv_Sq`hOnc0M|dDV*2IRV^^7 z>-%V~GFEvP%hob7aU-XA&;}VA<{Nu;YhPeh6#f2|MM~&eUkH|YY zIVJn|GcCKg6B83>@*7reC8Au4VA=9vP2ohb@#t=W#Fen83P|)iwY0f;?$V_n-V*%$ z{A!!j6s69Yk0K(D2pE)4R3^p5e17SD<3^Zk<+UE4&aSRkacarZO%Q##4J*BhB{eiO zc(ikLlWiE6-4VO@94vLd47S?ZwK4y*=-1mgLn?uL#W}qEzNEI=W|i}pBDo5ef3HlZYnENShgjNHLwTp&6QlKJ>~wVui)3@=;$b6cQ8xw zF+FVi*b3Zw1yP~C=Dq0Ex_$-`sTcUtv#pPQxqjS}k@VjchbUJwGLnMBp?W^tYXGB+ zm-cokmO6XxTp{@ENihf25Guyp9TJFszaZg#av1t4p|!}!B%p6R-S$evrW+5RPJ116 zcWV~g4_tL}y5KqJ9J=~4SZO_8Q!OX1MzXbvjrh8@+TXczr;W=awC3^$y&|uv1j~2#N*n@{ZR;aWaB^|2 zgv#xXW!SVe6__@7ZS-3vTr|ItCRdEdXFgbM726&U7yLa`AqJ^}&#*H7-8=63NcNOp z8{fZw_x7Jt3O_#SGZ1rDWo3=I>_dyJ=fa?LFS?`5b=r=2Sjks8Fo2@Hv2m%~sKZY- zI6XZ*-)%;HV}9V8s%nI*?E15FY9Fn;GENOwUk)N`Ne~W&{rsMPzlP;yn_~|Ipu0sj zT2D*(?-fTx%KpVeVL`OCw>i@F`GF@KLnqZJgN8+%0FL4Cet3`*w!~-MCQcf;@KaJA ze9_U-zMDgy;w&_>dutkcMKr_ges z0s{ksLqdcc2E`$E$Nv&7C@9F$E24sROOf?^r~Tn1EZbQjq3D!={RD{ruRP{|Iydu| zptLpCAu}Nn5j@cKRvI%qdplgdgy#YaEYqNK18eUmf?Z#JAQ(eeiKEfC7yJ)u{?>f` zn)E@?pu%JFnVRfa$%y|OhoN#JISf0y+EBUs%#ZB2!t48e1*Uwu`8WbW9%_v% zus|w%T!?~5J~dsfq*$A&nXUcA=Er-w6DO>p0vJEp45bh8vzQp}JFgX! z7h~h&8^OVd11H4>`+}@Ia#Wvj-87l)&dM>W-cKBuYDu_~XZTs%XN?=y>XTi646>#6 z1ENR;u|1t~U0HclFkqYd!N%Md<|~?-42xAe0!Z~RN(l5_`+g1gLw%V*OuN)`&oMeN z@f)7*%+_pn9x9t>KQdtV43nUt+4t8<+sXEvGBU5A@Oc3yp#Is%{o=)ozq_-Jb!DoZ zgWb?E^bUbHDbC1$I|RH8IX?u*%P&Rk#UE_stQJHFMVt_RgqRSl4BfF~S0K+ydCVEZ z0*g2fCk2szkeBsZGG_A{Mbog$wzSI~sKPg3F!cVG4g8LEvH|9wjz4qk@5K0xeVk34yT_=?$_yr?m}VFLPAPzPdp6bbp2xe)GqwLM_JOL}wA)ne`U_o=F#1>vl$EI2llhq9~vmMIb?>{1>%C*Tb? z_3b(D%e#dZaMw+ZjTfL;x&(WD3D&dHdm{Q;(&lz=^}e}HZ|j#5&d zuhp&ecOTQMz36Qx|H`ji37B=iyVEB5 zYkWLuv%1r%Ip^9GbJ%|o1(=5rd~Qi6yKv5?M_x&~n?Qn9R8lH1Yoh-a*M5*FvOfFc%*Bf_?tgj;T&HhIdM>ECxLj0| zS=TJKX3I0A+=L=LNy?K32USSEdGm2myFLP~+%8I&cZKG}#b5|A7hNWlxm4o`Qj5E? zD*Ctbc(}Q_2gY@Es?Q;}BWq;{iyO&tx$)W`dzdu*)UG zsw1^0QbEaXPaK=xK*IOd(wK;KXK?+KQ-$t-w5xVkR4kctl7fSSo!VtK5zB>0TvHd$ zM0xb+WKpjnQv8)Wd_H& z2?=fO>~ukt{_}`4zdY8UzY4BbY>+MIM1_f|>Ghj8-$D%bE|0|Lby79SX37Ud){0pL zj6OReyB07bEGQCgC$%@I{hF~i>uwu{Z%RrcISKxFsZAFhlO2YNfik!vTH-XCq>DQTJB zV6NIq(*ii7Bt3{GMI#mSy83={=IH~+pX^(lDK?bf89pO^hE-4hhmk0zEAt$JA$a9z z^+ObdE*kq5srlihbb|wGZ+L~p9=05KJOj@^=!K3){U7G?|GxumVN8!e_bLQC8~o@I zJssT_D36j#8ft5oAlsW&v;&-g;0UNcy?L2Xu6Ac>ghJuI^!HDm>-LH{3S2@7+TEng ziiPrUv0<+uFeDb+$Km14#ePtUZT&Gi_y#E#<+V^Y-B;>t8fXNc{MCUL?TZVq+vXPS$*r}IkAb_GL-LPc7J3NrA`!|i=Z(3${(omO z?KZc^LfPR66cEFZXxVEN@Cs_^XHeIy?5?)E?ru5D?k-(vOOb1bAeSKPCy7Fuj5m?8 zCc~B9Ir?QWINV$)D^j}DJUZHvc!?SZwLkKch0mLYaEfzKPBrkC462XzmV2*S_-roR zgrX5D1Z%kF+ByZCBQ7D~pqSX@)`FoTvWKAyuO{DOc>u4P zPuxNJWh%z=qtnw3!tK&E05*IBe1%3HV3Gt$_w%p|m3tclD9D4b6=RhF2m5>09jWL- zi`H)imtN}@nDiGrcBWGT)beY0dljkT5X@lgD=9R>YE3a`6XXJ9A$CvYRxErSb3#S^ z17gIXJ^Jjk{u)-VKluzdH~uNB6p_=`(tXILWnRlzUY*=@z@VV{lPq}>9&S3*`8HO- zAo0OMStvBb3=+4lKVB7uf3uGl18C^l>Xd$*4!$Nw$7ZMeH0hRKNSne<3 zf>n(WJNrLB5JUbo@~bX0u2GSqplQ!N~_vdJ{8lKEEOU)x1`L+50P zN`!lc4TM@~_%uKNlWXjLu}#m;5ih~Q!XjSw^_3p+-{FT7p5fr&P;(Dw=(wGFbZNS! z*%bGJaHUvFQ?Z*K%x?V5_^)4ChM#?^779)3sH(QcsW;~OuRtFN{XzTYsy3i?P{&Vs z4L}q}nmZKaGE>OZyVVDq2i#D`7SGeLNh|B?vmi)G9g|TyILF*z*)s@S-yusOM?@j1 ziZ`!P0SXt_4@gPuShY^_K>I zWN9>b`+;edL9^QrP#@Il<4|5hT}Fk2`YGRYaai9LZPowrZ6U7?H1hS#kRNN?UP(do zL#2^b((T zTHI+A1x@EC`+*lrOHNS4$wP5z{o_3&^seTBCv5xgLVbiFU`QVhh>O*5rG#MR5>Y{c z;lQb@bFS;+QG;IVTbQE53#ACH`2s1sY;*WQmR`~Mqo+j8022X61t?R{Y+wT-1+p^q z^y8Ha36{x+Z7g^_Qnjp3w-YbHWl7zmg|=#LZkBUP42vu zXlZF7avGLVfFhBq0nV*ocvC@v=$u+|y7mXX-a!ut00=k(SQR9S&lY=;UyGp^V|y?s z1idk0t$@F#LC@T8s0Mxs;Xe%;vEGQgO_Ay8oF*nF+!`52pqCSjSfO4!$k+?ip(5)+ z9c%i0O8eF$BEd29UsrsWuVh1_KCA~CiZSEXxxrDew0#CQ0=a{gGn#N|#-`E^Q&kSv zO%Xbz_JdZAZZuS_0|t*8TO0o%w>q?iF;Z=_p+w3BBU!(vdJ4Y5@HUjmab_JVD|2QI zSr>P>d#$N_Hyz$|7ydH-Vy!u|{f-Jg^0o?I&9zRKCXbo@ z^@~G-Ci<&q;V*fYUyN|ELK4|$5fa0HdriWIa+wa^*K^sb$CiIxISg`;UA?GWS(F{5 z{eAI*T$YIC-0Kufk-f&xA*%rIOzs9duetwB3MvsAohQUiPa2-mBIM2TX#gZo!>evV zQc;ofnpWUtdG)EupM*C8zEx(U{;992Q)ptPGnig*Gof<5M#bH+cL@gqm5lBEF-OqmNWtr5r%t&_(u;a8u@# zRA4-jT5H^b0j^7W__Q(EY}j*Nq{I??4II!oBN<9~Uf}C1i8%oR8PHzpbk1^Mj!{v- z0gXg2R$rqDCEh!_Dowf_(EP`bg@$}DmZpVl_VtS>{p|^pkOU)L*xm8z>9}Ml zj!AR!D~>xG{XNB{lcQVk5QoFXe)=R*p|WgVLY#-bkCR&x?y;FMa@|AGqbgIp_tgY) z)N1Wj$<%Ra9!zjSzan;TEnf2w^N|B9H;1rJk8=r^ue=xiwaU;E9=5UXdH(&pI%t%o9wXVzW9KBC$Q7Mz$ zA_|)4zXsO5as^%cA&LAJaf->!Ch8vC8l}9n_GXSb;f?NnQ-P(^+7#B5vBW=7X@n2D z9FwBP(wKy8FF|zkwoL49RM``J!@EF6X+6|VAVrRaJn0=ex(5t*8ra%XH_ucg4b!@R zaZzc_xA-C%$tW#g%&$)2#Ge0RiS@FFR~m42|-O?xA~BzBs0i?#!k?QV9qn)bF4d2H+8 zhR>8~NbEp<{c;;wxv_g!Vq{!P>08YBDI4Rrf3E8o$g$a8B|msApe)^E>%j!%o-Kp? zpthjDT=5O!S>i>a?0kh<^Hkjz^Q^iJ`dMOLgZ2b|0wxDjc!U?{Mmoi)K3}Xsv3yjJ zn4Q=%x7?r7>4>eCT6Cq?j|Czqn;sM4@9mI!k=Szak@t8-DG^w8FPrdpFi|uO?#mhr zYR7%}BSat?mB^7VB(+`o`%1UL!8jzzC1LuMX;CHaQgOP<7o!;Ev8<;*=4Zc=7ai}3 zkK!#i_2cM1iS-_1yh*@1e0>y6wx9wQgsaDSJ-VY?_;i7YKHsA5WXj}5fGMjdKj9>8 zC&%hv$e6y581;Ud8!phA8HZ5e4$#zvb}3<(Zjl(DU%EcE?Vqq-DwD_DhwWYG>0vU_ zpPJMosHb|ou-7*6^U7qjw;m5X^3xH|@S?@^Ci(+ez$K_;05Q;$m%c)=M5;~Q;Hp2c zn$Fd;^^`g1#0}R65u$_*mIc9a=l7;JS#>y?-E~TWae7xc7u|wSt9wPfdq@di8lI$g z`Sd2{bq}VAnw1le&$c?iCeO;`6v-%(*#0cFbSafB!4NG;^te@F%f4TJh%XJ>5FCHJ zn@^S$`qKa?^$J4@x-z=|^V4_K`ArL0>F6Ga+3@0t4Hlc~zz7lQ+Z|0~@*zoJJN;j;7ZVV4TZb=NI{R_C z>GdyJ6~X^5Zt@VXQ!`g5S;yg=QkyNDzz92`EUbNAgWC67?8AId+}I@aybH z_v73&v1oN+K^?;1!B=G8(EGZ)t)QFm3X>&V9St4|@uG&r(ljgGBOb}0Fuh2^)>!#L zbE>*I-=FRzr3hJ7&1|9;c|1`%%8{5Hrz5O3LwKfJXs?B4^VsG^Oida#+U1#zF)K^h zL@m1&Wo&CDOIVRZqb(t&Ku;twW{*6gQ~p8Dby`J5svE>zbq3D~HvQE<78EKUgAs6M zyBWK_Xm*ycE8KhEJRMjhkgW_iZ+J&HV7mKxDF*Id*>D%7|FHr=Ax<4pJeG$a6mf5e zd6~9Aj~ze%vl=f&^<_3ps|^nLB;z;Zp*h|)GQQMvV@4hSADZEkk*kXhf&HJbcn3B- zp(QvfS2oxq*1S!lE4*TTfL+L7(ALT6^k5o)@~`Yrnm_oM6o9sU#X;xkN}Eq*dt8~v z>=4@+KmQhYKvaRVmKPe8Nj;*?+jJq7f9d|z#JEzPHM_6r(W{m>dzt1JB zi;$%uC%^jtI(bemUlC3|=gVgUVMnQC^9pZy+nha>i#_A-{0Tw3RSw?`b3!zU3%;sa z_P!AK%1H7H-G*ArRyH)ij6dm-#t8I60Li_jr>L4l&a>i>2ZiPI)=-*lWKEN?Rc)RouUmMTvW>iVXYZB8OM)&^(*f70Iqa+yE`6}gq)%ke%Bh(0yHQ04H z+~Y@_=YApZAs5pKo3Mf{>50k?$kK!jU^reB ztbFI^5w~J+U8C0+toQo)rERTV1HY&x?&{E$V^80HgagL5Dd@x0UQmI;%pfdIOTF3j+VJZ zrD|j7c6S0qP)6^5`+3p? zIbJE)PCEA->MI1$S|*-g<;(gfPMLJ*f}rYX2*$aHOt(Rv2KkkeR^SylC)@N5Tm0n@ zP-mT_;uyCHARa!vM@W67>*t*)iyq?fwxA`;t|PLaha>7BM|@7UVY$zXRqta^sC}Mj zvUgNNhfjY2#baEzlwRhRGg+FOMgE?;H6qM$G( zpkJC_Dn#~b#s6DlV=ADVW@cu6hT$bCPIE}Ujwn)(gSA7 zZ^U+Ml+C$f+^z#SEqzHkn5sn9>qDb`X z^|rcEnFU+~Wrbhu=}WuEwh)`%l5|8=XyY5XG>5Xg_8qAx{JM;C^RAfCs!#M+dUBCM z#B8RYAkwu#!&~TDQ1>l+zYPr69SP#iB>8kanjyJ|z*Z3+Woxa9_mX^qrYnhaKZ2Ld z#P%nVbKQ5ZCQ@#z^zA7Qs~tE(RE2ENa_$RE5e`4l{DE|W3c_hj(Iv$Xr`6+G2zm0R zu)TDPl!w>!pY*Rugbga*C#2C|yJ`B&J?XF}%a1V7e>n&W9$rnf>f1nKtE+rG`UgaY z$ki5j`BE&eu2x(O3cDp)wr&j+j@S|2gn=-I3;{G69p@ahh>J|Bm66DQXn*GApD2AK zZ!7zogMRQ{g9CP`Qf8NxsYe!4WxQ6z0AsM~u`V z@Zu#(8ih?ojo*<(oeLa-2Chs42};ZQ$k)-ezo1$ozkL{K+tX3%BnPuZOWJNg$C zCjK5~p>b~p`7j(2{@Uyj7SfG=1H68Uo8cq+ZW^$Td~e%<~j z{Cc18pg|b$mH;R>KmS7gQ&tt=iuo_e06Ay(^HY;|8Ng%!(0!AboI{BEosolEU`^D! zEC8sD1I}altNuwW-`yBIo&rEu1mkuooDBDp`gbD*hk?u?iwfiDNhdawNIC*aBL}XV ziDt(I4egLOkjqkCsh5dY2VL74oj(70E$qEw*7aJ6+H(E?PNoVtR_pKYZ|t+PEZdW( zW-?Pede-)L7GFY3U1-}Y0CYBJfGjo_hDiN)OH0Kys(igyfl+t~TzovhF)x8kXa;s` zGh^1FE8|H&=uB?PT)p2k^+{1X3IYgTC}?THaAZKy6E@ZQ(sk|k9r&;wloS7i33U)r5#9n} zn}NzfRs{f5h?-Qo5Moj%xZ4^UQBoj?RF0I+jWQc@zqh+QCAQ8}27v@7J}o|x)({8>LWs&x}aE}6{ zP5Q~hmUL>*qNB$gBQBroO-7Mo+S8$mc4)D!Ff9Th96&qq5DO5OM!eW{f7cs)6pUW9 zE90iCgPct__iCq#U;;PyB1DY)aru8}FX%6U@z2CkJTy+CY!AnrZnzk$s z6=>%f&ifj{l>&Xo#mCnIr-eY)DtEB0Hf?{f)gX6x6+mv8;{NnpF#pETRpx3rJqha0 zL~s^338-U55%JJ!F`JB!xL~%_i!BsWfJ+^-Ux17z%8BJ2k~qX>pPn`Rr55n_7@UT9 z6wV;MJ@E|DRf8rY-@1zn;#3W+1S$&Qd6LrP#`RS36#mC?t|y& zwU{MbI8poi`-t)lV$X8obOkstNL$WIOS7N=&gsoH0A0X+U^@^U6L7?cv;=H`RClOb zT|)!;8^oNoxqe|agXgk4bHe*ep>imvziZZPKEF8(eV6O(f_~>&f*rBQ`u1(Ju}OPH z!5Y!CutY$u1bjtrHywBeL}55o(*~~@@TEYE;0o{c0>kM&U(~A$(HZCm4-bQ5baW{{ zShG*{I6{{7hVcQzH=s6eBX-1r0wdq%$N5J`PAJIBqaXwr7ZdMXH2*g0B+e}9#tr-_ zmV+bk8k;vs{zG{%!kMnjNY@=EL$5F1vVS7(fkcDl%+(0vv9BSjuOy$V$?e60sRM~b zxI!zV2pn0`fZ>dZdl3L;`t#pbK|dA&E&!sf<<&wTjf{*8_1B7&)3yR2MF+ag0wfqD zBqYeW#hp&W%}#+h#RLcdds;Oj2K77=Mc2l=ivAwv0>#KhT3ei@me?f3wY!QjKge-$*a{Q~Lx^!ID*t($+K z?#O$StIKxPRH4@PIb?Vs!-+6#Y;C~DAVQnimfL; z3<|RDOn>a^tEjF{55_M8(j3qyJ^lXXnlLDQXL~+0zjUmcsT@hY90aP|ix6(XJc&Tk z{RQ->;sN`+M^iRow|zmx+J^qE!vO0G7cRMTTTnNj;C&Co#jpnMRr^4Z3PEB+Ap#vk zzjZB8hP)b3gFpzo0kaH>I~()c!1pDkS%6*^VaI`8Ms!NRE+gU^3M#5^8_c-E<4~QQ zsKY(L0oc2KWpY4^LB>JybV^ zPbwfI#vr0k>+W~a$iG=y*)Kr$8!1$O@>qQ_YOpaHyLS9L&uMxn*cRnSEbF`GW2 z&AL~T3+5gU`j#iq@m&DYv$tCoDWD+W>hG|~ZH)#^fttt75Xk5v5Y>bg=JJ~AAezT% zS9Ur7!9kGGTfo(|?fm#~uNR3N{f1{oS=wWM{4iCx@%+3w84b&CmIh~YRb_Sc7@*$L zfJ%NgGgMJ>;*w3Qt7;>}*_%*Nyjt`Gw`_${IH?^`(t;4*1WE%_y-!y6k%~_{SKm6K zpuSvst>a1tXumRa@(eS8OtS!56+$oQ%Z%ZF=0FEpNP5g!K|Hkv0sBo8lh=YJ|<^xpb{zFQ%n;>)mN*}5^_gm4_PM|>n8V@%1HE0F4b0x$e z)q|%6!CcLg#7nlrP7h3vz$St^{uxX?FzB%we};-zOg&A36iT*~`C`cE|LKb$*MbDe zvSJT(R|t^_3j32HR`L)s3x}!#4*YK=iC>2GdS%dt0zS4OLD%nWE5k#f-) zDh6b{23D5m?nkqb_^$_* z0>L0E8?IL$9D#j-T9+77xC;&arUw*>ZKB>dRyWKUff!qg6EZ#`h~xlF+uJ|ibJ#l+ zv}~@u2A%~V_&4+#BTWw2_xAN;rH4#&!+DgVLuA!x8FMmTfX%TMPv;Rg+5;#f*yyhDaP06LA&?kR888SMCc)wu3j6bBLn`fraTYRKHq|F5FC|eY!0kta z5lC2$H0L4G@`w}9>=kIEu^7r)Xnvfb5B~)RZC#)rgzWIc?e^*MNu+24s{H0-`m^z} ze&xc!AYuOq_5XRzcZcQ5V3{z~J7>7K9>IaXgLu}VZ1TAO_9PRcqky^?B7^joFJJ72 zD-)2JABc<~HiV=_l(X15#ro8CUKsR6xxU2?I0E z*FOR)gG&L^86-^$v)LxQNi=lHn}6#1po$q(Q@eTp$I@GGei zs%8E_-JniOXznMVqk|$#x(_tUFet$Q3Rq@SDCF)#lK{$~qc|8Q0wsmvbh$KC1aAO} zfauk^%?#fIM~H_M3+ig>o;;&=9V0&_@O|_MdMIAIb_`M|BtC<3xBIX$ zNVf3ZU2Xu?)a%>l#xP}p!-V5G&pYdC4oC95@?kmDp(jCfRs=n~J5*$C725orGHVEzBw*GX$_5sL{nmH6NdH*9y>H z!M5v)3tr_6c!)D39LxzQQe8d7-PmlP_lI<}T2j5&T1ErkmjqqE1od&gRA@6ueFh;} zBX)%hdx6;Cab!tPsXa`%A!!|oy|*C5Mgo9f)z(9hMWVzri$LXc2E;c=a{=NhSXVWu zbCx$ZEg_+S)PQ#&@dlO@6H+vRBjfPqJ_K}WFS*ZZf;%MHbKHj3@jqY(2c9hXUIQWMs%R z90~%a-C!xQ6dD<-2`}G&cYq%HGdrejGlrWE2jZRC-??4F10V%yd5LYT0w;z zwJG$YJoAP21%>n_?qIJJbwWU(0YM~{d5KNi=-UBm)7)^A<2Gab5oy|5qGlXfLSz?f zAH^<}FAWJpKQD4!+e4oTCa1sr?*O?3UU}D4vJSKti#WM7Neh{JU<1y}m;ys0QcuO% zb&-8~grJ;roBqh)U9&uZr4%PT;kH5;6e`;<=JbR&6C6%+(r*5La~8IS4sf#Q*P3W6 za=!cu439n)$MCu8o6lzmZ~5W`jooL;Pi>(~+55=dcp}E(xQZ)dhQ_i{R-PQ{r2zh- zvWtjQMYVYw?;9V#aEzHu6}9m3tt1?B**qx!>9yKs?%=Jbs8Pp+Y2p0(Cz@Ypi`R*? zjhYz=h4L4jsJ1BMi6W*p3lJY@EbK&^&uZ)BW1bT@ z6B>Bz?t%ubt@ejkr8>gzgzEbYf*Rc=SZG#OB;3VWQvC`hUTp*@$&VP7ET22g;?;pH zpY>0z5oheG+r6pD^}7x#q{S0XDW7u%Zos{8G6mrt1E!ptwxGKbix9WTcu z1%=v63<_pp|9xnF`avBDPE65;28R|)ihZ}uWT_=X=dUwV1(Qvu z;DOQM!KiYRZ4{ViG!~JZxLo;xyZtG0%?F_$03?L#RwF}?-2ZT&>+nZFqaYF*I}Ggj zG2}Azm67kXzb7ujF^cwbD9OW-Yn2YaWKMD6lyYGO##!cHHFdP_;U)5t%!~ER6}Ff% zfq#rV5uQPG{vZrjGj>{Vv9M+g#j3)X@q{2ua6xW_I==!H{jhOxIlPo#POf?l&V3*0Eb3PN8zuLK!r|gN?gqi8f;t)|C=i=* zA*MPlaET)RF)SFvp+86exs7s`aIagr{Ew_Dga4NOmA`BJ1Ptr@KRI<%Adzx3^TQuh zuX#xQA`TtBQ43Jjmgmc-$4y1FvKO;R>}vER3?_U;UKQeZzK6{y5yqS_^z^S$<33LU zx8;xKR7;Fs`2+zKk0*B)UmL(>Bh%>TxVWyOdFOKLnvW7$q@u9n$Ihc05Mub@J~w=m z7dQ#{-&~pyGMbB3?@mNtCR~LzO_w$=_@5Tw!GDABcTSP`KG7Re@_htEkl_q%5KtxG z3{yh@!1wz{M`D$-&h`lXJ`M-i`QMMqFY>;?Z()F=tpqL&3^2q`UISR12U%xpH?7c! zXBMcOI(@Xy!IQ&8@LvSvIj>_;M^DGDK_~9uNvEFvp7@kY89zNmi}VP?yXZ+#gtUwP zI)U;hU4RHtfD|Xb15)x?EvvLduj~<$zw+LLQTqHY@ELzt^(qb&uah153lt+Rvp`SQ z0zHHa1b-J_T5Q>|ud`9QBMuSg)x;;^CQqLmAHqN)`-aTqT|40MrG76 z*(1y3kqBtunK5K6=o|Y18w*m>-_8ddY(0p=lyGu%X+7&&MO5flBA!zfk6kNIGU11*l8o~D=R3(NN6nd zx=&tANSID|P=7tXJ!w^nWpa zp{gVvXc?S$W}Yp1ZOVStiTvEsnlQYwybbvrx&KE{Y|h;=EjQC4vusji!qC*BzVG(O z`!fbS^*~}7z694`ABv%?&B%TN-V|G26J1(iwQ5~jEiBYigieVb(lAbeq z_CMj+S3aam@eJfQ;a8F0#BJTflOi)9!8eA45fb(d20Sp!Y9|jxE77leNViV(*-PQK z&N2Sy3?hswaQ+$H7^B3eXR%nO*Cb1)Zr8wXjQ;nV?xS0D$_1>7yK!NMshj7IJ0{< zLNR$?VtZ}z6_TU{wbR`^OoPpi4>M45C z9wsHCaZc3veI68)M`kJMqUT_n>k|A)oyOPNLvzl;pXI;P+TZ5JmVvn?scn46JOLV0 z*KdqFpr=0!~ltenVGu zgz17NQcf86z%De3kfv69MeVGy@E1A#pRmd%cPuO{uEQm+Tn-3V;*2sll8#z;7Cp}( zkw-CrRt&pb0TN7a(9QGo!{B?RPe_l~fdo4B3qt&&#Kyx{qPMWEf~_ zb~C|uAFHFW`?IR(i%{?mfOaNKA>h;b7x<)W7dC@Gg@=Vn7Q<-pnejXXA9$~sWztr8 z%lUXXhK^Fx-08T;_Zwl@m`r1S1x*}&WK{qj`$31rq8B}kpUz{j;|C85DDI%K{(_lA zwaj2A^h>hjWCJuD*nPpiFbw1A@l6-r|9GL*L$i+K!vsXq8iyB3is@pF4}p5U&Zm@;_DCucs^$>U`59g_g9S^%g|2`>oLdm9XwR1OQs)!T!#+Uf4R!8%_Z_ zTo;3gpUBS>w>l?wDp8&N>Vb;>`t92{6HxR6+s)9t-PPHt``PCb3K)mlNtb0nsaSEy zV=(~phpQ=tR(Mq!5EtJ9a|WmZ1NRBi;MQTdFbt&+sP+$MMFfqK0jjzK(_E-Q2Qg%* z5}EUQcPHqSV`X%>Ksg06SToh0q6Y0dfC-X+e1Wlh19)0;3Ov#TCXbgGH~{@cAP5-h zMi>p?*t~CL0KHLQ{DlY@7kQZDEZg{Li-yNbP(#Iq00{t-!oPhWOS=LqI>4;32K!c9_nDM;kX<6C*N+lpa8@<xe^&{k@HUi$faSG%MXYtWEL5Q-at!V3gbGiGBBpI z1CS3Tj3CLNQhZk^0lYAQXR!bo`xsy+CqT~&pKikt9}ExifRKM2dh8=7Merwtla5-B z_^#drIx1eml?%|2H_$c0JSG{g8hKOzV0&{T)hWnBVPNnYeZjhu6TYf^^X4Q_P;USf zH8VFyMnsW`8$kUUp^FROc+5x_ecP3?gGAR(3W8il7y&)NLDggy_u_>G%5{{0{`CR) z0}G&Jl2ZUmutktmU@iavzW~ohftlt)n0C_0*5-!~566sQj#s*27l>U^K!-%@nGxFe zMAA`q86(jB3;mli=2AUOfefAk19?Fsdzn*1S6U0(0*E&^@Jq3VfJ*R$St}727nd4M3ggi_Dv4Q0} zCgP4tZ&w5qgYjjUg5^eLIch>_r-2%u21Nqkh>SQD!OyV(WizZKfV-u!U#Njt!3Mr> zRi-a+;{{(6KsVBHpmDejj055rs;Wl-0y@Gf84FKx@NqZ>6QTg8Oi#}>JP417n1Ck_ znFFE(W {Ct^Q1ZXV0Hc zZuif7Ae23X0`7aoe1LAO`uYL1bc975g9E;N@>c@rznU~|rV8bEdteSP^<5E6v;ZcD zzJ`Z0~A{qM{r0Q?0sv)m$mHFRD%_e!1ugNJ&-cUJ-24jBOj z%f>CMS94^VwYwsD!g6R_+sr5p?wGL>*yQBgr#&R8^R zC({K>n*O7J(+VCpG?SCk5Xs(-OcleRLMsfkT!vw6pjagkc2`D*)$|BVwxq&SN&ts* zooJ+khi;QhzGpBo@FUqY&i8? z@f@Hy1bNTN;R709*}{Sgqy@~TfRWz~{Av#nE+Fl>Rex=6!4q=e67Wx+$ovtl-rG0@ zKKB;Ddt^2lv>ymp`z=l|Wx5QWo`Z}@m5qn-zPp|FaC+s;wWr7+AV73Kh{wpGz z))uBq30d1o{a@A|cBn}Rdu|Sg|F8NTzo}7>uKQmGpF4@x!sXeMN00tj1$;b(QY9aR z-!J|5`~G!ui=Q+cUvB=ZxRabEZqw*OF-;=&?~Grvq%QEp{rPLQvoD#tJG1CmW34P! z7q6mt_g>>Yz>B`}8;YIiWng3=J>ieMNE>&Paq6X|;~k{$ zj=B-~le`#HZ72GDB!|?4=opS7MTUs-#(8)w09qd@iSc+fHMk|pryqlVFs#u(1N#o798P;hepyID%ZW~Zxyoq;L1chU~0LJRp8 z1Jn*SuOt~pzb811Iw#RZ@8B5gnC>bB9+v#kqwahuCAK87Bo}ed`UT`33cvkx)mY#H z#ny@6x83N@GW@rMx#Fi7u8u}{pt@^LkX}byjEUsXk{J^D+->B({NEz{evGP5-ruK9 z(kgf{t~%gDWSKDRHM&Tk97QcLUN}*jBKJT^Q2>83_KKRfvDED^9#IMx>aDh2mK_D+ z!gnig83-`_b}Ybe88W6yE)jPe&-`sg!AbnX>13A%Zm<8Tb#UQ(ZThIG=6rhJPnFO< z;FW*(x2KTSw-OZh=SZ7SRqbQ(AL`gu6hfdl^3Ifde7s5QN$q3hs4~Xi5B}~%iv}tG zhK+eRta)QU%Zf4jbQf78iRi0+?~iqS^a|aFWh8z1UvwAbt&TTHe?}MGG4ZuOH9vFy zG`$aWZOEP`{v^5<$PTvD@#fG_IYriM?k4KgPK&UrAO+=GDefL_0ZhxPQyJMt<=E`yS zAB0&FB}4i5gZ>|?z66}gcK!Y}&%-t=<4%JiLn>pDkkF`5DWXuHLL^gcB0`~3lqq9T z8JbYB$&d(*k}@SjB}9dk|9U##_q(ot*SXH=WPA7fzRz^uYpr|T#6l%S$1`0IS^}JM zrx9OjL6sAnXt>dF3X#~;P{bm9t1AhgiSUg>pIEdE+-hAtF*^(UkW1JOQ*f&9_B!*R z|L1qRgwBrNM@15aCs%5|C7H)k6%+Nbr&(I{e`63;yRX-qzNwfH_7qTm!0Y`mU08{@ z{F≈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 *US2~l$}1~u@Ol$1bW6U8Nr_ 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
ZomzFMq99$d;F)Fbz2D8 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 F{vXgGM(_Xt literal 0 HcmV?d00001 diff --git a/docs/trust.png b/docs/trust.png new file mode 100644 index 0000000000000000000000000000000000000000..a4c424022fd678084157a69dd2005e1d48006e54 GIT binary patch literal 120883 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 literal 0 HcmV?d00001 diff --git a/owasp/suppressions.xml b/owasp/suppressions.xml new file mode 100644 index 00000000..8f2dc9f9 --- /dev/null +++ b/owasp/suppressions.xml @@ -0,0 +1,22 @@ + + + + see https://github.com/jeremylong/DependencyCheck/issues/1827> + CVE-2018-1258 + + + see https://github.com/jeremylong/DependencyCheck/issues/2952 + CVE-2011-2732 + CVE-2011-2731 + CVE-2012-5055 + + + see https://tomcat.apache.org/security-9.html#Apache_Tomcat_9.x_vulnerabilities vulnerability is fixed in tomcat 9.0.38 + CVE-2020-13943 + + + see https://nvd.nist.gov/vuln/detail/CVE-2020-10693 vulnerability is fixed in hibernate validator 6.0.20/ 6.1.5 - we are using 6.2.0.FINAL + CVE-2020-10693 + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..a0f930d2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,566 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.2 + + + + eu.europa.ec.dgc + dgc-gateway + latest + ${packaging.format} + + dgc-gateway + European Digital Green Certificate Gateway Service project. + + T-Systems International GmbH + + + + + dgc-github + https://maven.pkg.github.com/${github.organization}/* + + + jitpack.io + https://jitpack.io + + + + + war + + 11 + 11 + 11 + + UTF-8 + UTF-8 + + 6.1.6 + 2.5.3 + 5.3.9 + 5.5.1 + 1.18.20 + 4.4.2 + 1.5.10 + 5.7.2 + 1.4.2.Final + 3.11.2 + 1.69 + 3.1.0 + 1.13.0 + 4.25.0 + 2020.0.3 + + 3.3.0 + 3.1.2 + 3.9.0.2155 + 0.8.7 + 1.7.0 + 1.7.2 + 3.0.0-M5 + + EU Digital Green Certificate Gateway Service / dgc-gateway + 2021 + apache_v2 + + eu-digital-green-certificates + dgc-gateway + + eu-digital-green-certificates + ${sonar.organization}_${project.artifactId} + https://sonarcloud.io + + + https://github.com/eu-digital-green-certificates/dgc-gateway + + https://github.com/eu-digital-green-certificates/dgc-gateway/actions?query=workflow%3Aci + + + https://github.com/eu-digital-green-certificates/dgc-gateway/issues + + + https://github.com/eu-digital-green-certificates/dgc-gateway + + + + + docker + + docker + jar + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${project.build.directory}/docker + dgcg + + + + maven-assembly-plugin + + + make-zip-ACC + none + + + make-zip-test + none + + + make-zip-PRD + none + + + + + + + + + + + dgc-github + https://maven.pkg.github.com/${github.organization}/${github.project} + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + + + + + eu.europa.ec.dgc + dgc-lib + 1.1.3 + + + com.vdurmont + semver4j + ${semver4j.version} + + + com.github.everit-org.json-schema + org.everit.json.schema + ${json-schema.version} + + + mysql + mysql-connector-java + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + io.github.openfeign + feign-httpclient + + + org.springframework.boot + spring-boot-starter-test + test + + + org.liquibase + liquibase-core + + + org.projectlombok + lombok + provided + + + org.springdoc + springdoc-openapi-ui + ${springdoc.version} + + + com.h2database + h2 + runtime + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.bouncycastle + bcpkix-jdk15on + ${bcpkix.version} + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.springframework.security + spring-security-web + ${spring.security.version} + + + net.javacrumbs.shedlock + shedlock-provider-jdbc-template + ${shedlock.version} + + + net.javacrumbs.shedlock + shedlock-spring + ${shedlock.version} + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${plugin.checkstyle.version} + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${plugin.sonar.version} + + + org.jacoco + jacoco-maven-plugin + ${plugin.jacoco.version} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + org.codehaus.mojo + license-maven-plugin + 2.0.0 + + + org.apache.maven.plugins + maven-war-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-assembly-plugin + ${plugin.maven-assembly.version} + + + + + + org.owasp + dependency-check-maven + ${owasp.version} + + ./owasp/suppressions.xml + true + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + dev + 5000 + 30 + + + + pre-integration-test + + start + + + + post-integration-test + + stop + + + + + repackage + build-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + codestyle/checkstyle.xml + target/**/* + UTF-8 + true + true + warning + true + false + + + + validate + validate + + check + + + + + + org.jacoco + jacoco-maven-plugin + + + **/DgcGatewayApplication.java + **/restapi/dto/* + **/restapi/mapper/* + **/repository/* + **/model/* + **/entity/* + **/config/* + **/entity/* + + + + + + prepare-agent + + + + report + + report + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + org.codehaus.mojo + license-maven-plugin + + **/*.java + ${project.organization.name} and all other contributors + ---license-start + ---license-end + --- + false + true + true + . + THIRD-PARTY.md + templates/third-party.ftl + dgc-lib + + + + org.apache.maven.plugins + maven-war-plugin + + ${project.basedir}/src/WEB-INF/web.xml + + + + org.apache.maven.plugins + maven-resources-plugin + + ${project.build.sourceEncoding} + + + + net.ju-n.maven.plugins + checksum-maven-plugin + 1.4 + + + generate-artifact-checksum + install + + files + + + + generate-artifact-checksum-deploy + package + + files + + + + + + + ${project.build.directory} + + dgc* + + + + + SHA-1 + + + + + maven-assembly-plugin + + + make-zip-package + install + + single + + + DGCG001_ACC-${project.version} + false + + src/assembly/assembly.xml + + false + + + + make-zip-ACC + package + + single + + + DGCG001_ACC-${project.version} + false + + src/assembly/assembly.xml + + false + + + + make-zip-PRD + package + + single + + + DGCG001_PRD-${project.version} + false + + src/assembly/assembly.xml + + false + + + + make-zip-test + package + + single + + + DGCG001_TST-${project.version} + false + + src/assembly/assembly.xml + + false + + + + + + org.springdoc + springdoc-openapi-maven-plugin + 1.3 + + http://localhost:8090/api/docs + + + + integration-test + + generate + + + + + + + + diff --git a/settings.xml b/settings.xml new file mode 100644 index 00000000..9552ce4b --- /dev/null +++ b/settings.xml @@ -0,0 +1,12 @@ + + + false + + + dgc-github + ${app.packages.username} + ${app.packages.password} + + + diff --git a/src/WEB-INF/web.xml b/src/WEB-INF/web.xml new file mode 100644 index 00000000..d80081d1 --- /dev/null +++ b/src/WEB-INF/web.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assembly/assembly.xml b/src/assembly/assembly.xml new file mode 100644 index 00000000..847d80d0 --- /dev/null +++ b/src/assembly/assembly.xml @@ -0,0 +1,46 @@ + + zip + + zip + + + + src/main/resources/db/changelog + classpathFILE + + *.* + + + + src/main/resources/db/changelog + classpathPOST + + *.* + + + + src/main/resources/db/changelog + classpathPRE + + *.* + + + + + + ${project.basedir}/target/${artifactId}-${version}.${packaging} + webapps + dgcg.war + + + ${project.basedir}/target/${artifactId}-${version}.${packaging}.sha1 + webapps + dgcg.war.sha1 + + + false + + diff --git a/src/main/java/eu/europa/ec/dgc/gateway/DgcGatewayApplication.java b/src/main/java/eu/europa/ec/dgc/gateway/DgcGatewayApplication.java new file mode 100644 index 00000000..d36e783c --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/DgcGatewayApplication.java @@ -0,0 +1,46 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * The Application class. + */ +@SpringBootApplication +@EnableFeignClients +@EnableConfigurationProperties(DgcConfigProperties.class) +public class DgcGatewayApplication extends SpringBootServletInitializer { + + /** + * The main Method. + * + * @param args the args for the main method + */ + public static void main(String[] args) { + SpringApplication.run(DgcGatewayApplication.class, args); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/client/JrcClient.java b/src/main/java/eu/europa/ec/dgc/gateway/client/JrcClient.java new file mode 100644 index 00000000..8e72f104 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/client/JrcClient.java @@ -0,0 +1,42 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.client; + +import eu.europa.ec.dgc.gateway.model.JrcRatValuesetResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; + +@FeignClient( + name = "jrcClient", + url = "${dgc.jrc.url}", + configuration = JrcClientConfig.class) +public interface JrcClient { + + /** + * This method gets a the RAT values from JRC API. + * + * @return List of RAT values. + */ + @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE + ) + JrcRatValuesetResponse downloadRatValues(); +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/client/JrcClientConfig.java b/src/main/java/eu/europa/ec/dgc/gateway/client/JrcClientConfig.java new file mode 100644 index 00000000..c65815cc --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/client/JrcClientConfig.java @@ -0,0 +1,94 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.client; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import feign.Client; +import feign.httpclient.ApacheHttpClient; +import java.security.NoSuchAlgorithmException; +import javax.net.ssl.SSLContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class JrcClientConfig { + + private final DgcConfigProperties config; + + /** + * Configure the client depending on the ssl properties. + * + * @return an Apache Http Client with or without SSL features + */ + @Bean + public Client jrcClient() throws NoSuchAlgorithmException { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + + httpClientBuilder.setSSLContext(SSLContext.getDefault()); + httpClientBuilder.setSSLHostnameVerifier(new DefaultHostnameVerifier()); + + if (config.getJrc().getProxy().getHost() != null + && config.getJrc().getProxy().getPort() != -1 + && !config.getJrc().getProxy().getHost().isEmpty()) { + log.info("Using Proxy for JRC Connection"); + // Set proxy + httpClientBuilder.setProxy(new HttpHost( + config.getJrc().getProxy().getHost(), + config.getJrc().getProxy().getPort() + )); + + // Set proxy authentication + if (config.getJrc().getProxy().getUsername() != null + && config.getJrc().getProxy().getPassword() != null + && !config.getJrc().getProxy().getUsername().isEmpty() + && !config.getJrc().getProxy().getPassword().isEmpty()) { + + log.info("Using Proxy with Authentication for JRC Connection"); + + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials( + new AuthScope( + config.getJrc().getProxy().getHost(), + config.getJrc().getProxy().getPort()), + new UsernamePasswordCredentials( + config.getJrc().getProxy().getUsername(), + config.getJrc().getProxy().getPassword())); + + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + } else { + log.info("Using no proxy for JRC Connection"); + } + + return new ApacheHttpClient(httpClientBuilder.build()); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/BouncyCastleConfig.java b/src/main/java/eu/europa/ec/dgc/gateway/config/BouncyCastleConfig.java new file mode 100644 index 00000000..c927668b --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/BouncyCastleConfig.java @@ -0,0 +1,36 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import java.security.Security; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class BouncyCastleConfig { + + /** + * Adds BouncyCastle as Provider for JavaSecurity. + */ + public BouncyCastleConfig() { + Security.addProvider(new BouncyCastleProvider()); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java b/src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java new file mode 100644 index 00000000..2239c14e --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java @@ -0,0 +1,80 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Getter +@Setter +@ConfigurationProperties("dgc") +public class DgcConfigProperties { + + private final CertAuth certAuth = new CertAuth(); + private final TrustAnchor trustAnchor = new TrustAnchor(); + + private String validationRuleSchema; + + private JrcConfig jrc = new JrcConfig(); + + @Getter + @Setter + public static class JrcConfig { + private String url; + private Integer interval = 21_600_000; + private ProxyConfig proxy = new ProxyConfig(); + } + + @Getter + @Setter + public static class ProxyConfig { + + private String host; + private int port = -1; + private String username; + private String password; + } + + @Getter + @Setter + public static class TrustAnchor { + private String keyStorePath; + private String keyStorePass; + private String certificateAlias; + } + + @Getter + @Setter + public static class CertAuth { + + private final HeaderFields headerFields = new HeaderFields(); + private List certWhitelist; + + @Getter + @Setter + public static class HeaderFields { + private String thumbprint; + private String distinguishedName; + } + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/DgcKeyStore.java b/src/main/java/eu/europa/ec/dgc/gateway/config/DgcKeyStore.java new file mode 100644 index 00000000..bb9e3bae --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/DgcKeyStore.java @@ -0,0 +1,96 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class DgcKeyStore { + + private final DgcConfigProperties dgcConfigProperties; + + /** + * Creates a KeyStore instance with keys for DGC TrustAnchor. + * + * @return KeyStore Instance + * @throws KeyStoreException if no implementation for the specified type found + * @throws IOException if there is an I/O or format problem with the keystore data + * @throws CertificateException if any of the certificates in the keystore could not be loaded + * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found + */ + @Bean + public KeyStore trustAnchorKeyStore() throws KeyStoreException, IOException, + CertificateException, NoSuchAlgorithmException { + KeyStore keyStore = KeyStore.getInstance("JKS"); + + loadKeyStore( + keyStore, + dgcConfigProperties.getTrustAnchor().getKeyStorePath(), + dgcConfigProperties.getTrustAnchor().getKeyStorePass().toCharArray()); + + return keyStore; + } + + private void loadKeyStore(KeyStore keyStore, String path, char[] password) + throws CertificateException, NoSuchAlgorithmException, IOException { + + InputStream fileStream; + + if (path.startsWith("classpath:")) { + String resourcePath = path.substring(10); + fileStream = getClass().getClassLoader().getResourceAsStream(resourcePath); + } else { + File file = new File(path); + fileStream = file.exists() ? getStream(path) : null; + } + + if (fileStream != null && fileStream.available() > 0) { + keyStore.load(fileStream, password); + fileStream.close(); + } else { + keyStore.load(null); + log.info("Could not find Keystore {}", path); + } + + } + + private InputStream getStream(String path) { + try { + return new FileInputStream(path); + } catch (IOException e) { + log.info("Could not find Keystore {}", path); + } + return null; + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/ErrorHandler.java b/src/main/java/eu/europa/ec/dgc/gateway/config/ErrorHandler.java new file mode 100644 index 00000000..b89f1189 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/ErrorHandler.java @@ -0,0 +1,80 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import eu.europa.ec.dgc.gateway.exception.DgcgResponseException; +import eu.europa.ec.dgc.gateway.restapi.dto.ProblemReportDto; +import javax.validation.ConstraintViolationException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +@ControllerAdvice +@Configuration +@RequiredArgsConstructor +@Slf4j +public class ErrorHandler extends ResponseEntityExceptionHandler { + + /** + * Handles {@link ConstraintViolationException} when a validation failed. + * + * @param e the thrown {@link ConstraintViolationException} + * @return A ResponseEntity with a ErrorMessage inside. + */ + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleException(ConstraintViolationException e) { + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON) + .body(new ProblemReportDto("0x001", "Validation Error", "", e.getMessage())); + } + + /** + * Global Exception Handler to wrap exceptions into a readable JSON Object. + * + * @param e the thrown exception + * @return ResponseEntity with readable data. + */ + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + + if (e instanceof DgcgResponseException) { + DgcgResponseException de = (DgcgResponseException) e; + return ResponseEntity + .status(((ResponseStatusException) e).getStatus()) + .contentType(MediaType.APPLICATION_JSON) + .body(new ProblemReportDto(de.getCode(), de.getProblem(), de.getSentValues(), de.getDetails())); + } else { + log.error("Uncaught exception", e); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .contentType(MediaType.APPLICATION_JSON) + .body(new ProblemReportDto("0x008", "Internal Server Error", "", e.getMessage())); + } + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/MdcCleanupInterceptor.java b/src/main/java/eu/europa/ec/dgc/gateway/config/MdcCleanupInterceptor.java new file mode 100644 index 00000000..2b5989cd --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/MdcCleanupInterceptor.java @@ -0,0 +1,37 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.web.servlet.HandlerInterceptor; + +public class MdcCleanupInterceptor implements HandlerInterceptor { + + @Override + public void afterCompletion( + HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + + // Clean Up MDC after each Request. + DgcMdc.clear(); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/OpenApiConfig.java b/src/main/java/eu/europa/ec/dgc/gateway/config/OpenApiConfig.java new file mode 100644 index 00000000..279a5a08 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/OpenApiConfig.java @@ -0,0 +1,106 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import eu.europa.ec.dgc.gateway.restapi.dto.ValidationRuleDto; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.converter.ResolvedSchema; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.security.SecurityScheme; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.info.BuildProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +@RequiredArgsConstructor +public class OpenApiConfig { + + private final Optional buildProperties; + + private final DgcConfigProperties configProperties; + + private final Environment environment; + + public static final String SECURITY_SCHEMA_HASH = "Authentication Certificate Hash"; + public static final String SECURITY_SCHEMA_DISTINGUISH_NAME = "Authentication Certificate Distinguish Name"; + + @Bean + OpenAPI openApiInfo() { + String version; + + if (buildProperties.isPresent()) { + version = buildProperties.get().getVersion(); + } else { + version = "Development Build"; + } + + Components components = new Components(); + + // Add authorization if "local" Profile is enabled. + List activeProfiles = Arrays.asList(environment.getActiveProfiles()); + if (activeProfiles.contains("local")) { + components = new Components() + .addSecuritySchemes(SECURITY_SCHEMA_HASH, new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name(configProperties.getCertAuth().getHeaderFields().getThumbprint()) + .description("SHA256 Hash of Authentication Certificate (HEX encoded, " + + "e.g. e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)")) + .addSecuritySchemes(SECURITY_SCHEMA_DISTINGUISH_NAME, new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name(configProperties.getCertAuth().getHeaderFields().getDistinguishedName()) + .description(SECURITY_SCHEMA_DISTINGUISH_NAME + + "Should contain at least country property. (e.g. C=EU)")); + } + + ResolvedSchema validationRuleSchema = ModelConverters.getInstance().resolveAsResolvedSchema( + new AnnotatedType(ValidationRuleDto.class).resolveAsRef(false)); + + ArraySchema validationRuleArraySchema = new ArraySchema(); + validationRuleArraySchema.setItems(validationRuleSchema.schema); + + components.addSchemas(validationRuleSchema.schema.getName(), validationRuleSchema.schema); + components.addSchemas("ValidationRuleDownloadResponse", + new ObjectSchema().additionalProperties(validationRuleArraySchema)); + + return new OpenAPI() + .info(new Info() + .version(version) + .title("Digital Green Certificate Gateway") + .description("The API defines how to exchange verification information for digital green certificates.") + .license(new License() + .name("Apache 2.0") + .url("http://www.apache.org/licenses/LICENSE-2.0"))) + .components(components); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/SchedulerConfig.java b/src/main/java/eu/europa/ec/dgc/gateway/config/SchedulerConfig.java new file mode 100644 index 00000000..4d9a27ce --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/SchedulerConfig.java @@ -0,0 +1,33 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@Profile("!test") +@EnableScheduling +@EnableSchedulerLock(defaultLockAtMostFor = "PT30S") +public class SchedulerConfig { +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/ShedLockConfig.java b/src/main/java/eu/europa/ec/dgc/gateway/config/ShedLockConfig.java new file mode 100644 index 00000000..a5487595 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/ShedLockConfig.java @@ -0,0 +1,53 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import static net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider.Configuration.builder; + +import javax.sql.DataSource; +import lombok.RequiredArgsConstructor; +import net.javacrumbs.shedlock.core.LockProvider; +import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; + +@Configuration +@RequiredArgsConstructor +public class ShedLockConfig { + + private final DataSource dataSource; + + /** + * Creates a LockProvider for ShedLock. + * + * @return LockProvider + */ + @Bean + public LockProvider lockProvider() { + return new JdbcTemplateLockProvider(builder() + .withTableName("shedlock") + .withJdbcTemplate(new JdbcTemplate(dataSource)) + .usingDbTime() + .build() + ); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/ValidationRuleSchemaProvider.java b/src/main/java/eu/europa/ec/dgc/gateway/config/ValidationRuleSchemaProvider.java new file mode 100644 index 00000000..44d05382 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/ValidationRuleSchemaProvider.java @@ -0,0 +1,62 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import javax.annotation.PostConstruct; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.everit.json.schema.Schema; +import org.everit.json.schema.loader.SchemaLoader; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.util.ResourceUtils; + +@Service +@Configuration +@RequiredArgsConstructor +public class ValidationRuleSchemaProvider { + + private final DgcConfigProperties configProperties; + + @Getter + private Schema validationRuleSchema; + + @PostConstruct + void setup() throws FileNotFoundException, IOException { + InputStream schemaInputStream = ResourceUtils.getURL(configProperties.getValidationRuleSchema()).openStream(); + + try { + validationRuleSchema = SchemaLoader.builder() + .schemaJson(new JSONObject(new JSONTokener(schemaInputStream))) + .draftV7Support() + .build().load().build(); + } finally { + schemaInputStream.close(); + } + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/WebMvcConfig.java b/src/main/java/eu/europa/ec/dgc/gateway/config/WebMvcConfig.java new file mode 100644 index 00000000..df8be263 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/WebMvcConfig.java @@ -0,0 +1,34 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new MdcCleanupInterceptor()); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/AuditEventEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/AuditEventEntity.java new file mode 100644 index 00000000..2f81f63d --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/AuditEventEntity.java @@ -0,0 +1,85 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.entity; + +import java.time.ZonedDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "audit_event") +@AllArgsConstructor +@NoArgsConstructor +public class AuditEventEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + /** + * Timestamp of the Record. + */ + @Column(name = "timestamp", nullable = false) + private ZonedDateTime createdAt = ZonedDateTime.now(); + + /** + * ISO 3166 Alpha-2 Country Code. + * (plus code "EU" for administrative European Union entries). + */ + @Column(name = "country", nullable = false, length = 2) + private String country; + + /** + * uploader_sha256_fingerprint SHA256-fingerprint of the certificate. + */ + @Column(name = "uploader_sha256_fingerprint", nullable = false, length = 64) + private String uploaderSha256Fingerprint; + + /** + * uploader_sha256_fingerprint SHA256-fingerprint of the certificate. + */ + @Column(name = "authentication_sha256_fingerprint", nullable = false, length = 64) + private String authenticationSha256Fingerprint; + + /** + * ID of the event that was recorded. + */ + @Column(name = "event", nullable = false, length = 64) + private String event; + + /** + * Description of the recorded event. + */ + @Column(name = "description", nullable = false, length = 64) + private String description; + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/SignerInformationEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/SignerInformationEntity.java new file mode 100644 index 00000000..521fe813 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/SignerInformationEntity.java @@ -0,0 +1,97 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.entity; + +import java.time.ZonedDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "signer_information") +@AllArgsConstructor +@NoArgsConstructor +public class SignerInformationEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + /** + * Timestamp of the Record. + */ + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt = ZonedDateTime.now(); + + /** + * ISO 3166 Alpha-2 Country Code + * (plus code "EU" for administrative European Union entries). + */ + @Column(name = "country", nullable = false, length = 2) + private String country; + + /** + * SHA-256 Thumbprint of the certificate (hex encoded). + */ + @Column(name = "thumbprint", nullable = false, length = 64, unique = true) + private String thumbprint; + + /** + * Base64 encoded certificate raw data. + */ + @Column(name = "raw_data", nullable = false, length = 4096) + String rawData; + + /** + * Signature of the TrustAnchor. + */ + @Column(name = "signature", nullable = false, length = 6000) + String signature; + + /** + * Type of the certificate (currently only DSC). + */ + @Column(name = "certificate_type", nullable = false) + @Enumerated(EnumType.STRING) + CertificateType certificateType; + + public enum CertificateType { + + /** + * Certificate which the member state is using to sign documents (NBDSC). + */ + DSC + + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/TrustedPartyEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/TrustedPartyEntity.java new file mode 100644 index 00000000..9f0efd7d --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/TrustedPartyEntity.java @@ -0,0 +1,100 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.entity; + +import java.time.ZonedDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "trusted_party") +public class TrustedPartyEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + /** + * Timestamp of the Record. + */ + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt = ZonedDateTime.now(); + + /** + * ISO 3166 Alpha-2 Country Code + * (plus code "EU" for administrative European Union entries). + */ + @Column(name = "country", nullable = false, length = 2) + private String country; + + /** + * SHA-256 Thumbprint of the certificate (hex encoded). + */ + @Column(name = "thumbprint", nullable = false, length = 64, unique = true) + private String thumbprint; + + /** + * Base64 encoded certificate raw data. + */ + @Column(name = "raw_data", nullable = false, length = 4096) + String rawData; + + /** + * Signature of the TrustAnchor. + */ + @Column(name = "signature", nullable = false, length = 6000) + String signature; + + /** + * Type of the certificate (Authentication, Upload, CSCA). + */ + @Column(name = "certificate_type", nullable = false) + @Enumerated(EnumType.STRING) + CertificateType certificateType; + + public enum CertificateType { + /** + * Certificate which the member state is using to authenticate at DGC Gateway (NBTLS). + */ + AUTHENTICATION, + + /** + * Certificate which the member state is using to sign the uploaded information (NBUS). + */ + UPLOAD, + + /** + * Country Signing Certificate Authority certificate (NBCSCA). + */ + CSCA + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/ValidationRuleEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/ValidationRuleEntity.java new file mode 100644 index 00000000..60c5b682 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/ValidationRuleEntity.java @@ -0,0 +1,108 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.entity; + +import java.time.ZonedDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "validation_rule", uniqueConstraints = {@UniqueConstraint(columnNames = {"rule_id", "version"})}) +public class ValidationRuleEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + /** + * Timestamp of the Record. + */ + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt = ZonedDateTime.now(); + + /** + * Identifier of the Rule. + * Needs to be a non ID column because Rule ID is not unique. + */ + @Column(name = "rule_id", nullable = false, length = 100) + private String ruleId; + + /** + * CMS containing the whole JSON validation rule. + */ + @Column(name = "signature", nullable = false, length = 10000) + private String cms; + + /** + * Date from when a rule is valid. + */ + @Column(name = "validFrom", nullable = false) + private ZonedDateTime validFrom; + + /** + * Date until a rule is valid. + */ + @Column(name = "validTo", nullable = false) + private ZonedDateTime validTo; + + /** + * Version of the rule. + */ + @Column(name = "version", nullable = false, length = 30) + private String version; + + /** + * 2-Digit Country Code of origin of the rule. + */ + @Column(name = "country", nullable = false, length = 2) + private String country; + + /** + * Type of the certificate (Authentication, Upload, CSCA). + */ + @Column(name = "type", nullable = false) + @Enumerated(EnumType.STRING) + ValidationRuleType validationRuleType; + + public enum ValidationRuleType { + /** + * Rule is used to validate a certificate. + */ + ACCEPTANCE, + + /** + * Rule is used to invalidate a certificate. + */ + INVALIDATION + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java new file mode 100644 index 00000000..45a680b2 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java @@ -0,0 +1,52 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "valueset") +@AllArgsConstructor +@NoArgsConstructor +public class ValuesetEntity { + + @Id + @Column(name = "id", length = 100) + String id; + + /** + * Signature of the TrustAnchor. + */ + @Column(name = "json", nullable = false, length = 1024000) + @Lob + String json; + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/exception/DgcgResponseException.java b/src/main/java/eu/europa/ec/dgc/gateway/exception/DgcgResponseException.java new file mode 100644 index 00000000..926abcca --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/exception/DgcgResponseException.java @@ -0,0 +1,51 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +@Getter +public class DgcgResponseException extends ResponseStatusException { + + private final String code; + private final String details; + private final String sentValues; + private final String problem; + + /** + * All Args constructor for DgcgResponseException. + * + * @param status the HTTP Status. + * @param code the error code. + * @param details the details of the problem. + * @param sentValues the values sent to cause the error. + * @param problem short problem description. + */ + public DgcgResponseException(HttpStatus status, String code, String problem, String sentValues, String details) { + super(status); + this.code = code; + this.details = details; + this.sentValues = sentValues; + this.problem = problem; + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValueset.java b/src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValueset.java new file mode 100644 index 00000000..f57aafa9 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValueset.java @@ -0,0 +1,83 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; +import java.util.List; +import lombok.Data; + +@Data +public class JrcRatValueset { + + @JsonProperty("id_device") + String idDevice; + + @JsonProperty("commercial_name") + String commercialName; + + @JsonProperty("manufacturer") + Manufacturer manufacturer; + + @JsonProperty("hsc_common_list") + Boolean hscCommonList; + + @JsonProperty("hsc_mutual_recognition") + Boolean hscMutualRecognition; + + @JsonProperty("last_updated") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss z") + ZonedDateTime lastUpdated; + + @JsonProperty("hsc_list_history") + List hscListHistory; + + @Data + public static class HscListHistory { + + @JsonProperty("list_date") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss z") + ZonedDateTime listDate; + + @JsonProperty("in_common_list") + Boolean inCommonList; + + @JsonProperty("in_mutual_recognition") + Boolean inMutualRecognition; + } + + @Data + public static class Manufacturer { + + @JsonProperty("id_manufacturer") + String id; + + @JsonProperty("name") + String name; + + @JsonProperty("country") + String country; + + @JsonProperty("website") + String website; + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValuesetResponse.java b/src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValuesetResponse.java new file mode 100644 index 00000000..d6789c33 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/model/JrcRatValuesetResponse.java @@ -0,0 +1,38 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; +import java.util.List; +import lombok.Data; + +@Data +public class JrcRatValuesetResponse { + + @JsonProperty("extracted_on") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss z") + ZonedDateTime extractedOn; + + @JsonProperty("deviceList") + List deviceList; +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/ParsedValidationRule.java b/src/main/java/eu/europa/ec/dgc/gateway/model/ParsedValidationRule.java new file mode 100644 index 00000000..1cb0ee10 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/model/ParsedValidationRule.java @@ -0,0 +1,56 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; +import lombok.Data; + +/** + * This class only represents by DGCG required properties of the Validation Rule JSON. + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ParsedValidationRule { + + @JsonProperty("Identifier") + String identifier; + + @JsonProperty("Type") + String type; + + @JsonProperty("Country") + String country; + + @JsonProperty("Version") + String version; + + @JsonProperty("CertificateType") + String certificateType; + + @JsonProperty("ValidFrom") + ZonedDateTime validFrom; + + @JsonProperty("ValidTo") + ZonedDateTime validTo; + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/RatValueset.java b/src/main/java/eu/europa/ec/dgc/gateway/model/RatValueset.java new file mode 100644 index 00000000..44191663 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/model/RatValueset.java @@ -0,0 +1,50 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; +import lombok.Data; + +@Data +public class RatValueset { + + @JsonProperty("display") + String display; + + @JsonProperty("lang") + String lang = "en"; + + @JsonProperty("active") + Boolean active; + + @JsonProperty("system") + String system = "https://covid-19-diagnostics.jrc.ec.europa.eu/devices"; + + @JsonProperty("version") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss z", locale = "en-en") + ZonedDateTime version; + + @JsonProperty("validUntil") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss z", locale = "en-en") + ZonedDateTime validUntil; +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/TrustList.java b/src/main/java/eu/europa/ec/dgc/gateway/model/TrustList.java new file mode 100644 index 00000000..fb7574f5 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/model/TrustList.java @@ -0,0 +1,44 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.model; + +import java.time.ZonedDateTime; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class TrustList { + + private String kid; + + private ZonedDateTime timestamp; + + private String country; + + private TrustListType certificateType; + + private String thumbprint; + + private String signature; + + private String rawData; +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/TrustListType.java b/src/main/java/eu/europa/ec/dgc/gateway/model/TrustListType.java new file mode 100644 index 00000000..8ad1f7ff --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/model/TrustListType.java @@ -0,0 +1,29 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.model; + +public enum TrustListType { + + DSC, + UPLOAD, + CSCA, + AUTHENTICATION +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/Valueset.java b/src/main/java/eu/europa/ec/dgc/gateway/model/Valueset.java new file mode 100644 index 00000000..900fa5c2 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/model/Valueset.java @@ -0,0 +1,41 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.LocalDate; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Valueset { + + @JsonProperty("valueSetId") + String id; + + @JsonProperty("valueSetDate") + LocalDate date; + + @JsonProperty("valueSetValues") + Map value; +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/repository/AuditEventRepository.java b/src/main/java/eu/europa/ec/dgc/gateway/repository/AuditEventRepository.java new file mode 100644 index 00000000..410175bb --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/repository/AuditEventRepository.java @@ -0,0 +1,27 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.repository; + +import eu.europa.ec.dgc.gateway.entity.AuditEventEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AuditEventRepository extends JpaRepository { +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java b/src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java new file mode 100644 index 00000000..e4c06131 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java @@ -0,0 +1,43 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.repository; + +import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity; +import java.util.List; +import java.util.Optional; +import javax.transaction.Transactional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SignerInformationRepository extends JpaRepository { + + Optional getFirstByThumbprint(String thumbprint); + + Optional getFirstByThumbprintStartsWith(String thumbprintStart); + + @Transactional + void deleteByThumbprint(String thumbprint); + + List getByCertificateType(SignerInformationEntity.CertificateType type); + + List getByCertificateTypeAndCountry( + SignerInformationEntity.CertificateType type, String countryCode); + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java b/src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java new file mode 100644 index 00000000..903892c2 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java @@ -0,0 +1,44 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.repository; + +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface TrustedPartyRepository extends JpaRepository { + + List getByCountryAndCertificateType(String country, TrustedPartyEntity.CertificateType type); + + List getByCertificateType(TrustedPartyEntity.CertificateType type); + + Optional getFirstByThumbprintAndCountryAndCertificateType( + String thumbprint, String country, TrustedPartyEntity.CertificateType type); + + Optional getFirstByThumbprintAndCertificateType( + String thumbprint, TrustedPartyEntity.CertificateType type); + + @Query("SELECT DISTINCT t.country FROM TrustedPartyEntity t") + List getCountryCodeList(); + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/repository/ValidationRuleRepository.java b/src/main/java/eu/europa/ec/dgc/gateway/repository/ValidationRuleRepository.java new file mode 100644 index 00000000..7dc84f3d --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/repository/ValidationRuleRepository.java @@ -0,0 +1,60 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.repository; + +import eu.europa.ec.dgc.gateway.entity.ValidationRuleEntity; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Optional; +import javax.transaction.Transactional; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +@Transactional +public interface ValidationRuleRepository extends JpaRepository { + + Optional getFirstByRuleIdOrderByIdDesc(String ruleId); + + @Query("SELECT v.id FROM ValidationRuleEntity v WHERE " + + "v.validFrom <= :threshold AND v.ruleId = :ruleId ORDER BY v.id DESC") + List getIdByValidFromIsBeforeAndRuleIdIs( + @Param("threshold") ZonedDateTime threshold, @Param("ruleId") String ruleId, Pageable pageable); + + List getByRuleIdAndValidFromIsGreaterThanEqualOrderByIdDesc( + String ruleId, ZonedDateTime threshold); + + @Query("SELECT max(v.id) FROM ValidationRuleEntity v WHERE v.country = :country GROUP BY v.ruleId") + List getLatestIds(@Param("country") String countryCode); + + List getByIdIsGreaterThanEqualAndRuleIdIsOrderByIdDesc(Long minimumId, String ruleId); + + @Modifying + @Query("DELETE FROM ValidationRuleEntity v WHERE v.ruleId = :ruleId") + int deleteByRuleId(@Param("ruleId") String ruleId); + + Optional getByRuleIdAndVersion(String ruleId, String version); + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/repository/ValuesetRepository.java b/src/main/java/eu/europa/ec/dgc/gateway/repository/ValuesetRepository.java new file mode 100644 index 00000000..c5af55a5 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/repository/ValuesetRepository.java @@ -0,0 +1,35 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.repository; + +import eu.europa.ec.dgc.gateway.entity.ValuesetEntity; +import java.util.List; +import javax.transaction.Transactional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +@Transactional +public interface ValuesetRepository extends JpaRepository { + + @Query("SELECT v.id FROM ValuesetEntity v") + List getIds(); + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListController.java new file mode 100644 index 00000000..83410927 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListController.java @@ -0,0 +1,82 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import eu.europa.ec.dgc.gateway.config.OpenApiConfig; +import eu.europa.ec.dgc.gateway.restapi.dto.ProblemReportDto; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationRequired; +import eu.europa.ec.dgc.gateway.service.TrustedPartyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/countrylist") +@RequiredArgsConstructor +@Validated +@Slf4j +public class CountryListController { + + private final TrustedPartyService trustedPartyService; + + /** + * Countrylist download endpoint. + */ + @CertificateAuthenticationRequired + @GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Returns the full list of onboarded countries.", + tags = {"Country List"}, + responses = { + @ApiResponse( + responseCode = "200", + description = "Returns the full list of onboarded countries.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + array = @ArraySchema(schema = @Schema(example = "EU")))), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + }) + public ResponseEntity> downloadCountryList() { + return ResponseEntity.ok(trustedPartyService.getCountryList()); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateController.java new file mode 100644 index 00000000..fd2d41a1 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateController.java @@ -0,0 +1,383 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import eu.europa.ec.dgc.gateway.config.OpenApiConfig; +import eu.europa.ec.dgc.gateway.exception.DgcgResponseException; +import eu.europa.ec.dgc.gateway.restapi.converter.CmsCertificateMessageConverter; +import eu.europa.ec.dgc.gateway.restapi.dto.ProblemReportDto; +import eu.europa.ec.dgc.gateway.restapi.dto.SignedCertificateDto; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationFilter; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationRequired; +import eu.europa.ec.dgc.gateway.service.AuditService; +import eu.europa.ec.dgc.gateway.service.SignerInformationService; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/signerCertificate") +@Slf4j +@RequiredArgsConstructor +public class SignerCertificateController { + + private final SignerInformationService signerInformationService; + + private final AuditService auditService; + + private static final String MDC_VERIFICATION_ERROR_REASON = "verificationFailureReason"; + private static final String MDC_VERIFICATION_ERROR_MESSAGE = "verificationFailureMessage"; + + /** + * VerificationInformation Upload Controller. + */ + @CertificateAuthenticationRequired + @PostMapping(path = "", consumes = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Uploads Signer Certificate of a trusted Issuer", + tags = {"Signer Information"}, + parameters = { + @Parameter( + in = ParameterIn.HEADER, + name = HttpHeaders.CONTENT_TYPE, + required = true, + schema = @Schema(type = "string"), + example = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE), + @Parameter( + in = ParameterIn.HEADER, + name = HttpHeaders.CONTENT_ENCODING, + required = true, + schema = @Schema(type = "string"), + example = "base64") + }, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + description = "Request body with payload. (limited)", + content = @Content( + mediaType = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE, + schema = @Schema(implementation = SignedCertificateDto.class)) + ), + responses = { + @ApiResponse( + responseCode = "201", + description = "Verification Information was created successfully."), + @ApiResponse( + responseCode = "400", + description = "Bad request. Possible reasons: Wrong Format, no CMS, not the correct signing alg," + + " missing attributes, invalid signature, certificate not signed by known CA", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class))), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )), + @ApiResponse( + responseCode = "409", + description = "Conflict. Chosen UUID is already used. Please choose another one.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class))) + } + ) + public ResponseEntity postVerificationInformation( + @RequestBody SignedCertificateDto cms, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String countryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) String authThumbprint + ) { + + DgcMdc.put("signerCertSubject", cms.getSignerCertificate().getSubject().toString()); + DgcMdc.put("payloadCertSubject", cms.getPayloadCertificate().getSubject().toString()); + + log.info("Uploading new verification certificate"); + + if (!cms.isVerified()) { + throw new DgcgResponseException( + HttpStatus.BAD_REQUEST, + "0x009", + "Invalid CMS Signature", + "", + "Signature of CMS signed certificate is not validating content of CMS package"); + } + + try { + signerInformationService.addSignerCertificate( + cms.getPayloadCertificate(), + cms.getSignerCertificate(), + cms.getSignature(), + countryCode); + } catch (SignerInformationService.SignerCertCheckException e) { + DgcMdc.put(MDC_VERIFICATION_ERROR_REASON, e.getReason().toString()); + DgcMdc.put(MDC_VERIFICATION_ERROR_MESSAGE, e.getMessage()); + log.error("Verification certificate upload failed"); + + String sentValues = String.format("{%s} country:{%s}", cms, countryCode); + if (e.getReason() == SignerInformationService.SignerCertCheckException.Reason.ALREADY_EXIST_CHECK_FAILED) { + throw new DgcgResponseException(HttpStatus.CONFLICT, "0x002", + "You cant upload an existing certificate.", + sentValues, e.getMessage()); + } else if (e.getReason() == SignerInformationService.SignerCertCheckException.Reason.UPLOAD_FAILED) { + auditService.addAuditEvent( + countryCode, + cms.getSignerCertificate(), + authThumbprint, + "UPLOAD_FAILED", + "postVerificationInformation triggered UPLOAD_FAILED"); + + throw new DgcgResponseException(HttpStatus.INTERNAL_SERVER_ERROR, + "0x003", "Upload of Signer Certificate failed", sentValues, e.getMessage()); + } else { + auditService.addAuditEvent( + countryCode, + cms.getSignerCertificate(), + authThumbprint, + "BAD_REQUEST", + "postVerificationInformation triggered BAD_REQUEST"); + + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x004", "Possible reasons: Wrong Format," + + " no CMS, not the correct signing alg missing attributes, invalid signature, certificate not " + + "signed by known CA", sentValues, e.getMessage()); + } + } + auditService.addAuditEvent( + countryCode, + cms.getSignerCertificate(), + authThumbprint, + "SUCCESS", + "postVerificationInformation successful executed"); + return ResponseEntity.status(201).build(); + } + + /** + * Http Method for deleting signer certificate. + */ + @CertificateAuthenticationRequired + @DeleteMapping(path = "", consumes = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Deletes Signer Certificate of a trusted Issuer", + tags = {"Signer Information"}, + parameters = { + @Parameter( + in = ParameterIn.HEADER, + name = HttpHeaders.CONTENT_TYPE, + required = true, + schema = @Schema(type = "string"), + example = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE), + @Parameter( + in = ParameterIn.HEADER, + name = HttpHeaders.CONTENT_ENCODING, + required = true, + schema = @Schema(type = "string"), + example = "base64") + }, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + description = "Request body with payload. (limited)", + content = @Content( + mediaType = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE, + schema = @Schema(implementation = SignedCertificateDto.class)) + ), + responses = { + @ApiResponse( + responseCode = "204", + description = "Certificate was deleted successfully."), + @ApiResponse( + responseCode = "400", + description = "Bad request. Possible reasons: Wrong Format, no CMS, not the correct signing alg," + + " missing attributes, invalid signature, certificate not signed by known CA", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class))), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + } + ) + public ResponseEntity deleteVerificationInformation( + @RequestBody SignedCertificateDto cms, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String countryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) String authThumbprint + ) { + + DgcMdc.put("signerCertSubject", cms.getSignerCertificate().getSubject().toString()); + DgcMdc.put("payloadCertSubject", cms.getPayloadCertificate().getSubject().toString()); + + log.info("Revoking verification certificate"); + + if (!cms.isVerified()) { + throw new DgcgResponseException( + HttpStatus.BAD_REQUEST, + "0x009", + "Invalid CMS Signature", + "", + "Signature of CMS signed certificate is not validating content of CMS package"); + } + + try { + signerInformationService.deleteSignerCertificate( + cms.getPayloadCertificate(), + cms.getSignerCertificate(), + countryCode); + } catch (SignerInformationService.SignerCertCheckException e) { + DgcMdc.put(MDC_VERIFICATION_ERROR_REASON, e.getReason().toString()); + DgcMdc.put(MDC_VERIFICATION_ERROR_MESSAGE, e.getMessage()); + log.error("Verification certificate delete failed"); + + String sentValues = String.format("{%s} country:{%s}", cms, countryCode); + if (e.getReason() == SignerInformationService.SignerCertCheckException.Reason.EXIST_CHECK_FAILED) { + auditService.addAuditEvent( + countryCode, + cms.getSignerCertificate(), + authThumbprint, + "EXIST_CHECK_FAILED", + "revokeVerificationInformation triggered EXIST_CHECK_FAILED"); + + throw new DgcgResponseException(HttpStatus.NOT_FOUND, "0x005", + "The certificate doesn't exists in the database.", + sentValues, e.getMessage()); + } else if (e.getReason() == SignerInformationService.SignerCertCheckException.Reason.UPLOAD_FAILED) { + auditService.addAuditEvent( + countryCode, + cms.getSignerCertificate(), + authThumbprint, + "DELETE_FAILED", + "revokeVerificationInformation triggered UPLOAD_FAILED"); + + throw new DgcgResponseException(HttpStatus.INTERNAL_SERVER_ERROR, + "0x006", "Delete of Signer Certificate failed", sentValues, e.getMessage()); + } else { + auditService.addAuditEvent( + countryCode, + cms.getSignerCertificate(), + authThumbprint, + "BAD_REQUEST", + "revokeVerificationInformation triggered BAD_REQUEST"); + + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x007", "Possible reasons: Wrong Format," + + " no CMS, not the correct signing alg missing attributes, invalid signature, certificate not " + + "signed by known CA", sentValues, e.getMessage()); + } + } + auditService.addAuditEvent( + countryCode, + cms.getSignerCertificate(), + authThumbprint, + "SUCCESS", + "revokeVerificationInformation triggered SUCCESS"); + + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } + + /** + * Alias Method for deleting signer certificate. + */ + @CertificateAuthenticationRequired + @PostMapping(path = "/delete", consumes = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Deletes Signer Certificate of a trusted Issuer", + description = "This endpoint is a workaround alias endpoint. This should only be used if it is not possible" + + " to send http payloads with DELETE requests.", + tags = {"Signer Information"}, + parameters = { + @Parameter( + in = ParameterIn.HEADER, + name = HttpHeaders.CONTENT_TYPE, + required = true, + schema = @Schema(type = "string"), + example = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE), + @Parameter( + in = ParameterIn.HEADER, + name = HttpHeaders.CONTENT_ENCODING, + required = true, + schema = @Schema(type = "string"), + example = "base64") + }, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + description = "Request body with payload. (limited)", + content = @Content( + mediaType = CmsCertificateMessageConverter.CONTENT_TYPE_CMS_VALUE, + schema = @Schema(implementation = SignedCertificateDto.class)) + ), + responses = { + @ApiResponse( + responseCode = "204", + description = "Certificate was deleted successfully."), + @ApiResponse( + responseCode = "400", + description = "Bad request. Possible reasons: Wrong Format, no CMS, not the correct signing alg," + + " missing attributes, invalid signature, certificate not signed by known CA", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class))), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + } + ) + public ResponseEntity deleteVerificationInformationAlias( + @RequestBody SignedCertificateDto cms, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String countryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) String authThumbprint + ) { + return deleteVerificationInformation(cms, countryCode, authThumbprint); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListController.java new file mode 100644 index 00000000..5a2b48d9 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListController.java @@ -0,0 +1,242 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import eu.europa.ec.dgc.gateway.config.OpenApiConfig; +import eu.europa.ec.dgc.gateway.model.TrustListType; +import eu.europa.ec.dgc.gateway.restapi.dto.CertificateTypeDto; +import eu.europa.ec.dgc.gateway.restapi.dto.ProblemReportDto; +import eu.europa.ec.dgc.gateway.restapi.dto.TrustListDto; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationFilter; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationRequired; +import eu.europa.ec.dgc.gateway.restapi.mapper.GwTrustListMapper; +import eu.europa.ec.dgc.gateway.service.TrustListService; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import java.util.List; +import java.util.Locale; +import javax.validation.Valid; +import javax.validation.constraints.Size; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/trustList") +@RequiredArgsConstructor +@Validated +@Slf4j +public class TrustListController { + + private final TrustListService trustListService; + + private final GwTrustListMapper trustListMapper; + + private static final String MDC_PROP_DOWNLOAD_KEYS_COUNT = "downloadedKeys"; + private static final String MDC_PROP_DOWNLOAD_KEYS_TYPE = "downloadedKeysType"; + private static final String MDC_PROP_DOWNLOAD_KEYS_COUNTRY = "downloadedKeysCountry"; + private static final String DOWNLOADED_TRUSTLIST_LOG_MESSAGE = "Downloaded TrustList"; + + /** + * TrustList Download Controller. + */ + @CertificateAuthenticationRequired + @GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Returns the full list of trusted certificates.", + tags = {"Trust Lists"}, + responses = { + @ApiResponse( + responseCode = "200", + description = "Returns the full list of trusted parties.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + array = @ArraySchema(schema = @Schema(implementation = TrustListDto.class)))), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + }) + public ResponseEntity> downloadTrustList( + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String downloaderCountryCode + ) { + List trustList = trustListMapper.trustListToTrustListDto(trustListService.getTrustList()); + + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_COUNT, trustList.size()); + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_COUNTRY, downloaderCountryCode); + + log.info(DOWNLOADED_TRUSTLIST_LOG_MESSAGE); + + return ResponseEntity.ok(trustList); + } + + /** + * TrustList Download Controller (filtered by type). + */ + @CertificateAuthenticationRequired + @GetMapping(path = "/{type}", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Returns a filtered list of trusted certificates.", + tags = {"Trust Lists"}, + parameters = { + @Parameter( + in = ParameterIn.PATH, + name = "type", + description = "Certificate Type to filter for", + required = true, + schema = @Schema(implementation = CertificateTypeDto.class)) + }, + responses = { + @ApiResponse( + responseCode = "200", + description = "Returns a filtered list of trusted certificates.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + array = @ArraySchema(schema = @Schema(implementation = TrustListDto.class)))), + @ApiResponse( + responseCode = "400", + description = "Bad request. Unknown Certificate Type.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + }) + public ResponseEntity> downloadTrustListFilteredByType( + @Valid @PathVariable("type") CertificateTypeDto type, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String downloaderCountryCode + ) { + + TrustListType mappedType = trustListMapper.certificateTypeDtoToTrustListType(type); + + List trustList = trustListMapper.trustListToTrustListDto( + trustListService.getTrustList(mappedType)); + + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_COUNT, trustList.size()); + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_TYPE, type.name()); + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_COUNTRY, downloaderCountryCode); + + log.info(DOWNLOADED_TRUSTLIST_LOG_MESSAGE); + + return ResponseEntity.ok(trustList); + } + + /** + * TrustList Download Controller (filtered by type and country). + */ + @CertificateAuthenticationRequired + @GetMapping(path = "/{type}/{country}", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Returns a filtered list of trusted certificates.", + tags = {"Trust Lists"}, + parameters = { + @Parameter( + in = ParameterIn.PATH, + name = "type", + description = "Certificate Type to filter for", + required = true, + schema = @Schema(implementation = CertificateTypeDto.class)), + @Parameter( + in = ParameterIn.PATH, + name = "country", + description = "2-Digit Country Code to filter for", + example = "EU", + required = true) + }, + responses = { + @ApiResponse( + responseCode = "200", + description = "Returns a filtered list of trusted certificates.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + array = @ArraySchema(schema = @Schema(implementation = TrustListDto.class)))), + @ApiResponse( + responseCode = "400", + description = "Bad request. Unknown Certificate Type or invalid country code.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + }) + public ResponseEntity> downloadTrustListFilteredByCountryAndType( + @Valid @PathVariable("type") CertificateTypeDto type, + @Valid @Size(max = 2, min = 2) @PathVariable("country") String countryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String downloaderCountryCode + ) { + + TrustListType mappedType = trustListMapper.certificateTypeDtoToTrustListType(type); + countryCode = countryCode.toUpperCase(Locale.ROOT); + + List trustList = trustListMapper.trustListToTrustListDto( + trustListService.getTrustList(mappedType, countryCode)); + + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_COUNT, trustList.size()); + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_TYPE, type.name()); + DgcMdc.put(MDC_PROP_DOWNLOAD_KEYS_COUNTRY, downloaderCountryCode); + + log.info(DOWNLOADED_TRUSTLIST_LOG_MESSAGE); + + return ResponseEntity.ok(trustList); + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleController.java new file mode 100644 index 00000000..cb8da86c --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleController.java @@ -0,0 +1,378 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import eu.europa.ec.dgc.gateway.config.OpenApiConfig; +import eu.europa.ec.dgc.gateway.entity.ValidationRuleEntity; +import eu.europa.ec.dgc.gateway.exception.DgcgResponseException; +import eu.europa.ec.dgc.gateway.restapi.converter.CmsStringMessageConverter; +import eu.europa.ec.dgc.gateway.restapi.dto.ProblemReportDto; +import eu.europa.ec.dgc.gateway.restapi.dto.SignedStringDto; +import eu.europa.ec.dgc.gateway.restapi.dto.ValidationRuleDto; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationFilter; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationRequired; +import eu.europa.ec.dgc.gateway.restapi.mapper.GwValidationRuleMapper; +import eu.europa.ec.dgc.gateway.service.AuditService; +import eu.europa.ec.dgc.gateway.service.ValidationRuleService; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.validator.constraints.Length; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/rules") +@Slf4j +@RequiredArgsConstructor +public class ValidationRuleController { + + private final ValidationRuleService validationRuleService; + + private final AuditService auditService; + + private final GwValidationRuleMapper validationRuleMapper; + + private static final String MDC_VALIDATION_RULE_DOWNLOAD_AMOUNT = "validationDownloadAmount"; + private static final String MDC_VALIDATION_RULE_DOWNLOAD_REQUESTER = "validationDownloadRequester"; + private static final String MDC_VALIDATION_RULE_DOWNLOAD_REQUESTED = "validationDownloadRequested"; + private static final String MDC_VALIDATION_RULE_DELETE_ID = "validationDownloadId"; + private static final String MDC_VALIDATION_RULE_DELETE_AMOUNT = "validationDeleteAmount"; + + /** + * Endpoint to download a Validation Rule. + */ + @CertificateAuthenticationRequired + @GetMapping(path = "/{country}", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Download all rules of country.", + tags = {"Validation Rules"}, + parameters = { + @Parameter( + in = ParameterIn.PATH, + name = "country", + required = true, + example = "EU") + }, + responses = { + @ApiResponse( + responseCode = "200", + description = "Download successful.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(ref = "#/components/schemas/ValidationRuleDownloadResponse") + ) + ) + } + ) + public ResponseEntity>> downloadValidationRules( + @Valid @PathVariable("country") @Length(max = 2, min = 2) String requestedCountryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String requesterCountryCode + ) { + + log.info("Rule Download Request"); + + List validationRuleEntities = + validationRuleService.getActiveValidationRules(requestedCountryCode); + + Map> map = new HashMap<>(); + + validationRuleEntities.forEach(validationRuleEntitiy -> + map.computeIfAbsent(validationRuleEntitiy.getRuleId(), k -> new ArrayList<>()) + .add(validationRuleMapper.entityToDto(validationRuleEntitiy))); + + DgcMdc.put(MDC_VALIDATION_RULE_DOWNLOAD_AMOUNT, validationRuleEntities.size()); + DgcMdc.put(MDC_VALIDATION_RULE_DOWNLOAD_REQUESTER, requesterCountryCode); + DgcMdc.put(MDC_VALIDATION_RULE_DOWNLOAD_REQUESTED, requestedCountryCode); + log.info("Rule Download Success"); + + return ResponseEntity.ok(map); + } + + /** + * Endpoint to upload a Validation Rule. + */ + @CertificateAuthenticationRequired + @PostMapping(path = "", consumes = { + CmsStringMessageConverter.CONTENT_TYPE_CMS_TEXT_VALUE, CmsStringMessageConverter.CONTENT_TYPE_CMS_VALUE}) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Create a new versions of a rule with id", + tags = {"Validation Rules"}, + requestBody = @RequestBody( + required = true, + description = "CMS Signed String with Validation Rule. Needs to be signed with valid Upload Certificate" + ), + responses = { + @ApiResponse( + responseCode = "201", + description = "Created successful." + ), + @ApiResponse( + responseCode = "400", + description = "Bad data submitted. See ProblemReport for more details.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ), + @ApiResponse( + responseCode = "403", + description = "You are not allowed to create this validation rules.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ) + } + ) + public ResponseEntity uploadValidationRule( + @org.springframework.web.bind.annotation.RequestBody SignedStringDto signedJson, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String authenticatedCountryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) String thumbprint + ) { + + log.info("Rule Upload Request"); + + if (!signedJson.isVerified()) { + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x260", "CMS signature is invalid", "", + "Submitted string needs to be signed by a valid upload certificate"); + } + + ValidationRuleEntity createdValidationRule; + + try { + createdValidationRule = validationRuleService.addValidationRule( + signedJson.getPayloadString(), + signedJson.getSignerCertificate(), + signedJson.getRawMessage(), + authenticatedCountryCode); + } catch (ValidationRuleService.ValidationRuleCheckException e) { + DgcMdc.put("validationRuleUploadError", e.getMessage()); + DgcMdc.put("validationRuleUploadReason", e.getReason().toString()); + log.error("Rule Upload Failed"); + + switch (e.getReason()) { + case INVALID_JSON: + throw new DgcgResponseException( + HttpStatus.BAD_REQUEST, "0x200", "Invalid JSON", "", e.getMessage()); + case INVALID_COUNTRY: + throw new DgcgResponseException(HttpStatus.FORBIDDEN, "0x210", "Invalid Country sent", "", + e.getMessage()); + case INVALID_VERSION: + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x220", "Invalid Version", "", + e.getMessage()); + case UPLOADER_CERT_CHECK_FAILED: + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x230", "Invalid Upload Cert", + signedJson.getSignerCertificate().getSubject().toString(), e.getMessage()); + case INVALID_TIMESTAMP: + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x240", "Invalid Timestamp(s)", + "", e.getMessage()); + case INVALID_RULE_ID: + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x250", "Invalid RuleID", + "", e.getMessage()); + default: + throw new DgcgResponseException(HttpStatus.INTERNAL_SERVER_ERROR, "0x299", "Unexpected Error", + "", ""); + } + } + + + log.info("Rule Upload Success"); + + auditService.addAuditEvent( + authenticatedCountryCode, + signedJson.getSignerCertificate(), + thumbprint, + "CREATED", + String.format("Created Validation Rule with ID %s (%s)", + createdValidationRule.getRuleId(), createdValidationRule.getVersion())); + + return ResponseEntity.status(HttpStatus.CREATED).build(); + } + + /** + * Endpoint to delete a Validation Rule. + */ + @CertificateAuthenticationRequired + @DeleteMapping(path = "", consumes = { + CmsStringMessageConverter.CONTENT_TYPE_CMS_TEXT_VALUE, CmsStringMessageConverter.CONTENT_TYPE_CMS_VALUE}) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Delete all versions of a rule with id", + tags = {"Validation Rules"}, + requestBody = @RequestBody( + required = true, + description = "CMS Signed String representing the Rule ID. Needs to be signed with valid Upload Certificate" + ), + responses = { + @ApiResponse( + responseCode = "204", + description = "Delete successful." + ), + @ApiResponse( + responseCode = "400", + description = "Bad data submitted. See ProblemReport for more details.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ), + @ApiResponse( + responseCode = "403", + description = "You are not allowed to delete these validation rules.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ), + @ApiResponse( + responseCode = "404", + description = "Validation rule not found.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ) + } + ) + public ResponseEntity deleteValidationRules( + @org.springframework.web.bind.annotation.RequestBody SignedStringDto signedString, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String authenticatedCountryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) String thumbprint + ) { + + log.info("Rule Delete Request"); + + if (!signedString.isVerified()) { + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x260", "CMS signature is invalid", "", + "Submitted string needs to be signed by a valid upload certificate"); + } + + try { + validationRuleService.contentCheckUploaderCertificate( + signedString.getSignerCertificate(), authenticatedCountryCode); + } catch (ValidationRuleService.ValidationRuleCheckException e) { + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x230", "Invalid Upload certificate", "", + "You have to use a onboarded upload certificate to sign the string"); + } + + String countryCodeFromIdString = + validationRuleService.getCountryCodeFromIdString(signedString.getPayloadString()); + + if (countryCodeFromIdString == null) { + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x250", "ID-String is invalid", + signedString.getPayloadString(), "Example valid ID-String: GR-EU-11100"); + } + + if (!countryCodeFromIdString.equals(authenticatedCountryCode)) { + throw new DgcgResponseException(HttpStatus.FORBIDDEN, "0x210", "Invalid country in ID-String", + String.format( + "Your authenticated country code: %s, Your requested country code: %s", + authenticatedCountryCode, countryCodeFromIdString), + "ID-String needs to contain your Country Code."); + } + + int deleted = validationRuleService.deleteByRuleId(signedString.getPayloadString()); + + if (deleted == 0) { + throw new DgcgResponseException(HttpStatus.NOT_FOUND, "0x270", "Validation Rule does not exist", + String.format("Validation-Rule Id: %s", signedString.getPayloadString()), + "You can only delete existing validation rules."); + } + + DgcMdc.put(MDC_VALIDATION_RULE_DELETE_AMOUNT, deleted); + DgcMdc.put(MDC_VALIDATION_RULE_DELETE_ID, signedString.getPayloadString()); + log.info("Rule Delete Success"); + + auditService.addAuditEvent( + authenticatedCountryCode, + signedString.getSignerCertificate(), + thumbprint, + "DELETED", + "Deleted Validation Rule with ID " + signedString.getPayloadString()); + + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } + + /** + * Alias endpoint to delete a Validation Rule. + */ + @CertificateAuthenticationRequired + @PostMapping(path = "/delete", consumes = { + CmsStringMessageConverter.CONTENT_TYPE_CMS_TEXT_VALUE, CmsStringMessageConverter.CONTENT_TYPE_CMS_VALUE}) + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Delete all versions of a rule with id (Alias Endpoint for DELETE)", + tags = {"Validation Rules"}, + requestBody = @RequestBody( + required = true, + description = "CMS Signed String representing the Rule ID. Needs to be signed with valid Upload Certificate" + ), + responses = { + @ApiResponse( + responseCode = "204", + description = "Delete successful." + ), + @ApiResponse( + responseCode = "400", + description = "Bad data submitted. See ProblemReport for more details.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ), + @ApiResponse( + responseCode = "403", + description = "You are not allowed to delete these validation rules.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ), + @ApiResponse( + responseCode = "404", + description = "Validation rule not found.", + content = @Content(schema = @Schema(implementation = ProblemReportDto.class)) + ) + } + ) + public ResponseEntity deleteValidationRulesAliasEndpoint( + @org.springframework.web.bind.annotation.RequestBody SignedStringDto signedString, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY) String authenticatedCountryCode, + @RequestAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) String thumbprint + ) { + return deleteValidationRules(signedString, authenticatedCountryCode, thumbprint); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetController.java new file mode 100644 index 00000000..1bbe1e06 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetController.java @@ -0,0 +1,141 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import eu.europa.ec.dgc.gateway.config.OpenApiConfig; +import eu.europa.ec.dgc.gateway.exception.DgcgResponseException; +import eu.europa.ec.dgc.gateway.restapi.dto.ProblemReportDto; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationRequired; +import eu.europa.ec.dgc.gateway.service.ValuesetService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +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.RestController; + +@RestController +@RequestMapping("/valuesets") +@Slf4j +@RequiredArgsConstructor +public class ValuesetController { + + private final ValuesetService valuesetService; + + /** + * Controller to get valueset ids. + */ + @CertificateAuthenticationRequired + @GetMapping(path = "") + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Gets a list of available valuesets.", + tags = {"Valueset"}, + responses = { + @ApiResponse( + responseCode = "200", + description = "List of valueset ids", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = String.class))) + ), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + } + ) + public ResponseEntity> getValuesetIds() { + return ResponseEntity.ok(valuesetService.getValuesetIds()); + } + + /** + * Controller to get a specific valueset. + */ + @CertificateAuthenticationRequired + @GetMapping(path = "/{id}") + @Operation( + security = { + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_HASH), + @SecurityRequirement(name = OpenApiConfig.SECURITY_SCHEMA_DISTINGUISH_NAME) + }, + summary = "Requests a specific valueset by its id.", + tags = {"Valueset"}, + parameters = @Parameter(in = ParameterIn.PATH, name = "id", description = "Valueset ID"), + responses = { + @ApiResponse( + responseCode = "200", + description = "Valueset JSON Object", + content = @Content(schema = @Schema(implementation = String.class)) + ), + @ApiResponse( + responseCode = "401", + description = "Unauthorized. No Access to the system. (Client Certificate not present or whitelisted)", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )), + @ApiResponse( + responseCode = "404", + description = "Valueset not found", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ProblemReportDto.class) + )) + } + ) + public ResponseEntity getValueset(@PathVariable("id") String id) { + + Optional valueset = valuesetService.getValueSetById(id); + + if (valueset.isEmpty()) { + throw new DgcgResponseException( + HttpStatus.NOT_FOUND, + "0x100", + "Valueset not found", + String.format("Requested valueset id %s", id), + "Use the GET /valuesets endpoint to get a list of available valueset ids."); + } + + return ResponseEntity + .status(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(valueset.get()); + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CertificateTypeEnumConverter.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CertificateTypeEnumConverter.java new file mode 100644 index 00000000..1c3043c3 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CertificateTypeEnumConverter.java @@ -0,0 +1,41 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.converter; + +import eu.europa.ec.dgc.gateway.restapi.dto.CertificateTypeDto; +import java.util.Locale; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +@Component +public class CertificateTypeEnumConverter implements Converter { + + /** + * Converts a string into {@link CertificateTypeDto} (case insensitive). + * + * @param source String to convert + * @return value of {@link CertificateTypeDto} + */ + @Override + public CertificateTypeDto convert(String source) { + return CertificateTypeDto.valueOf(source.toUpperCase(Locale.ROOT)); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsCertificateMessageConverter.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsCertificateMessageConverter.java new file mode 100644 index 00000000..d8f79d7f --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsCertificateMessageConverter.java @@ -0,0 +1,97 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.converter; + +import eu.europa.ec.dgc.gateway.exception.DgcgResponseException; +import eu.europa.ec.dgc.gateway.restapi.dto.SignedCertificateDto; +import eu.europa.ec.dgc.signing.SignedCertificateMessageParser; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class CmsCertificateMessageConverter extends AbstractHttpMessageConverter { + + public static final MediaType CONTENT_TYPE_CMS = new MediaType("application", "cms"); + public static final String CONTENT_TYPE_CMS_VALUE = "application/cms"; + + public CmsCertificateMessageConverter() { + super(CONTENT_TYPE_CMS); + } + + @Override + protected boolean supports(Class clazz) { + return SignedCertificateDto.class.isAssignableFrom(clazz); + } + + @Override + protected SignedCertificateDto readInternal( + Class clazz, + HttpInputMessage inputMessage + ) throws IOException, HttpMessageNotReadableException { + + byte[] inputBytes = inputMessage.getBody().readAllBytes(); + SignedCertificateMessageParser certificateParser = new SignedCertificateMessageParser(inputBytes); + + switch (certificateParser.getParserState()) { + case FAILURE_INVALID_BASE64: + throw badRequest("Invalid Base64 CMS Message"); + case FAILURE_INVALID_CMS: + throw badRequest("Could not parse CMS Message"); + case FAILURE_INVALID_CMS_BODY: + throw badRequest("CMS Message needs to have binary data as body."); + case FAILURE_CMS_SIGNING_CERT_INVALID: + throw badRequest("CMS Message needs to contain exactly one X509 certificate"); + case FAILURE_CMS_SIGNER_INFO: + throw badRequest("CMS Message needs to have exactly 1 signer information."); + case FAILURE_CMS_BODY_PARSING_FAILED: + throw badRequest("CMS Message payload needs to be a DER encoded X509 certificate"); + default: + } + + return SignedCertificateDto.builder() + .payloadCertificate(certificateParser.getPayload()) + .signerCertificate(certificateParser.getSigningCertificate()) + .rawMessage(new String(inputBytes, StandardCharsets.UTF_8)) + .signature(certificateParser.getSignature()) + .verified(certificateParser.isSignatureVerified()) + .build(); + } + + @Override + protected void writeInternal(SignedCertificateDto signedCertificateDto, HttpOutputMessage outputMessage) + throws HttpMessageNotWritableException { + throw new HttpMessageNotWritableException("Outbound Usage of CMS Messages is currently not supported!"); + } + + private DgcgResponseException badRequest(String message) { + return new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x402", message, "", ""); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsStringMessageConverter.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsStringMessageConverter.java new file mode 100644 index 00000000..535695a4 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/converter/CmsStringMessageConverter.java @@ -0,0 +1,99 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.converter; + +import eu.europa.ec.dgc.gateway.exception.DgcgResponseException; +import eu.europa.ec.dgc.gateway.restapi.dto.SignedStringDto; +import eu.europa.ec.dgc.signing.SignedStringMessageParser; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class CmsStringMessageConverter extends AbstractHttpMessageConverter { + + public static final MediaType CONTENT_TYPE_CMS_TEXT = new MediaType("application", "cms-text"); + public static final String CONTENT_TYPE_CMS_TEXT_VALUE = "application/cms-text"; + public static final MediaType CONTENT_TYPE_CMS = new MediaType("application", "cms"); + public static final String CONTENT_TYPE_CMS_VALUE = "application/cms"; + + public CmsStringMessageConverter() { + super(CONTENT_TYPE_CMS_TEXT, CONTENT_TYPE_CMS); + } + + @Override + protected boolean supports(Class clazz) { + return SignedStringDto.class.isAssignableFrom(clazz); + } + + @Override + protected SignedStringDto readInternal( + Class clazz, + HttpInputMessage inputMessage + ) throws IOException, HttpMessageNotReadableException { + + byte[] inputBytes = inputMessage.getBody().readAllBytes(); + SignedStringMessageParser parser = new SignedStringMessageParser(inputBytes); + + switch (parser.getParserState()) { + case FAILURE_INVALID_BASE64: + throw badRequest("Invalid Base64 CMS Message"); + case FAILURE_INVALID_CMS: + throw badRequest("Could not parse CMS Message"); + case FAILURE_INVALID_CMS_BODY: + throw badRequest("CMS Message needs to have binary data as body."); + case FAILURE_CMS_SIGNING_CERT_INVALID: + throw badRequest("CMS Message needs to contain exactly one X509 certificate"); + case FAILURE_CMS_SIGNER_INFO: + throw badRequest("CMS Message needs to have exactly 1 signer information."); + case FAILURE_CMS_BODY_PARSING_FAILED: + throw badRequest("CMS Message payload needs to be a String"); + default: + } + + return SignedStringDto.builder() + .payloadString(parser.getPayload()) + .signerCertificate(parser.getSigningCertificate()) + .rawMessage(new String(inputBytes, StandardCharsets.UTF_8)) + .signature(parser.getSignature()) + .verified(parser.isSignatureVerified()) + .build(); + } + + @Override + protected void writeInternal(SignedStringDto signedStringDto, HttpOutputMessage outputMessage) + throws HttpMessageNotWritableException { + throw new HttpMessageNotWritableException("Outbound Usage of CMS Messages is currently not supported!"); + } + + private DgcgResponseException badRequest(String message) { + return new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x402", message, "", ""); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/CertificateTypeDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/CertificateTypeDto.java new file mode 100644 index 00000000..67c662e6 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/CertificateTypeDto.java @@ -0,0 +1,28 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.dto; + +public enum CertificateTypeDto { + AUTHENTICATION, + UPLOAD, + CSCA, + DSC +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ProblemReportDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ProblemReportDto.java new file mode 100644 index 00000000..8bc46394 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ProblemReportDto.java @@ -0,0 +1,46 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Schema( + name = "ProblemReport", + type = "object" +) +@Data +@AllArgsConstructor +public class ProblemReportDto { + + @Schema(example = "0x001") + private String code; + + @Schema(example = "Signer Certificate is unknown.") + private String problem; + + @Schema(example = "Certificate Thumbprint: 2342424f24c242f42f4b24...") + private String sendValue; + + @Schema(example = "Use a known upload certificate to upload signer information.") + private String details; +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedCertificateDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedCertificateDto.java new file mode 100644 index 00000000..114d339a --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedCertificateDto.java @@ -0,0 +1,60 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.bouncycastle.cert.X509CertificateHolder; + +@Schema( + name = "Signed Certificate (CMS)", + type = "string", + example = "MIICyDCCAbCgAwIBAgIGAXR3DZUUMA0GCSqGSIb3DQEBBQUAMBwxCzAJBgNVBAYT" + + "AkRFMQ0wCwYDVQQDDARkZW1vMB4XDTIwMDgyNzA4MDY1MloXDTIxMDkxMDA4MDY1" + + "MlowHDELMAkGA1UEBhMCREUxDTALBgNVBAMMBGRlbW8wggEiMA0GCSqGSIb3DQEB" + + "AQUAA4IBDwAwggEKAoIBAQCKR0TEJOO4z0ks4OMAovcyxuPpeZuR1JykNNFd3OR+" + + "vFWJLJtDYgRjtuqSuKCghLa/ci+0yIs3OeitGtajqFIukYksvX2LxOZDYDUbnpGQ" + + "DPNMVmpEavDBbvKON8C8K036pC41bNvwkTrfUyZ8iE+hV2+kj1SHUyw7jweEUoiw" + + "NmMiaXXPiMIOj7D0qnmM+iTGN9g/DrJ/IvvsgiGpK3QlQ5pnHs2BvzrSw4LFAZ8c" + + "SQfWKheZVHfQf26mJFdEzowrzfzForDdeFAPIIirhufE3jWFxj1thfztu+VSMj84" + + "sDqodEt2VJOY+DvLB1Ls/26LSmFtMnCEuBAhkbQ1E0tbAgMBAAGjEDAOMAwGA1Ud" + + "EwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBABaMEQz4Gbj+G0SZGZaIDoUFDB6n" + + "1R6iUS0zTBgsV8pSpFhwPryRiLdeNzIzsDdQ1ack1NfQ6YPn3/yOJ/SvnXs6n+vO" + + "WQW2KsuiymPSd/wjeywRRMfCysHjrmE+m+8lrFDrKuPnrACwQIsX9PDEsRRBnpSy" + + "5NKUZn6u3iPV9x6rwYCdCa/8VDGLqVb3eEE5dbFaYG9uW02cSbmsiZm8KmW8b6BF" + + "eIwHVRAH6Cs1VZI8UIrdVGCE111tUo/0957rF+/doFyJcwX+4ESH0m2MsHFjXDfG" + + "U8yTjiUh/b2Erk4TCmrJpux30QRhsNZwkmEYSbRv+vp5/obgH1mL5ouoV5I=" +) +@Data +@Builder +@AllArgsConstructor +public class SignedCertificateDto { + + private final X509CertificateHolder payloadCertificate; + private final X509CertificateHolder signerCertificate; + private final String rawMessage; + private final String signature; + + private final boolean verified; + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedStringDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedStringDto.java new file mode 100644 index 00000000..38aa07da --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/SignedStringDto.java @@ -0,0 +1,60 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.bouncycastle.cert.X509CertificateHolder; + +@Schema( + name = "Signed String (CMS)", + type = "string", + example = "MIICyDCCAbCgAwIBAgIGAXR3DZUUMA0GCSqGSIb3DQEBBQUAMBwxCzAJBgNVBAYT" + + "AkRFMQ0wCwYDVQQDDARkZW1vMB4XDTIwMDgyNzA4MDY1MloXDTIxMDkxMDA4MDY1" + + "MlowHDELMAkGA1UEBhMCREUxDTALBgNVBAMMBGRlbW8wggEiMA0GCSqGSIb3DQEB" + + "AQUAA4IBDwAwggEKAoIBAQCKR0TEJOO4z0ks4OMAovcyxuPpeZuR1JykNNFd3OR+" + + "vFWJLJtDYgRjtuqSuKCghLa/ci+0yIs3OeitGtajqFIukYksvX2LxOZDYDUbnpGQ" + + "DPNMVmpEavDBbvKON8C8K036pC41bNvwkTrfUyZ8iE+hV2+kj1SHUyw7jweEUoiw" + + "NmMiaXXPiMIOj7D0qnmM+iTGN9g/DrJ/IvvsgiGpK3QlQ5pnHs2BvzrSw4LFAZ8c" + + "SQfWKheZVHfQf26mJFdEzowrzfzForDdeFAPIIirhufE3jWFxj1thfztu+VSMj84" + + "sDqodEt2VJOY+DvLB1Ls/26LSmFtMnCEuBAhkbQ1E0tbAgMBAAGjEDAOMAwGA1Ud" + + "EwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBABaMEQz4Gbj+G0SZGZaIDoUFDB6n" + + "1R6iUS0zTBgsV8pSpFhwPryRiLdeNzIzsDdQ1ack1NfQ6YPn3/yOJ/SvnXs6n+vO" + + "WQW2KsuiymPSd/wjeywRRMfCysHjrmE+m+8lrFDrKuPnrACwQIsX9PDEsRRBnpSy" + + "5NKUZn6u3iPV9x6rwYCdCa/8VDGLqVb3eEE5dbFaYG9uW02cSbmsiZm8KmW8b6BF" + + "eIwHVRAH6Cs1VZI8UIrdVGCE111tUo/0957rF+/doFyJcwX+4ESH0m2MsHFjXDfG" + + "U8yTjiUh/b2Erk4TCmrJpux30QRhsNZwkmEYSbRv+vp5/obgH1mL5ouoV5I=" +) +@Data +@Builder +@AllArgsConstructor +public class SignedStringDto { + + private final String payloadString; + private final X509CertificateHolder signerCertificate; + private final String rawMessage; + private final String signature; + + private final boolean verified; + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/TrustListDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/TrustListDto.java new file mode 100644 index 00000000..d1c12619 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/TrustListDto.java @@ -0,0 +1,59 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.ZonedDateTime; +import lombok.Getter; +import lombok.Setter; + +@Schema( + name = "TrustList", + type = "object" +) +@Getter +@Setter +public class TrustListDto { + + @Schema(example = "qroU+hDDovs=") + private String kid; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") + private ZonedDateTime timestamp; + + @Schema(example = "EU") + private String country; + + private CertificateTypeDto certificateType; + + @Schema(example = "aaba14fa10c3a2fb441a28af0ec1bb4128153b9ddc796b66bfa04b02ea3e103e") + private String thumbprint; + + @Schema(example = "o53CbAa77LyIMFc5Gz+B2Jc275Gdg/SdLayw7gx0GrTcinR95zfTLr8nNHgJMYlX3rD8Y11zB/Osyt0 ..." + + " W+VIrYRGSEmgjGy2EwzvA5nVhsaA+/udnmbyQw9LjAOQ==") + private String signature; + + @Schema(example = "MIICyDCCAbCgAwIBAgIGAXR3DZUUMA0GCSqGSIb3DQEBBQUAMBwxCzAJB ..." + + " Jpux30QRhsNZwkmEYSbRv+vp5/obgH1mL5ouoV5I=") + private String rawData; + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ValidationRuleDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ValidationRuleDto.java new file mode 100644 index 00000000..61e467f6 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/ValidationRuleDto.java @@ -0,0 +1,48 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.ZonedDateTime; +import lombok.Getter; +import lombok.Setter; + +@Schema(name = "ValidationRule") +@Setter +@Getter +public class ValidationRuleDto { + + @Schema(description = "Version of the Rule (Semver)", example = "1.0.0") + String version; + + @Schema(description = "Rule is valid from") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") + ZonedDateTime validFrom; + + @Schema(description = "Rule is valid to") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") + ZonedDateTime validTo; + + @Schema(description = "CMS containing the signed JSON Object with the rule itself") + String cms; + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationFilter.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationFilter.java new file mode 100644 index 00000000..7d780f0b --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationFilter.java @@ -0,0 +1,202 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.filter; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.exception.DgcgResponseException; +import eu.europa.ec.dgc.gateway.service.TrustedPartyService; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.util.encoders.DecoderException; +import org.bouncycastle.util.encoders.Hex; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +@Slf4j +@Component +@AllArgsConstructor +public class CertificateAuthenticationFilter extends OncePerRequestFilter { + + public static final String REQUEST_PROP_COUNTRY = "reqPropCountry"; + public static final String REQUEST_PROP_THUMBPRINT = "reqPropCertThumbprint"; + + private final RequestMappingHandlerMapping requestMap; + + private final DgcConfigProperties properties; + + private final TrustedPartyService trustedPartyService; + + private final HandlerExceptionResolver handlerExceptionResolver; + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + try { + HandlerExecutionChain handlerExecutionChain = requestMap.getHandler(request); + + if (handlerExecutionChain == null) { + return true; + } else { + return !((HandlerMethod) handlerExecutionChain.getHandler()).getMethod() + .isAnnotationPresent(CertificateAuthenticationRequired.class); + } + } catch (Exception e) { + handlerExceptionResolver.resolveException(request, null, null, e); + return true; + } + } + + private String normalizeCertificateHash(String inputString) { + if (inputString == null) { + return null; + } + + boolean isHexString; + // check if it is a hex string + try { + Hex.decode(inputString); + isHexString = true; + } catch (DecoderException ignored) { + isHexString = false; + } + + // We can assume that the given string is hex encoded SHA-256 hash when length is 64 and string is hex encoded + if (inputString.length() == 64 && isHexString) { + return inputString; + } else { + try { + String hexString; + if (inputString.contains("%")) { // only url decode input string if it contains none base64 characters + inputString = URLDecoder.decode(inputString, StandardCharsets.UTF_8); + } + hexString = Hex.toHexString(Base64.getDecoder().decode(inputString)); + return hexString; + } catch (IllegalArgumentException ignored) { + log.error("Could not normalize certificate hash."); + return null; + } + } + } + + @Override + protected void doFilterInternal( + HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, + FilterChain filterChain + ) throws ServletException, IOException { + logger.debug("Checking request for auth headers"); + + String headerDistinguishedName = + httpServletRequest.getHeader(properties.getCertAuth().getHeaderFields().getDistinguishedName()); + + String headerCertThumbprint = normalizeCertificateHash( + httpServletRequest.getHeader(properties.getCertAuth().getHeaderFields().getThumbprint())); + + if (headerDistinguishedName == null || headerCertThumbprint == null) { + log.error("No thumbprint or distinguish name"); + handlerExceptionResolver.resolveException( + httpServletRequest, + httpServletResponse, + null, + new DgcgResponseException( + HttpStatus.UNAUTHORIZED, + "0x400", + "No thumbprint or distinguish name", + "", "")); + return; + } + + headerDistinguishedName = URLDecoder.decode(headerDistinguishedName, StandardCharsets.UTF_8); + + DgcMdc.put("dnString", headerDistinguishedName); + DgcMdc.put("thumbprint", headerCertThumbprint); + + Map distinguishNameMap = parseDistinguishNameString(headerDistinguishedName); + + if (!distinguishNameMap.containsKey("C")) { + log.error("Country property is missing"); + handlerExceptionResolver.resolveException( + httpServletRequest, httpServletResponse, null, + new DgcgResponseException( + HttpStatus.BAD_REQUEST, + "0x401", + "Client Certificate must contain country property", + headerDistinguishedName, "")); + return; + } + + Optional certFromDb = trustedPartyService.getCertificate( + headerCertThumbprint, + distinguishNameMap.get("C"), + TrustedPartyEntity.CertificateType.AUTHENTICATION + ); + + if (certFromDb.isEmpty()) { + log.error("Unknown client certificate"); + handlerExceptionResolver.resolveException( + httpServletRequest, httpServletResponse, null, + new DgcgResponseException( + HttpStatus.UNAUTHORIZED, + "0x402", + "Client is not authorized to access the service", + "", "")); + + return; + } + + log.info("Successful Authentication"); + httpServletRequest.setAttribute(REQUEST_PROP_COUNTRY, distinguishNameMap.get("C")); + httpServletRequest.setAttribute(REQUEST_PROP_THUMBPRINT, headerCertThumbprint); + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + /** + * Parses a given Distinguish Name string (e.g. "C=DE,OU=Test Unit,O=Test Company"). + * + * @param dnString the DN string to parse. + * @return Map with properties of the DN string. + */ + private Map parseDistinguishNameString(String dnString) { + return Arrays.stream(dnString.split(",")) + .map(part -> part.split("=")) + .filter(entry -> entry.length == 2) + .collect(Collectors.toMap(arr -> arr[0].toUpperCase().trim(), arr -> arr[1].trim(), (s, s2) -> s)); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRequired.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRequired.java new file mode 100644 index 00000000..5ddc1a20 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRequired.java @@ -0,0 +1,31 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.filter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface CertificateAuthenticationRequired { +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwTrustListMapper.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwTrustListMapper.java new file mode 100644 index 00000000..e016da0c --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwTrustListMapper.java @@ -0,0 +1,38 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.mapper; + +import eu.europa.ec.dgc.gateway.model.TrustList; +import eu.europa.ec.dgc.gateway.model.TrustListType; +import eu.europa.ec.dgc.gateway.restapi.dto.CertificateTypeDto; +import eu.europa.ec.dgc.gateway.restapi.dto.TrustListDto; +import java.util.List; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface GwTrustListMapper { + + TrustListDto trustListToTrustListDto(TrustList trustList); + + List trustListToTrustListDto(List trustList); + + TrustListType certificateTypeDtoToTrustListType(CertificateTypeDto certificateTypeDto); +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwValidationRuleMapper.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwValidationRuleMapper.java new file mode 100644 index 00000000..983d9e87 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/GwValidationRuleMapper.java @@ -0,0 +1,32 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.mapper; + +import eu.europa.ec.dgc.gateway.entity.ValidationRuleEntity; +import eu.europa.ec.dgc.gateway.restapi.dto.ValidationRuleDto; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface GwValidationRuleMapper { + + ValidationRuleDto entityToDto(ValidationRuleEntity entity); + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/AuditService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/AuditService.java new file mode 100644 index 00000000..b292b1d7 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/AuditService.java @@ -0,0 +1,89 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.AuditEventEntity; +import eu.europa.ec.dgc.gateway.repository.AuditEventRepository; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import eu.europa.ec.dgc.utils.CertificateUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.cert.X509CertificateHolder; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class AuditService { + + private final AuditEventRepository auditEventRepository; + + private final CertificateUtils certificateUtils; + + private static final String MDC_PROP_AUDIT_ID = "auditId"; + private static final String MDC_PROP_AUDIT_COUNTRY = "country"; + + /** + * Method to create an audit Event. + * + * @param countryCode 2-digit country Code + * @param uploaderCertificate the uploader cert + * @param authenticationSha256Fingerprint fingerprint of the authentication cert + * @param auditEvent Event ID + * @param auditEventDescription EventDescription + */ + public void addAuditEvent(String countryCode, X509CertificateHolder uploaderCertificate, + String authenticationSha256Fingerprint, + String auditEvent, String auditEventDescription) { + addAuditEvent( + countryCode, + certificateUtils.getCertThumbprint(uploaderCertificate), + authenticationSha256Fingerprint, + auditEvent, + auditEventDescription + ); + } + + /** + * Method to create an audit Event. + * + * @param countryCode 2-digit country Code + * @param uploaderSha256Fingerprint fingerprint of the uploader cert + * @param authenticationSha256Fingerprint fingerprint of the authentication cert + * @param auditEvent Event ID + * @param auditEventDescription EventDescription + */ + public void addAuditEvent(String countryCode, String uploaderSha256Fingerprint, + String authenticationSha256Fingerprint, String auditEvent, String auditEventDescription) { + AuditEventEntity auditEventEntity = new AuditEventEntity(); + auditEventEntity.setEvent(auditEvent); + auditEventEntity.setDescription(auditEventDescription); + auditEventEntity.setCountry(countryCode); + auditEventEntity.setAuthenticationSha256Fingerprint(authenticationSha256Fingerprint); + auditEventEntity.setUploaderSha256Fingerprint(uploaderSha256Fingerprint); + log.debug("Created AuditEvent with ID {} for Country {} with uploader {} authentication{}", auditEvent, + countryCode, uploaderSha256Fingerprint, authenticationSha256Fingerprint); + DgcMdc.put(MDC_PROP_AUDIT_COUNTRY, countryCode); + DgcMdc.put(MDC_PROP_AUDIT_ID, auditEvent); + log.info("Created AuditEvent"); + auditEventRepository.save(auditEventEntity); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateService.java new file mode 100644 index 00000000..14dc35fe --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateService.java @@ -0,0 +1,157 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.dgc.gateway.client.JrcClient; +import eu.europa.ec.dgc.gateway.model.JrcRatValueset; +import eu.europa.ec.dgc.gateway.model.JrcRatValuesetResponse; +import eu.europa.ec.dgc.gateway.model.RatValueset; +import eu.europa.ec.dgc.gateway.model.Valueset; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import feign.FeignException; +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Optional; +import javax.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class RatValuesetUpdateService { + + private static final String RAT_VALUESET_ID = "covid-19-lab-test-manufacturer-and-name"; + + private static final TypeReference> typeReference = new TypeReference<>() { + }; + + private final ValuesetService valuesetService; + + private final ObjectMapper objectMapper; + + private final JrcClient jrcClient; + + /** + * Setup ObjectMapper to keep Timezone when deserializing. + */ + @PostConstruct + public void setup() { + objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + } + + /** + * Updates the ValueSet for Rapid Antigen Tests. + */ + @Scheduled(fixedDelayString = "${dgc.jrc.interval:21600000}") + @SchedulerLock(name = "rat_valueset_update") + public void update() { + log.info("Starting RAT Valueset update"); + + Optional valueSetJson = valuesetService.getValueSetById(RAT_VALUESET_ID); + Valueset parsedValueset = + new Valueset<>(RAT_VALUESET_ID, LocalDate.now(), new HashMap<>()); + + if (valueSetJson.isPresent()) { + try { + parsedValueset = objectMapper.readValue(valueSetJson.get(), typeReference); + } catch (JsonProcessingException e) { + log.error("Could not parse RatValueSet", e); + } + } + + JrcRatValuesetResponse jrcResponse; + try { + jrcResponse = jrcClient.downloadRatValues(); + } catch (FeignException e) { + log.error("Failed to download RatValueset from JRC", e); + return; + } + + for (JrcRatValueset device : jrcResponse.getDeviceList()) { + JrcRatValueset.HscListHistory latestHistoryEntryNotInFuture = null; + JrcRatValueset.HscListHistory latestHistoryEntry = null; + long now = ZonedDateTime.now().toEpochSecond(); + + if (device.getHscListHistory() != null) { + + latestHistoryEntryNotInFuture = device.getHscListHistory().stream() + .sorted(Comparator + .comparing((JrcRatValueset.HscListHistory x) -> x.getListDate().toEpochSecond()) + .reversed()) + .dropWhile(x -> x.getListDate().toEpochSecond() > now) + .findFirst() + .orElse(null); + + latestHistoryEntry = device.getHscListHistory().stream() + .max(Comparator.comparing(x -> x.getListDate().toEpochSecond())) + .orElse(null); + } + + if (latestHistoryEntry == null) { + DgcMdc.put("valuesetId", device.getIdDevice()); + log.error("Valueset Entry has no history information. Skipping entry."); + DgcMdc.remove("valuesetId"); + } else { + RatValueset valuesetInDb = + parsedValueset.getValue().computeIfAbsent(device.getIdDevice(), s -> new RatValueset()); + + valuesetInDb.setDisplay( + String.format("%s, %s", device.getManufacturer().getName(), device.getCommercialName())); + + if (latestHistoryEntryNotInFuture != null) { + valuesetInDb.setActive(latestHistoryEntryNotInFuture.getInCommonList()); + valuesetInDb.setVersion(latestHistoryEntryNotInFuture.getListDate()); + } else { + valuesetInDb.setActive(null); + valuesetInDb.setVersion(null); + } + + if (latestHistoryEntry.getListDate().toEpochSecond() < now) { + valuesetInDb.setValidUntil(null); + } else { + valuesetInDb.setValidUntil(latestHistoryEntry.getListDate()); + } + } + } + + parsedValueset.setDate(LocalDate.now()); + String updatedValuesetJson; + try { + updatedValuesetJson = objectMapper.writeValueAsString(parsedValueset); + } catch (JsonProcessingException e) { + log.error("Failed to write updated RAT Valueset as String", e); + return; + } + + valuesetService.updateValueSet(RAT_VALUESET_ID, updatedValuesetJson); + log.info("Updating RAT Valueset finished."); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java new file mode 100644 index 00000000..bb5f99ab --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java @@ -0,0 +1,328 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.SignerInformationRepository; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.List; +import java.util.Optional; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.asn1.x500.RDN; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.CertException; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.operator.ContentVerifierProvider; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.RuntimeOperatorException; +import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class SignerInformationService { + + private final TrustedPartyService trustedPartyService; + + private final CertificateUtils certificateUtils; + + private final SignerInformationRepository signerInformationRepository; + + private static final String MDC_PROP_UPLOAD_CERT_THUMBPRINT = "uploadCertThumbprint"; + private static final String MDC_PROP_CSCA_CERT_THUMBPRINT = "cscaCertThumbprint"; + + /** + * Method to query persistence layer for all stored SignerInformation. + * + * @return List of SignerInformation + */ + public List getSignerInformation() { + return signerInformationRepository.findAll(); + } + + /** + * Method to query persistence layer for SignerInformation filtered by Type. + * + * @param type type to filter for + * @return List of SignerInformation + */ + public List getSignerInformation(SignerInformationEntity.CertificateType type) { + return signerInformationRepository.getByCertificateType(type); + } + + /** + * Method to query persistence layer for SignerInformation filtered by Type and Country. + * + * @param countryCode 2-digit country Code to filter for. + * @param type type to filter for + * @return List of SignerInformation + */ + public List getSignerInformation( + String countryCode, + SignerInformationEntity.CertificateType type) { + return signerInformationRepository.getByCertificateTypeAndCountry(type, countryCode); + } + + /** + * Adds a new Trusted Signer Certificate to TrustStore DB. + * + * @param uploadedCertificate the certificate to add + * @param signerCertificate the certificate which was used to sign the message + * @param signature the detached signature of cms message + * @param authenticatedCountryCode the country code of the uploader country from cert authentication + * @throws SignerCertCheckException if validation check has failed. The exception contains + * a reason property with detailed information why the validation has failed. + */ + public SignerInformationEntity addSignerCertificate( + X509CertificateHolder uploadedCertificate, + X509CertificateHolder signerCertificate, + String signature, + String authenticatedCountryCode + ) throws SignerCertCheckException { + + contentCheckUploaderCertificate(signerCertificate, authenticatedCountryCode); + contentCheckCountryOfOrigin(uploadedCertificate, authenticatedCountryCode); + contentCheckCsca(uploadedCertificate, authenticatedCountryCode); + contentCheckAlreadyExists(uploadedCertificate); + contentCheckKidAlreadyExists(uploadedCertificate); + + // All checks passed --> Save to DB + byte[] certRawData; + try { + certRawData = uploadedCertificate.getEncoded(); + } catch (IOException e) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.UPLOAD_FAILED, "Internal Server Error"); + } + + SignerInformationEntity newSignerInformation = new SignerInformationEntity(); + newSignerInformation.setCountry(authenticatedCountryCode); + newSignerInformation.setRawData(Base64.getEncoder().encodeToString(certRawData)); + newSignerInformation.setThumbprint(certificateUtils.getCertThumbprint(uploadedCertificate)); + newSignerInformation.setCertificateType(SignerInformationEntity.CertificateType.DSC); + newSignerInformation.setSignature(signature); + + log.info("Saving new SignerInformation Entity"); + + newSignerInformation = signerInformationRepository.save(newSignerInformation); + + DgcMdc.remove(MDC_PROP_UPLOAD_CERT_THUMBPRINT); + DgcMdc.remove(MDC_PROP_CSCA_CERT_THUMBPRINT); + + return newSignerInformation; + } + + /** + * Deletes a Trusted Signer Certificate from TrustStore DB. + * + * @param uploadedCertificate the certificate to delete + * @param signerCertificate the certificate which was used to sign the message + * @param authenticatedCountryCode the country code of the uploader country from cert authentication + * @throws SignerCertCheckException if validation check has failed. The exception contains + * a reason property with detailed information why the validation has failed. + */ + public void deleteSignerCertificate( + X509CertificateHolder uploadedCertificate, + X509CertificateHolder signerCertificate, + String authenticatedCountryCode + ) throws SignerCertCheckException { + + contentCheckUploaderCertificate(signerCertificate, authenticatedCountryCode); + contentCheckCountryOfOrigin(uploadedCertificate, authenticatedCountryCode); + contentCheckExists(uploadedCertificate); + + log.info("Revoking SignerInformation Entity"); + + // All checks passed --> Delete from DB + signerInformationRepository.deleteByThumbprint(certificateUtils.getCertThumbprint(uploadedCertificate)); + + DgcMdc.remove(MDC_PROP_UPLOAD_CERT_THUMBPRINT); + } + + private void contentCheckUploaderCertificate( + X509CertificateHolder signerCertificate, + String authenticatedCountryCode) throws SignerCertCheckException { + // Content Check Step 1: Uploader Certificate + String signerCertThumbprint = certificateUtils.getCertThumbprint(signerCertificate); + Optional certFromDb = trustedPartyService.getCertificate( + signerCertThumbprint, + authenticatedCountryCode, + TrustedPartyEntity.CertificateType.UPLOAD + ); + + if (certFromDb.isEmpty()) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.UPLOADER_CERT_CHECK_FAILED, + "Could not find upload certificate with hash %s and country %s", + signerCertThumbprint, authenticatedCountryCode); + } + + DgcMdc.put(MDC_PROP_UPLOAD_CERT_THUMBPRINT, signerCertThumbprint); + } + + private void contentCheckCountryOfOrigin(X509CertificateHolder uploadedCertificate, + String authenticatedCountryCode) throws SignerCertCheckException { + + // Content Check Step 2: Country of Origin check + RDN[] uploadedCertCountryProperties = + uploadedCertificate.getSubject().getRDNs(X509ObjectIdentifiers.countryName); + + if (uploadedCertCountryProperties.length > 1) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.COUNTRY_OF_ORIGIN_CHECK_FAILED, + "Uploaded certificate contains more than one country property."); + } else if (uploadedCertCountryProperties.length < 1) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.COUNTRY_OF_ORIGIN_CHECK_FAILED, + "Uploaded certificate contains no country property."); + } + + if (!uploadedCertCountryProperties[0].getFirst().getValue().toString().equals(authenticatedCountryCode)) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.COUNTRY_OF_ORIGIN_CHECK_FAILED, + "Uploaded certificate is not issued for uploader country."); + } + } + + private void contentCheckCsca(X509CertificateHolder uploadedCertificate, + String authenticatedCountryCode) throws SignerCertCheckException { + + // Content Check Step 3: CSCA Check + List trustedCas = + trustedPartyService.getCertificates(authenticatedCountryCode, TrustedPartyEntity.CertificateType.CSCA); + + if (trustedCas.isEmpty()) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.CSCA_CHECK_FAILED, + "CSCA list for country %s is empty", authenticatedCountryCode); + } + + Optional matchingCa = trustedCas.stream() + .dropWhile(ca -> !certificateSignedByCa(uploadedCertificate, ca)) + .findFirst(); + + if (matchingCa.isEmpty()) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.CSCA_CHECK_FAILED, + "Could not verify uploaded certificate was signed by valid CSCA."); + } else { + DgcMdc.put(MDC_PROP_CSCA_CERT_THUMBPRINT, matchingCa.get().getThumbprint()); + } + } + + private void contentCheckAlreadyExists(X509CertificateHolder uploadedCertificate) throws SignerCertCheckException { + + String uploadedCertificateThumbprint = certificateUtils.getCertThumbprint(uploadedCertificate); + Optional signerInformationEntity = + signerInformationRepository.getFirstByThumbprint(uploadedCertificateThumbprint); + + if (signerInformationEntity.isPresent()) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.ALREADY_EXIST_CHECK_FAILED, + "Uploaded certificate already exists"); + } + } + + private void contentCheckKidAlreadyExists(X509CertificateHolder uploadedCertificate) + throws SignerCertCheckException { + + String uploadedCertificateThumbprint = certificateUtils.getCertThumbprint(uploadedCertificate); + // KID is the first 8 byte of hash. So we take the first 16 characters of the hash + String thumbprintKidPart = uploadedCertificateThumbprint.substring(0, 16); + + Optional signerInformationEntity = + signerInformationRepository.getFirstByThumbprintStartsWith(thumbprintKidPart); + + if (signerInformationEntity.isPresent()) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.KID_CHECK_FAILED, + "A certificate with KID of uploaded certificate already exists"); + } + } + + private void contentCheckExists(X509CertificateHolder uploadedCertificate) throws SignerCertCheckException { + + String uploadedCertificateThumbprint = certificateUtils.getCertThumbprint(uploadedCertificate); + Optional signerInformationEntity = + signerInformationRepository.getFirstByThumbprint(uploadedCertificateThumbprint); + + if (signerInformationEntity.isEmpty()) { + throw new SignerCertCheckException(SignerCertCheckException.Reason.EXIST_CHECK_FAILED, + "Uploaded certificate does not exists"); + } + } + + private boolean certificateSignedByCa(X509CertificateHolder certificate, TrustedPartyEntity caCertificateEntity) { + X509Certificate caCertificate = trustedPartyService.getX509CertificateFromEntity(caCertificateEntity); + + ContentVerifierProvider verifier; + try { + verifier = new JcaContentVerifierProviderBuilder().build(caCertificate); + } catch (OperatorCreationException e) { + DgcMdc.put("certHash", caCertificateEntity.getThumbprint()); + log.error("Failed to instantiate JcaContentVerifierProvider from cert"); + return false; + } + + try { + return certificate.isSignatureValid(verifier); + } catch (CertException | RuntimeOperatorException e) { + return false; + } + } + + /** + * Extracts X509Certificate from {@link SignerInformationEntity}. + * + * @param signerInformationEntity entity from which the certificate should be extracted. + * @return X509Certificate representation. + */ + public X509Certificate getX509CertificateFromEntity(SignerInformationEntity signerInformationEntity) { + try { + byte[] rawDataBytes = Base64.getDecoder().decode(signerInformationEntity.getRawData()); + return certificateUtils.convertCertificate(new X509CertificateHolder(rawDataBytes)); + } catch (Exception e) { + log.error("Failed to parse Certificate from SignerInformationEntity", e); + } + + return null; + } + + public static class SignerCertCheckException extends Exception { + + @Getter + private final Reason reason; + + public SignerCertCheckException(Reason reason, String message, Object... args) { + super(String.format(message, args)); + this.reason = reason; + } + + public enum Reason { + UPLOADER_CERT_CHECK_FAILED, + COUNTRY_OF_ORIGIN_CHECK_FAILED, + CSCA_CHECK_FAILED, + ALREADY_EXIST_CHECK_FAILED, + KID_CHECK_FAILED, + EXIST_CHECK_FAILED, + UPLOAD_FAILED + } + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java new file mode 100644 index 00000000..0c40791e --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java @@ -0,0 +1,145 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.model.TrustList; +import eu.europa.ec.dgc.gateway.model.TrustListType; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TrustListService { + + private final TrustedPartyService trustedPartyService; + + private final SignerInformationService signerInformationService; + + private final CertificateUtils certificateUtils; + + /** + * Get a TrustList with TrustList Entries of all type and all country. + * + * @return List of {@link TrustList} ordered by KID + */ + public List getTrustList() { + return mergeAndConvert( + trustedPartyService.getCertificates(), + signerInformationService.getSignerInformation() + ); + } + + /** + * Get a TrustList with TrustList Entries of all countries filtered by type. + * + * @param type the type to filter for. + * @return List of {@link TrustList} ordered by KID + */ + public List getTrustList(TrustListType type) { + if (type == TrustListType.DSC) { + return mergeAndConvert( + Collections.emptyList(), + signerInformationService.getSignerInformation(SignerInformationEntity.CertificateType.DSC) + ); + } else { + return mergeAndConvert( + trustedPartyService.getCertificates(map(type)), + Collections.emptyList() + ); + } + } + + /** + * Get a TrustList with TrustList Entries filtered by countriy and type. + * + * @param type the type to filter for. + * @param countryCode the 2-Digit country code to filter for. + * @return List of {@link TrustList} ordered by KID + */ + public List getTrustList(TrustListType type, String countryCode) { + if (type == TrustListType.DSC) { + return mergeAndConvert( + Collections.emptyList(), + signerInformationService.getSignerInformation(countryCode, SignerInformationEntity.CertificateType.DSC) + ); + } else { + return mergeAndConvert( + trustedPartyService.getCertificates(countryCode, map(type)), + Collections.emptyList() + ); + } + } + + private List mergeAndConvert( + List trustedPartyList, + List signerInformationList) { + + return Stream.concat( + trustedPartyList.stream().map(this::convert), + signerInformationList.stream().map(this::convert) + ) + .sorted(Comparator.comparing(TrustList::getKid)) + .collect(Collectors.toList()); + } + + private TrustList convert(TrustedPartyEntity trustedPartyEntity) { + return new TrustList( + certificateUtils.getCertKid(trustedPartyService.getX509CertificateFromEntity(trustedPartyEntity)), + trustedPartyEntity.getCreatedAt(), + trustedPartyEntity.getCountry(), + map(trustedPartyEntity.getCertificateType()), + trustedPartyEntity.getThumbprint(), + trustedPartyEntity.getSignature(), + trustedPartyEntity.getRawData() + ); + } + + private TrustList convert(SignerInformationEntity signerInformationEntity) { + return new TrustList( + certificateUtils.getCertKid(signerInformationService.getX509CertificateFromEntity(signerInformationEntity)), + signerInformationEntity.getCreatedAt(), + signerInformationEntity.getCountry(), + map(signerInformationEntity.getCertificateType()), + signerInformationEntity.getThumbprint(), + signerInformationEntity.getSignature(), + signerInformationEntity.getRawData() + ); + } + + private TrustedPartyEntity.CertificateType map(TrustListType type) { + return TrustedPartyEntity.CertificateType.valueOf(type.name()); + } + + private TrustListType map(Enum type) { + return TrustListType.valueOf(type.name()); + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java new file mode 100644 index 00000000..7d55b5f3 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java @@ -0,0 +1,203 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.TrustedPartyRepository; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import eu.europa.ec.dgc.signing.SignedCertificateMessageParser; +import eu.europa.ec.dgc.signing.SignedMessageParser; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.cert.X509CertificateHolder; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TrustedPartyService { + + private static final String MDC_PROP_CERT_THUMBPRINT = "certVerifyThumbprint"; + private static final String MDC_PROP_PARSER_STATE = "parserState"; + private final TrustedPartyRepository trustedPartyRepository; + private final KeyStore trustAnchorKeyStore; + private final DgcConfigProperties dgcConfigProperties; + private final CertificateUtils certificateUtils; + + /** + * Method to query the db for all certificates. + * + * @return List holding the found certificates. + */ + public List getCertificates() { + + return trustedPartyRepository.findAll() + .stream() + .filter(this::validateCertificateIntegrity) + .collect(Collectors.toList()); + } + + /** + * Method to query the db for certificates by type. + * + * @param type type to filter for. + * @return List holding the found certificates. + */ + public List getCertificates(TrustedPartyEntity.CertificateType type) { + + return trustedPartyRepository.getByCertificateType(type) + .stream() + .filter(this::validateCertificateIntegrity) + .collect(Collectors.toList()); + } + + /** + * Method to query the db for certificates. + * + * @param country country of certificate. + * @param type type of certificate. + * @return List holding the found certificates. + */ + public List getCertificates(String country, TrustedPartyEntity.CertificateType type) { + + return trustedPartyRepository.getByCountryAndCertificateType(country, type) + .stream() + .filter(this::validateCertificateIntegrity) + .collect(Collectors.toList()); + } + + /** + * Method to query the db for a certificate. + * + * @param thumbprint RSA-256 thumbprint of certificate. + * @param country country of certificate. + * @param type type of certificate. + * @return Optional holding the certificate if found. + */ + public Optional getCertificate( + String thumbprint, String country, TrustedPartyEntity.CertificateType type) { + + return trustedPartyRepository.getFirstByThumbprintAndCountryAndCertificateType(thumbprint, country, type) + .map(trustedPartyEntity -> validateCertificateIntegrity(trustedPartyEntity) ? trustedPartyEntity : null); + } + + /** + * Returns a list of onboarded countries. + * + * @return List of String. + */ + public List getCountryList() { + return trustedPartyRepository.getCountryCodeList(); + } + + private boolean validateCertificateIntegrity(TrustedPartyEntity trustedPartyEntity) { + + DgcMdc.put(MDC_PROP_CERT_THUMBPRINT, trustedPartyEntity.getThumbprint()); + + // check if entity has signature and certificate information + if (trustedPartyEntity.getSignature() == null || trustedPartyEntity.getSignature().isEmpty() + || trustedPartyEntity.getRawData() == null || trustedPartyEntity.getRawData().isEmpty()) { + log.error("Certificate entity does not contain raw certificate or certificate signature."); + return false; + } + + // check if raw data contains a x509 certificate + X509Certificate x509Certificate = getX509CertificateFromEntity(trustedPartyEntity); + if (x509Certificate == null) { + log.error("Raw certificate data does not contain a valid x509Certificate."); + return false; + } + + // verify if thumbprint in database matches the certificate in raw data + if (!verifyThumbprintMatchesCertificate(trustedPartyEntity, x509Certificate)) { + log.error("Thumbprint in database does not match thumbprint of stored certificate."); + return false; + } + + // load DGCG Trust Anchor PublicKey from KeyStore + X509CertificateHolder trustAnchor = null; + try { + trustAnchor = certificateUtils.convertCertificate((X509Certificate) trustAnchorKeyStore.getCertificate( + dgcConfigProperties.getTrustAnchor().getCertificateAlias())); + } catch (KeyStoreException | CertificateEncodingException | IOException e) { + log.error("Could not load DGCG-TrustAnchor from KeyStore.", e); + return false; + } + + // verify certificate signature + SignedCertificateMessageParser parser = + new SignedCertificateMessageParser(trustedPartyEntity.getSignature(), trustedPartyEntity.getRawData()); + + if (parser.getParserState() != SignedMessageParser.ParserState.SUCCESS) { + DgcMdc.put(MDC_PROP_PARSER_STATE, parser.getParserState().name()); + log.error("TrustAnchor Verification failed."); + return false; + } + + if (!parser.isSignatureVerified()) { + log.error("TrustAnchor Verification failed: Signature is not matching signed certificate"); + return false; + } + + if (!parser.getSigningCertificate().equals(trustAnchor)) { + log.error("TrustAnchor Verification failed: Certificate was not signed by known TrustAnchor"); + return false; + } + + return true; + } + + + /** + * Extracts X509Certificate from {@link TrustedPartyEntity}. + * + * @param trustedPartyEntity entity from which the certificate should be extraced. + * @return X509Certificate representation. + */ + public X509Certificate getX509CertificateFromEntity(TrustedPartyEntity trustedPartyEntity) { + try { + byte[] rawDataBytes = Base64.getDecoder().decode(trustedPartyEntity.getRawData()); + return certificateUtils.convertCertificate(new X509CertificateHolder(rawDataBytes)); + } catch (Exception e) { + log.error("Raw certificate data does not contain a valid x509Certificate", e); + } + + return null; + } + + private boolean verifyThumbprintMatchesCertificate( + TrustedPartyEntity trustedPartyEntity, X509Certificate certificate) { + String certHash = certificateUtils.getCertThumbprint(certificate); + + return certHash != null && certHash.equals(trustedPartyEntity.getThumbprint()); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/ValidationRuleService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/ValidationRuleService.java new file mode 100644 index 00000000..09cb0c1b --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/ValidationRuleService.java @@ -0,0 +1,405 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vdurmont.semver4j.Semver; +import eu.europa.ec.dgc.gateway.config.ValidationRuleSchemaProvider; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.entity.ValidationRuleEntity; +import eu.europa.ec.dgc.gateway.model.ParsedValidationRule; +import eu.europa.ec.dgc.gateway.repository.ValidationRuleRepository; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.cert.X509CertificateHolder; +import org.everit.json.schema.Schema; +import org.everit.json.schema.ValidationException; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ValidationRuleService { + + private final ValidationRuleRepository validationRuleRepository; + + private final CertificateUtils certificateUtils; + + private final TrustedPartyService trustedPartyService; + + private final ValidationRuleSchemaProvider validationRuleSchemaProvider; + + private final ObjectMapper objectMapper; + + private static final String MDC_PROP_UPLOAD_CERT_THUMBPRINT = "uploadCertThumbprint"; + + /** + * Queries the database for active Validation Rules filtered by country. + * If the latest Validation Rule's validity is in future the whole version history of rules between the currently + * active and the latest rule will be returned. + * + * @param country 2 Digit Country Code + * @return List of ValidationRule Entities. + */ + public List getActiveValidationRules(String country) { + + // Getting the IDs of the latest ValidationRuleVersion of each Rule ID. + return validationRuleRepository.getLatestIds(country).stream() + // Getting the corresponding entity + .map(validationRuleRepository::findById) + // Resolve Optional + .map(Optional::get) + // Check if version history is needed for each rule + .map(rule -> { + if (ZonedDateTime.now().isAfter(rule.getValidFrom())) { + // rule already valid - only return this one + return Collections.singletonList(rule); + } else { + // Rule is valid in future, history of this rule is required + + // Get the ID of the first Entity with this RuleId and ValidFrom is Before now. + List ids = validationRuleRepository.getIdByValidFromIsBeforeAndRuleIdIs( + ZonedDateTime.now(), rule.getRuleId(), PageRequest.of(0, 1)); + + if (ids.isEmpty()) { + // Rule has no older but currently valid version + // --> return all rules with valid from is greater than today + + return validationRuleRepository.getByRuleIdAndValidFromIsGreaterThanEqualOrderByIdDesc( + rule.getRuleId(), + ZonedDateTime.now() + ); + } else { + // Return al previous versions and rule itself. + return validationRuleRepository + .getByIdIsGreaterThanEqualAndRuleIdIsOrderByIdDesc(ids.get(0), rule.getRuleId()); + } + } + }) + // flatten the 2 dimensional list + .flatMap(Collection::stream) + // return as one dimensional list + .collect(Collectors.toList()); + } + + /** + * Deletes all rules with given ID. + * + * @param ruleId to delete + * @return amount of deleted entities. + */ + public int deleteByRuleId(String ruleId) { + return validationRuleRepository.deleteByRuleId(ruleId); + } + + /** + * Gets the 2 Digit Country Code from a ID String (e.g. GR-EU-13330 -> EU) + * + * @param idString the idString to parse + * @return the 2 digit country code or null if parsing has failed. + */ + public String getCountryCodeFromIdString(String idString) { + String[] parts = idString.split("-"); + + if (parts.length != 3) { + return null; + } + + if (parts[1].length() != 2) { + return null; + } + + return parts[1]; + } + + /** + * Adds a new Validation Rule DB. + * + * @param uploadedRule the JSON String with the uploaded rule. + * @param signerCertificate the certificate which was used to sign the message + * @param cms the cms containing the JSON + * @param authenticatedCountryCode the country code of the uploader country from cert authentication + * @throws ValidationRuleCheckException if validation check has failed. The exception contains + * a reason property with detailed information why the validation has failed. + */ + public ValidationRuleEntity addValidationRule( + String uploadedRule, + X509CertificateHolder signerCertificate, + String cms, + String authenticatedCountryCode + ) throws ValidationRuleCheckException { + + contentCheckUploaderCertificate(signerCertificate, authenticatedCountryCode); + ParsedValidationRule parsedValidationRule = contentCheckValidJson(uploadedRule); + + ValidationRuleEntity.ValidationRuleType validationRuleType = + parsedValidationRule.getType().equals("Acceptance") ? ValidationRuleEntity.ValidationRuleType.ACCEPTANCE + : ValidationRuleEntity.ValidationRuleType.INVALIDATION; + + contentCheckRuleIdPrefixMatchCertificateType(parsedValidationRule); + contentCheckRuleIdPrefixMatchType(parsedValidationRule, validationRuleType); + contentCheckUploaderCountry(parsedValidationRule, authenticatedCountryCode); + Optional latestValidationRule = contentCheckVersion(parsedValidationRule); + contentCheckTimestamps(parsedValidationRule, validationRuleType, latestValidationRule); + + // All checks passed --> Save to DB + ValidationRuleEntity newValidationRule = new ValidationRuleEntity(); + newValidationRule.setValidationRuleType(validationRuleType); + newValidationRule.setCountry(parsedValidationRule.getCountry()); + newValidationRule.setRuleId(parsedValidationRule.getIdentifier()); + newValidationRule.setValidTo(parsedValidationRule.getValidTo()); + newValidationRule.setValidFrom(parsedValidationRule.getValidFrom()); + newValidationRule.setCms(cms); + newValidationRule.setVersion(parsedValidationRule.getVersion()); + + log.info("Saving new ValidationRule Entity"); + + newValidationRule = validationRuleRepository.save(newValidationRule); + + DgcMdc.remove(MDC_PROP_UPLOAD_CERT_THUMBPRINT); + + return newValidationRule; + } + + private void contentCheckRuleIdPrefixMatchCertificateType(ParsedValidationRule parsedValidationRule) + throws ValidationRuleCheckException { + + Map mapping = Map.of( + "TR", "Test", + "VR", "Vaccination", + "RR", "Recovery", + "GR", "General" + ); + + for (Map.Entry entry : mapping.entrySet()) { + if (parsedValidationRule.getIdentifier().startsWith(entry.getKey()) + && !parsedValidationRule.getCertificateType().equals(entry.getValue())) { + + throw new ValidationRuleCheckException(ValidationRuleCheckException.Reason.INVALID_RULE_ID, + String.format("ID must start with %s for %s Rules", entry.getKey(), entry.getValue())); + } + } + } + + private void contentCheckRuleIdPrefixMatchType( + ParsedValidationRule parsedValidationRule, ValidationRuleEntity.ValidationRuleType type) + throws ValidationRuleCheckException { + + if (parsedValidationRule.getIdentifier().startsWith("IR") + && type == ValidationRuleEntity.ValidationRuleType.ACCEPTANCE) { + throw new ValidationRuleCheckException(ValidationRuleCheckException.Reason.INVALID_RULE_ID, + "Acceptance Rule Rule-ID requires prefix other than IR."); + } + + if (!parsedValidationRule.getIdentifier().startsWith("IR") + && type == ValidationRuleEntity.ValidationRuleType.INVALIDATION) { + throw new ValidationRuleCheckException(ValidationRuleCheckException.Reason.INVALID_RULE_ID, + "Invalidation Rule Rule-ID requires IR prefix."); + } + } + + private Optional contentCheckVersion(ParsedValidationRule parsedValidationRule) + throws ValidationRuleCheckException { + // Get latest version in DB + Optional latestValidationRule = + validationRuleRepository.getFirstByRuleIdOrderByIdDesc(parsedValidationRule.getIdentifier()); + + if (latestValidationRule.isEmpty()) { + return latestValidationRule; + } + + Semver latestVersion = new Semver(latestValidationRule.get().getVersion()); + Semver uploadedVersion = new Semver(parsedValidationRule.getVersion()); + + if (uploadedVersion.isLowerThanOrEqualTo(latestVersion)) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_VERSION, + "Version of new rule (%s) needs to be greater then old version (%s)", uploadedVersion, latestVersion + ); + } + + return latestValidationRule; + } + + private void contentCheckTimestamps( + ParsedValidationRule parsedValidationRule, + ValidationRuleEntity.ValidationRuleType type, + Optional latestValidationRule) throws ValidationRuleCheckException { + + if (!parsedValidationRule.getValidTo().isAfter(parsedValidationRule.getValidFrom())) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_TIMESTAMP, + "ValidFrom (%s) needs to be before ValidTo (%s).", + parsedValidationRule.getValidFrom().toString(), + parsedValidationRule.getValidTo().toString()); + } + + if (parsedValidationRule.getValidFrom().isAfter(ZonedDateTime.now().plus(2, ChronoUnit.WEEKS))) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_TIMESTAMP, + "ValidFrom (%s) cannot be more than 2 weeks in future.", + parsedValidationRule.getValidFrom().toString()); + } + + if (type == ValidationRuleEntity.ValidationRuleType.ACCEPTANCE + && parsedValidationRule.getValidFrom() + .isBefore(ZonedDateTime.now().plus(48, ChronoUnit.HOURS))) { + + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_TIMESTAMP, + "ValidFrom (%s) needs to be at least 48h in future for Acceptance Validation Rules", + parsedValidationRule.getValidFrom().toString()); + } + + if (type == ValidationRuleEntity.ValidationRuleType.INVALIDATION + && parsedValidationRule.getValidFrom().isBefore(ZonedDateTime.now())) { + + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_TIMESTAMP, + "ValidFrom (%s) needs to be in future for Invalidation Rules", + parsedValidationRule.getValidFrom().toString()); + } + + if (parsedValidationRule.getValidFrom().plus(3, ChronoUnit.DAYS) + .isAfter(parsedValidationRule.getValidTo())) { + + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_TIMESTAMP, + "Rule Validity must be at least 72h but is %dh", + ChronoUnit.HOURS.between(parsedValidationRule.getValidFrom(), parsedValidationRule.getValidTo())); + } + + if (latestValidationRule.isPresent() + && parsedValidationRule.getValidFrom().isBefore(latestValidationRule.get().getValidFrom())) { + + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_TIMESTAMP, + "ValidFrom (%s) needs to be after or equal to ValidFrom (%s) of previous version of the rule.", + parsedValidationRule.getValidFrom().toString(), latestValidationRule.get().getValidFrom().toString()); + } + } + + private void contentCheckUploaderCountry(ParsedValidationRule parsedValidationRule, String countryCode) + throws ValidationRuleCheckException { + if (!parsedValidationRule.getCountry().equals(countryCode)) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_COUNTRY, + "Country does not match your authentication."); + } + + if (!getCountryCodeFromIdString(parsedValidationRule.getIdentifier()).equals(countryCode)) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_COUNTRY, + "Country Code in Identifier does not match country."); + } + } + + private ParsedValidationRule contentCheckValidJson(String json) throws ValidationRuleCheckException { + Schema validationSchema = validationRuleSchemaProvider.getValidationRuleSchema(); + + try { + JSONObject jsonObject = new JSONObject(json); + validationSchema.validate(jsonObject); + } catch (JSONException e) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_JSON, + "JSON could not be parsed"); + } catch (ValidationException validationException) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_JSON, + "JSON does not align to Validation Rule Schema: %s", + String.join(", ", validationException.getAllMessages())); + } + + try { + objectMapper.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true); + return objectMapper.readValue(json, ParsedValidationRule.class); + } catch (JsonProcessingException e) { + throw new ValidationRuleCheckException( + ValidationRuleCheckException.Reason.INVALID_JSON, + "JSON could not be parsed"); + } + } + + /** + * Checks a given UploadCertificate if it exists in the database and is assigned to given CountryCode. + * + * @param signerCertificate Upload Certificate + * @param authenticatedCountryCode Country Code. + * @throws ValidationRuleCheckException if Validation fails. + */ + public void contentCheckUploaderCertificate( + X509CertificateHolder signerCertificate, + String authenticatedCountryCode) throws ValidationRuleCheckException { + // Content Check Step 1: Uploader Certificate + String signerCertThumbprint = certificateUtils.getCertThumbprint(signerCertificate); + Optional certFromDb = trustedPartyService.getCertificate( + signerCertThumbprint, + authenticatedCountryCode, + TrustedPartyEntity.CertificateType.UPLOAD + ); + + if (certFromDb.isEmpty()) { + throw new ValidationRuleCheckException(ValidationRuleCheckException.Reason.UPLOADER_CERT_CHECK_FAILED, + "Could not find upload certificate with hash %s and country %s", + signerCertThumbprint, authenticatedCountryCode); + } + + DgcMdc.put(MDC_PROP_UPLOAD_CERT_THUMBPRINT, signerCertThumbprint); + } + + public static class ValidationRuleCheckException extends Exception { + + @Getter + private final Reason reason; + + public ValidationRuleCheckException(Reason reason, String message, Object... args) { + super(String.format(message, args)); + this.reason = reason; + } + + public enum Reason { + INVALID_JSON, + INVALID_COUNTRY, + INVALID_TIMESTAMP, + INVALID_VERSION, + INVALID_RULE_ID, + UPLOADER_CERT_CHECK_FAILED, + } + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/ValuesetService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/ValuesetService.java new file mode 100644 index 00000000..084c4abc --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/ValuesetService.java @@ -0,0 +1,84 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.ValuesetEntity; +import eu.europa.ec.dgc.gateway.repository.ValuesetRepository; +import eu.europa.ec.dgc.gateway.utils.DgcMdc; +import java.util.List; +import java.util.Optional; +import javax.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ValuesetService { + + private final ValuesetRepository valuesetRepository; + + /** + * Gets a list of existing valueset IDs. + * + * @return List of Strings + */ + public List getValuesetIds() { + log.info("Getting ValueSet IDs"); + + return valuesetRepository.getIds(); + } + + /** + * Gets the content of a valueset by its id. + * + * @param id id of the valueset + * @return the json content + */ + public Optional getValueSetById(String id) { + DgcMdc.put("valueSetId", id); + log.info("Requesting Value Set."); + + return valuesetRepository.findById(id).map(ValuesetEntity::getJson); + } + + /** + * Sets the JSON-Value of a ValueSet. + * If a Valueset with the given ID does not exists it will be created. + * + * @param id ValueSet-ID + * @param value JSON String containing Valueset. + */ + @Transactional + public void updateValueSet(String id, String value) { + log.info("Updating Valueset."); + + Optional valuesetEntityOptional = valuesetRepository.findById(id); + + ValuesetEntity valuesetEntity = valuesetEntityOptional.orElse(new ValuesetEntity()); + valuesetEntity.setId(id); + valuesetEntity.setJson(value); + + valuesetRepository.save(valuesetEntity); + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/utils/DgcMdc.java b/src/main/java/eu/europa/ec/dgc/gateway/utils/DgcMdc.java new file mode 100644 index 00000000..db7312d7 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/utils/DgcMdc.java @@ -0,0 +1,112 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.utils; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.slf4j.MDC; + +/** + * Wrapper for MDC to escape values for better log files. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DgcMdc { + + /** + * Put a diagnostic context value (the value parameter) as identified with the + * key parameter into the current thread's diagnostic context map. The + * key parameter cannot be null. The value parameter + * can be null only if the underlying implementation supports it. + * + *

This method delegates all work to the MDC of the underlying logging system. + * + * @param key non-null key + * @param value value to put in the map + * @throws IllegalArgumentException in case the "key" parameter is null + */ + public static void put(String key, String value) { + value = value == null ? ">>null<<" : value; + + value = value.replace("\"", "'"); + MDC.put(key, "\"" + value + "\""); + } + + /** + * Put a diagnostic context value (the value parameter) as identified with the + * key parameter into the current thread's diagnostic context map. The + * key parameter cannot be null. The value parameter + * can be null only if the underlying implementation supports it. + * + *

This method delegates all work to the MDC of the underlying logging system. + * + * @param key non-null key + * @param value a numeric value to put in the map + * @throws IllegalArgumentException in case the "key" parameter is null + */ + public static void put(String key, long value) { + put(key, String.valueOf(value)); + } + + /** + * Put a diagnostic context value (the value parameter) as identified with the + * key parameter into the current thread's diagnostic context map. The + * key parameter cannot be null. The value parameter + * can be null only if the underlying implementation supports it. + * + *

This method delegates all work to the MDC of the underlying logging system. + * + * @param key non-null key + * @param value a date value to put in the map + * @throws IllegalArgumentException in case the "key" parameter is null + */ + public static void put(String key, Date value) { + ZonedDateTime timestamp = ZonedDateTime.ofInstant(value.toInstant(), ZoneOffset.UTC); + + put( + key, + timestamp.format(DateTimeFormatter.ISO_INSTANT) + ); + } + + /** + * Remove the diagnostic context identified by the key parameter using + * the underlying system's MDC implementation. The key parameter + * cannot be null. This method does nothing if there is no previous value + * associated with key. + * + * @param key non-null key + * @throws IllegalArgumentException in case the "key" parameter is null + */ + public static void remove(String key) { + MDC.remove(key); + } + + /** + * Clear all entries in the MDC of the underlying implementation. + */ + public static void clear() { + MDC.clear(); + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 00000000..d8c6d0f8 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,14 @@ +spring: + h2: + console: + enabled: true + path: /h2-console +springdoc: + api-docs: + path: /api/docs + enabled: true + swagger-ui: + path: /swagger +dgc: + trustAnchor: + keyStorePath: classpath:keystore/dgc-ta.jks diff --git a/src/main/resources/application-h2.yml b/src/main/resources/application-h2.yml new file mode 100644 index 00000000..a25b168f --- /dev/null +++ b/src/main/resources/application-h2.yml @@ -0,0 +1,9 @@ +spring: + datasource: + jndi-name: false + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; + username: sa + password: '' + jpa: + database-platform: org.hibernate.dialect.H2Dialect diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 00000000..d8a239f6 --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,6 @@ +springdoc: + api-docs: + path: /api/docs + enabled: true + swagger-ui: + path: /swagger diff --git a/src/main/resources/application-mysql.yml b/src/main/resources/application-mysql.yml new file mode 100644 index 00000000..8fdafc86 --- /dev/null +++ b/src/main/resources/application-mysql.yml @@ -0,0 +1,9 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/dgc + username: sa + password: sa + driver-class-name: com.mysql.cj.jdbc.Driver + jndi-name: false + jpa: + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 00000000..558d7801 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,48 @@ +server: + port: 8090 +spring: + profiles: + group: + "dev": "h2" + application: + name: eu-digital-green-certificates-gateway + datasource: + jndi-name: jdbc/dgc + jpa: + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + hibernate: + ddl-auto: validate + liquibase: + enabled: true + change-log: classpath:db/changelog.xml + task: + scheduling: + pool: + size: 5 +management: + endpoints: + web: + exposure: + exclude: "*" + server: + port: -1 +dgc: + jrc: + url: https://covid-19-diagnostics.jrc.ec.europa.eu/devices/hsc-common-recognition-rat + proxy: + host: ${https.proxyHost:} + port: ${https.proxyPort:-1} + username: ${https.proxyUser:} + password: ${https.proxyPassword:} + validationRuleSchema: classpath:validation-rule.schema.json + trustAnchor: + keyStorePath: /ec/prod/app/san/dgc/dgc-ta.jks + keyStorePass: dgc-p4ssw0rd + certificateAlias: dgc_trust_anchor + cert-auth: + header-fields: + thumbprint: X-SSL-Client-SHA256 + distinguished-name: X-SSL-Client-DN +springdoc: + api-docs: + enabled: false diff --git a/src/main/resources/db/changelog.xml b/src/main/resources/db/changelog.xml new file mode 100644 index 00000000..25ee75b7 --- /dev/null +++ b/src/main/resources/db/changelog.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/add-shedlock-table.xml b/src/main/resources/db/changelog/add-shedlock-table.xml new file mode 100644 index 00000000..3f9095d6 --- /dev/null +++ b/src/main/resources/db/changelog/add-shedlock-table.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/add-unique-constraints.xml b/src/main/resources/db/changelog/add-unique-constraints.xml new file mode 100644 index 00000000..8d454a0f --- /dev/null +++ b/src/main/resources/db/changelog/add-unique-constraints.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/add-validation-rule-table.xml b/src/main/resources/db/changelog/add-validation-rule-table.xml new file mode 100644 index 00000000..8ac6ff23 --- /dev/null +++ b/src/main/resources/db/changelog/add-validation-rule-table.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/add-valueset-table.xml b/src/main/resources/db/changelog/add-valueset-table.xml new file mode 100644 index 00000000..4568bfa1 --- /dev/null +++ b/src/main/resources/db/changelog/add-valueset-table.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/create-audit-table.xml b/src/main/resources/db/changelog/create-audit-table.xml new file mode 100644 index 00000000..630a6fc0 --- /dev/null +++ b/src/main/resources/db/changelog/create-audit-table.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/fix-certificate-thumbprints.xml b/src/main/resources/db/changelog/fix-certificate-thumbprints.xml new file mode 100644 index 00000000..ce51f139 --- /dev/null +++ b/src/main/resources/db/changelog/fix-certificate-thumbprints.xml @@ -0,0 +1,20 @@ + + + + + + UPDATE trusted_party SET thumbprint = concat('00', thumbprint) WHERE length(thumbprint) = 62; + + + + UPDATE signer_information SET thumbprint = concat('00', thumbprint) WHERE length(thumbprint) = 62; + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/increase-column-size-for-valueset.xml b/src/main/resources/db/changelog/increase-column-size-for-valueset.xml new file mode 100644 index 00000000..605cf2c3 --- /dev/null +++ b/src/main/resources/db/changelog/increase-column-size-for-valueset.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/init-tables.xml b/src/main/resources/db/changelog/init-tables.xml new file mode 100644 index 00000000..0709924b --- /dev/null +++ b/src/main/resources/db/changelog/init-tables.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/snapshot.json b/src/main/resources/db/snapshot.json new file mode 100644 index 00000000..03c99253 --- /dev/null +++ b/src/main/resources/db/snapshot.json @@ -0,0 +1,731 @@ +{ + "snapshot": { + "created": "2021-06-28T15:18:17.131", + "database": { + "productVersion": "2021.1.2", + "shortName": "intellijPsiClass", + "majorVersion": "0", + "minorVersion": "0", + "user": "A34636994", + "productName": "JPA Buddy Intellij", + "url": "jpab?generationContext=60d05a16-aca2-4aeb-9823-8d67f6b942f1" + }, + "metadata": { + "generationContext": { + "dbmsType": "mysql" + } + }, + "objects": { + "liquibase.structure.core.Catalog": [ + { + "catalog": { + "default": true, + "name": "JPA_BUDDY", + "snapshotId": "ff23152" + } + } + ], + "liquibase.structure.core.Column": [ + { + "column": { + "certainDataType": false, + "name": "authentication_sha256_fingerprint", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23153", + "snapshotId": "ff23159", + "type": { + "columnSize": "64!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "certificate_type", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23163", + "snapshotId": "ff23172", + "type": { + "columnSize": "255!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "certificate_type", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23191", + "snapshotId": "ff23200", + "type": { + "columnSize": "255!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "country", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23153", + "snapshotId": "ff23157", + "type": { + "columnSize": "2!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "country", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23163", + "snapshotId": "ff23169", + "type": { + "columnSize": "2!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "country", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23191", + "snapshotId": "ff23197", + "type": { + "columnSize": "2!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "country", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23183", + "type": { + "columnSize": "2!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "created_at", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23163", + "snapshotId": "ff23168", + "type": { + "typeName": "DATETIME" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "created_at", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23191", + "snapshotId": "ff23196", + "type": { + "typeName": "DATETIME" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "created_at", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23177", + "type": { + "typeName": "DATETIME" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "description", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23153", + "snapshotId": "ff23161", + "type": { + "columnSize": "64!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "event", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23153", + "snapshotId": "ff23160", + "type": { + "columnSize": "64!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "autoIncrementInformation": { + "incrementBy": "1!{java.math.BigInteger}", + "startWith": "1!{java.math.BigInteger}" + }, + "certainDataType": false, + "name": "id", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23153", + "snapshotId": "ff23155", + "type": { + "typeName": "BIGINT" + } + } + }, + { + "column": { + "autoIncrementInformation": { + "incrementBy": "1!{java.math.BigInteger}", + "startWith": "1!{java.math.BigInteger}" + }, + "certainDataType": false, + "name": "id", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23163", + "snapshotId": "ff23167", + "type": { + "typeName": "BIGINT" + } + } + }, + { + "column": { + "autoIncrementInformation": { + "incrementBy": "1!{java.math.BigInteger}", + "startWith": "1!{java.math.BigInteger}" + }, + "certainDataType": false, + "name": "id", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23191", + "snapshotId": "ff23195", + "type": { + "typeName": "BIGINT" + } + } + }, + { + "column": { + "autoIncrementInformation": { + "incrementBy": "1!{java.math.BigInteger}", + "startWith": "1!{java.math.BigInteger}" + }, + "certainDataType": false, + "name": "id", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23176", + "type": { + "typeName": "BIGINT" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "id", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23186", + "snapshotId": "ff23188", + "type": { + "columnSize": "100!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "json", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23186", + "snapshotId": "ff23189", + "type": { + "columnSize": "1024000!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "raw_data", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23163", + "snapshotId": "ff23170", + "type": { + "columnSize": "4096!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "raw_data", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23191", + "snapshotId": "ff23198", + "type": { + "columnSize": "4096!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "rule_id", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23178", + "type": { + "columnSize": "100!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "signature", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23163", + "snapshotId": "ff23171", + "type": { + "columnSize": "6000!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "signature", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23191", + "snapshotId": "ff23199", + "type": { + "columnSize": "6000!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "signature", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23179", + "type": { + "columnSize": "10000!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "thumbprint", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23163", + "snapshotId": "ff23165", + "type": { + "columnSize": "64!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "thumbprint", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23191", + "snapshotId": "ff23193", + "type": { + "columnSize": "64!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "timestamp", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23153", + "snapshotId": "ff23156", + "type": { + "typeName": "DATETIME" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "type", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23184", + "type": { + "columnSize": "255!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "uploader_sha256_fingerprint", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23153", + "snapshotId": "ff23158", + "type": { + "columnSize": "64!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "valid_from", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23180", + "type": { + "typeName": "DATETIME" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "valid_to", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23181", + "type": { + "typeName": "DATETIME" + } + } + }, + { + "column": { + "certainDataType": false, + "name": "version", + "nullable": false, + "relation": "liquibase.structure.core.Table#ff23174", + "snapshotId": "ff23182", + "type": { + "columnSize": "30!{java.lang.Integer}", + "typeName": "VARCHAR" + } + } + } + ], + "liquibase.structure.core.Index": [ + { + "index": { + "columns": [ + "liquibase.structure.core.Column#ff23155" + ], + "name": "IX_PK_AUDIT_EVENT", + "snapshotId": "ff23154", + "table": "liquibase.structure.core.Table#ff23153", + "unique": true + } + }, + { + "index": { + "columns": [ + "liquibase.structure.core.Column#ff23167" + ], + "name": "IX_PK_SIGNER_INFORMATION", + "snapshotId": "ff23166", + "table": "liquibase.structure.core.Table#ff23163", + "unique": true + } + }, + { + "index": { + "columns": [ + "liquibase.structure.core.Column#ff23195" + ], + "name": "IX_PK_TRUSTED_PARTY", + "snapshotId": "ff23194", + "table": "liquibase.structure.core.Table#ff23191", + "unique": true + } + }, + { + "index": { + "columns": [ + "liquibase.structure.core.Column#ff23176" + ], + "name": "IX_PK_VALIDATION_RULE", + "snapshotId": "ff23175", + "table": "liquibase.structure.core.Table#ff23174", + "unique": true + } + }, + { + "index": { + "columns": [ + "liquibase.structure.core.Column#ff23188" + ], + "name": "IX_PK_VALUESET", + "snapshotId": "ff23187", + "table": "liquibase.structure.core.Table#ff23186", + "unique": true + } + } + ], + "liquibase.structure.core.PrimaryKey": [ + { + "primaryKey": { + "backingIndex": "liquibase.structure.core.Index#ff23154", + "columns": [ + "liquibase.structure.core.Column#ff23155" + ], + "name": "PK_AUDIT_EVENT", + "snapshotId": "ff23162", + "table": "liquibase.structure.core.Table#ff23153" + } + }, + { + "primaryKey": { + "backingIndex": "liquibase.structure.core.Index#ff23166", + "columns": [ + "liquibase.structure.core.Column#ff23167" + ], + "name": "PK_SIGNER_INFORMATION", + "snapshotId": "ff23173", + "table": "liquibase.structure.core.Table#ff23163" + } + }, + { + "primaryKey": { + "backingIndex": "liquibase.structure.core.Index#ff23194", + "columns": [ + "liquibase.structure.core.Column#ff23195" + ], + "name": "PK_TRUSTED_PARTY", + "snapshotId": "ff23201", + "table": "liquibase.structure.core.Table#ff23191" + } + }, + { + "primaryKey": { + "backingIndex": "liquibase.structure.core.Index#ff23175", + "columns": [ + "liquibase.structure.core.Column#ff23176" + ], + "name": "PK_VALIDATION_RULE", + "snapshotId": "ff23185", + "table": "liquibase.structure.core.Table#ff23174" + } + }, + { + "primaryKey": { + "backingIndex": "liquibase.structure.core.Index#ff23187", + "columns": [ + "liquibase.structure.core.Column#ff23188" + ], + "name": "PK_VALUESET", + "snapshotId": "ff23190", + "table": "liquibase.structure.core.Table#ff23186" + } + } + ], + "liquibase.structure.core.Schema": [ + { + "schema": { + "catalog": "liquibase.structure.core.Catalog#ff23152", + "default": true, + "name": "JPA_BUDDY", + "snapshotId": "ff23151" + } + } + ], + "liquibase.structure.core.Table": [ + { + "table": { + "columns": [ + "liquibase.structure.core.Column#ff23155", + "liquibase.structure.core.Column#ff23156", + "liquibase.structure.core.Column#ff23157", + "liquibase.structure.core.Column#ff23158", + "liquibase.structure.core.Column#ff23159", + "liquibase.structure.core.Column#ff23160", + "liquibase.structure.core.Column#ff23161" + ], + "indexes": [ + "liquibase.structure.core.Index#ff23154" + ], + "name": "audit_event", + "primaryKey": "liquibase.structure.core.PrimaryKey#ff23162", + "schema": "liquibase.structure.core.Schema#ff23151", + "snapshotId": "ff23153" + } + }, + { + "table": { + "columns": [ + "liquibase.structure.core.Column#ff23167", + "liquibase.structure.core.Column#ff23168", + "liquibase.structure.core.Column#ff23169", + "liquibase.structure.core.Column#ff23165", + "liquibase.structure.core.Column#ff23170", + "liquibase.structure.core.Column#ff23171", + "liquibase.structure.core.Column#ff23172" + ], + "indexes": [ + "liquibase.structure.core.Index#ff23166" + ], + "name": "signer_information", + "primaryKey": "liquibase.structure.core.PrimaryKey#ff23173", + "schema": "liquibase.structure.core.Schema#ff23151", + "snapshotId": "ff23163", + "uniqueConstraints": [ + "liquibase.structure.core.UniqueConstraint#ff23164" + ] + } + }, + { + "table": { + "columns": [ + "liquibase.structure.core.Column#ff23195", + "liquibase.structure.core.Column#ff23196", + "liquibase.structure.core.Column#ff23197", + "liquibase.structure.core.Column#ff23193", + "liquibase.structure.core.Column#ff23198", + "liquibase.structure.core.Column#ff23199", + "liquibase.structure.core.Column#ff23200" + ], + "indexes": [ + "liquibase.structure.core.Index#ff23194" + ], + "name": "trusted_party", + "primaryKey": "liquibase.structure.core.PrimaryKey#ff23201", + "schema": "liquibase.structure.core.Schema#ff23151", + "snapshotId": "ff23191", + "uniqueConstraints": [ + "liquibase.structure.core.UniqueConstraint#ff23192" + ] + } + }, + { + "table": { + "columns": [ + "liquibase.structure.core.Column#ff23176", + "liquibase.structure.core.Column#ff23177", + "liquibase.structure.core.Column#ff23178", + "liquibase.structure.core.Column#ff23179", + "liquibase.structure.core.Column#ff23180", + "liquibase.structure.core.Column#ff23181", + "liquibase.structure.core.Column#ff23182", + "liquibase.structure.core.Column#ff23183", + "liquibase.structure.core.Column#ff23184" + ], + "indexes": [ + "liquibase.structure.core.Index#ff23175" + ], + "name": "validation_rule", + "primaryKey": "liquibase.structure.core.PrimaryKey#ff23185", + "schema": "liquibase.structure.core.Schema#ff23151", + "snapshotId": "ff23174" + } + }, + { + "table": { + "columns": [ + "liquibase.structure.core.Column#ff23188", + "liquibase.structure.core.Column#ff23189" + ], + "indexes": [ + "liquibase.structure.core.Index#ff23187" + ], + "name": "valueset", + "primaryKey": "liquibase.structure.core.PrimaryKey#ff23190", + "schema": "liquibase.structure.core.Schema#ff23151", + "snapshotId": "ff23186" + } + } + ], + "liquibase.structure.core.UniqueConstraint": [ + { + "uniqueConstraint": { + "clustered": false, + "columns": [ + "liquibase.structure.core.Column#ff23165" + ], + "deferrable": false, + "disabled": false, + "initiallyDeferred": false, + "name": "UC_SIGNER_INFORMATION_THUMBPRINT", + "snapshotId": "ff23164", + "table": "liquibase.structure.core.Table#ff23163", + "validate": true + } + }, + { + "uniqueConstraint": { + "clustered": false, + "columns": [ + "liquibase.structure.core.Column#ff23193" + ], + "deferrable": false, + "disabled": false, + "initiallyDeferred": false, + "name": "UC_TRUSTED_PARTY_THUMBPRINT", + "snapshotId": "ff23192", + "table": "liquibase.structure.core.Table#ff23191", + "validate": true + } + } + ] + }, + "snapshotControl": { + "snapshotControl": { + "includedType": [ + "liquibase.structure.core.Catalog", + "liquibase.structure.core.Column", + "liquibase.structure.core.ForeignKey", + "liquibase.structure.core.Index", + "liquibase.structure.core.PrimaryKey", + "liquibase.structure.core.Schema", + "liquibase.structure.core.Sequence", + "liquibase.structure.core.Table", + "liquibase.structure.core.UniqueConstraint", + "liquibase.structure.core.View" + ] + } + } + } +} diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..8a6a2211 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,57 @@ + + + + + + + + + DEBUG + + + + + timestamp="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC}", level="%level", hostname="${HOSTNAME}", pid="${PID:-}", thread="%thread", class="%logger{40}", message="%replace(%replace(%m){'[\r\n]+', ', '}){'"', '\''}", trace="%X{traceId}", span="%X{spanId}", %X%n + + + utf8 + + + + + ${catalina.base:-.}/logs/dgcg.log + + ${catalina.base:-.}/logs/dgcg-%d{yyyy-MM-dd}.log + 90 + + true + true + + + + timestamp="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC}", level="%level", hostname="${HOSTNAME}", pid="${PID:-}", thread="%thread", class="%logger{40}", message="%replace(%replace(%m){'[\r\n]+', ','}){'"', '\''}", exception="%replace(%ex){'[\r\n]+', ', '}", trace="%X{traceId}", span="%X{spanId}", %X%n%nopex + + + utf8 + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/validation-rule.schema.json b/src/main/resources/validation-rule.schema.json new file mode 100644 index 00000000..454740ef --- /dev/null +++ b/src/main/resources/validation-rule.schema.json @@ -0,0 +1,146 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://webgate.acceptance.ec.europa.eu/dgcg-json-api/validation-rule.schema.json", + "title": "EU DCC Validation Rule", + "description": "Rule to validate an issued EU Digital Covid Certificate.", + "type": "object", + "additionalProperties": false, + "required": [ + "AffectedFields", + "Country", + "CertificateType", + "Description", + "Engine", + "EngineVersion", + "Identifier", + "Logic", + "SchemaVersion", + "Type", + "ValidFrom", + "ValidTo", + "Version" + ], + "properties": { + "Identifier": { + "type": "string", + "description": "The unique rule name", + "pattern": "^(GR|VR|TR|RR|IR)-[A-Z]{2}-\\d{4}$" + }, + "Type": { + "type": "string", + "description": "Type of the rule", + "enum": [ + "Acceptance", + "Invalidation" + ] + }, + "Country": { + "type": "string", + "description": "ISO Country Code of rule owner", + "pattern": "^[A-Z]{2}$" + }, + "Region": { + "type": "string", + "description": "Additional Region property to precise country property.", + "pattern": "^[A-Z0-9]{0,5}$" + }, + "Version": { + "type": "string", + "description": "Version of the rule (Semver)", + "pattern": "^\\d+\\.\\d+\\.\\d+$" + }, + "SchemaVersion": { + "type": "string", + "description": "Version of the used schema (Semver)", + "pattern": "^\\d+\\.\\d+\\.\\d+$" + }, + "Engine": { + "type": "string", + "description": "Type of the RuleEngine" + }, + "EngineVersion": { + "type": "string", + "description": "Version of the used engine (Semver)", + "pattern": "^\\d+\\.\\d+\\.\\d+$" + }, + "CertificateType": { + "type": "string", + "description": "Type of the certificate", + "enum": [ + "General", + "Test", + "Vaccination", + "Recovery" + ] + }, + "Description": { + "type": "array", + "description": "Array of human readable description of the rule", + "items": { + "type": "object", + "required": [ + "lang", + "desc" + ], + "properties": { + "lang": { + "type": "string", + "description": "Language of the description", + "pattern": "^([a-z]{2}|[a-z]{2}-[a-z]{2})$" + }, + "desc": { + "type": "string", + "description": "Description of this rule in specified language", + "minLength": 20 + } + } + }, + "contains": { + "type": "object", + "required": [ + "lang", + "desc" + ], + "properties": { + "lang": { + "type": "string", + "description": "Language of the description", + "pattern": "^en$" + }, + "desc": { + "type": "string", + "description": "Human readable description of this rule in English language", + "minLength": 20 + } + } + }, + "minItems": 1 + }, + "ValidFrom": { + "type": "string", + "description": "Start validity of the rule as ISO 8601 Timestamp (without ms, with timezone)", + "format": "date-time", + "pattern": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([-+][0-2]\\d:[0-5]\\d|Z)$" + }, + "ValidTo": { + "type": "string", + "description": "End validity of the rule as ISO 8601 Timestamp (without ms, with timezone)", + "format": "date-time", + "pattern": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([-+][0-2]\\d:[0-5]\\d|Z)$" + }, + "AffectedFields": { + "type": "array", + "description": "Fields of the payload which are used by the rule.", + "items": { + "type": "string", + "description": "Affected field of payload" + }, + "minItems": 1 + }, + "Logic": { + "type": "object", + "description": "The logic payload in JSON", + "minProperties": 1 + } + } +} \ No newline at end of file diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListIntegrationTest.java new file mode 100644 index 00000000..3b6ac86c --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CountryListIntegrationTest.java @@ -0,0 +1,88 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.TrustedPartyRepository; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +class CountryListIntegrationTest { + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + TrustedPartyRepository trustedPartyRepository; + + @Autowired + DgcConfigProperties dgcConfigProperties; + + private static final String countryCode = "EU"; + private static final String authCertSubject = "C=EU"; + + @Autowired + private MockMvc mockMvc; + + @BeforeEach + void testData() { + trustedPartyRepository.deleteAll(); + } + + @Test + void testGetTrustedParties() throws Exception { + // Insert some Certificates for random Countries + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, "AA"); + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, "AB"); + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, "AC"); + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, "AD"); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/countrylist") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.length()").value(5)) + .andExpect(jsonPath("$[0]").value("AA")) + .andExpect(jsonPath("$[1]").value("AB")) + .andExpect(jsonPath("$[2]").value("AC")) + .andExpect(jsonPath("$[3]").value("AD")) + .andExpect(jsonPath("$[4]").value(countryCode)); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateIntegrationTest.java new file mode 100644 index 00000000..c49bf4db --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/SignerCertificateIntegrationTest.java @@ -0,0 +1,326 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.AuditEventRepository; +import eu.europa.ec.dgc.gateway.repository.SignerInformationRepository; +import eu.europa.ec.dgc.gateway.testdata.CertificateTestUtils; +import eu.europa.ec.dgc.gateway.testdata.DgcTestKeyStore; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.signing.SignedCertificateMessageBuilder; +import eu.europa.ec.dgc.signing.SignedCertificateMessageParser; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Optional; +import org.bouncycastle.cert.X509CertificateHolder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +class SignerCertificateIntegrationTest { + + @Autowired + DgcConfigProperties dgcConfigProperties; + + @Autowired + CertificateUtils certificateUtils; + + @Autowired + DgcTestKeyStore dgcTestKeyStore; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + SignerInformationRepository signerInformationRepository; + + @Autowired + AuditEventRepository auditEventRepository; + @Autowired + private MockMvc mockMvc; + + private static final String countryCode = "EU"; + private static final String authCertSubject = "C=" + countryCode; + + @Test + void testSuccessfulUpload() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + String payload = new SignedCertificateMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(new X509CertificateHolder(payloadCertificate.getEncoded())) + .buildAsString(); + + // immediately parse the message to get the signature from the signed message + String signature = new SignedCertificateMessageParser(payload).getSignature(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + Assertions.assertEquals(signerInformationEntitiesInDb + 1, signerInformationRepository.count()); + Optional createdSignerInformationEntity = + signerInformationRepository.getFirstByThumbprint(certificateUtils.getCertThumbprint(payloadCertificate)); + + Assertions.assertTrue(createdSignerInformationEntity.isPresent()); + + Assertions.assertEquals(auditEventEntitiesInDb+1, auditEventRepository.count()); + Assertions.assertEquals(SignerInformationEntity.CertificateType.DSC, createdSignerInformationEntity.get().getCertificateType()); + Assertions.assertEquals(countryCode, createdSignerInformationEntity.get().getCountry()); + Assertions.assertEquals(signature, createdSignerInformationEntity.get().getSignature()); + Assertions.assertEquals(Base64.getEncoder().encodeToString(payloadCertificate.getEncoded()), createdSignerInformationEntity.get().getRawData()); + } + + @Test + void testUploadFailedConflict() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + String payload = new SignedCertificateMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(new X509CertificateHolder(payloadCertificate.getEncoded())) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isConflict()); + Assertions.assertEquals(auditEventEntitiesInDb+1, auditEventRepository.count()); + Assertions.assertEquals(signerInformationEntitiesInDb + 1, signerInformationRepository.count()); + } + + @Test + void testUploadFailedInvalidCSCA() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + // sign with TrustAnchor + X509Certificate cscaCertificate = dgcTestKeyStore.getTrustAnchor(); + PrivateKey cscaPrivateKey = dgcTestKeyStore.getTrustAnchorPrivateKey(); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + String payload = new SignedCertificateMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(new X509CertificateHolder(payloadCertificate.getEncoded())) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + Assertions.assertEquals(auditEventEntitiesInDb+1, auditEventRepository.count()); + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testUploadFailedInvalidCSCAWrongCountryCode() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + // sign with CSCA from another country + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "XX"); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, "XX"); + + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + String payload = new SignedCertificateMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(new X509CertificateHolder(payloadCertificate.getEncoded())) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + Assertions.assertEquals(auditEventEntitiesInDb+1, auditEventRepository.count()); + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testUploadFailedPayloadCertCountryWrong() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, "XX", "Payload Cert", cscaCertificate, cscaPrivateKey); + + String payload = new SignedCertificateMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(new X509CertificateHolder(payloadCertificate.getEncoded())) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + Assertions.assertEquals(auditEventEntitiesInDb+1, auditEventRepository.count()); + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testUploadFailedWrongSignerCertificate() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "XX"); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, "XX"); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + String payload = new SignedCertificateMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(new X509CertificateHolder(payloadCertificate.getEncoded())) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + Assertions.assertEquals(auditEventEntitiesInDb+1, auditEventRepository.count()); + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testUploadFailedInvalidCmsMessage() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + String payload = new SignedCertificateMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(new X509CertificateHolder(payloadCertificate.getEncoded())) + .buildAsString(); + + // randomly play a little bit inside the base64 string + payload = payload.replace(payload.substring(10, 50), payload.substring(80, 120)); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java new file mode 100644 index 00000000..40affd9a --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java @@ -0,0 +1,490 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.SignerInformationRepository; +import eu.europa.ec.dgc.gateway.repository.TrustedPartyRepository; +import eu.europa.ec.dgc.gateway.restapi.dto.CertificateTypeDto; +import eu.europa.ec.dgc.gateway.restapi.dto.TrustListDto; +import eu.europa.ec.dgc.gateway.testdata.CertificateTestUtils; +import eu.europa.ec.dgc.gateway.testdata.DgcTestKeyStore; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.io.UnsupportedEncodingException; +import java.security.KeyPairGenerator; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.time.ZonedDateTime; +import java.util.Base64; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +@SpringBootTest +@AutoConfigureMockMvc +class TrustListIntegrationTest { + + @Autowired + SignerInformationRepository signerInformationRepository; + + @Autowired + TrustedPartyRepository trustedPartyRepository; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + DgcConfigProperties dgcConfigProperties; + + @Autowired + CertificateUtils certificateUtils; + + @Autowired + DgcTestKeyStore dgcTestKeyStore; + + @Autowired + private MockMvc mockMvc; + + private static final String countryCode = "EU"; + private static final String authCertSubject = "C=" + countryCode; + + X509Certificate certUploadDe, certUploadEu, certCscaDe, certCscaEu, certAuthDe, certAuthEu, certDscDe, certDscEu; + + @BeforeEach + void testData() throws Exception { + trustedPartyRepository.deleteAll(); + signerInformationRepository.deleteAll(); + + certUploadDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "DE"); + certUploadEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "EU"); + certCscaDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "DE"); + certCscaEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "EU"); + certAuthDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "DE"); + certAuthEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU"); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ec"); + certDscDe = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "DE", "Test"); + certDscEu = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EU", "Test"); + + signerInformationRepository.save(new SignerInformationEntity( + null, + ZonedDateTime.now(), + "DE", + certificateUtils.getCertThumbprint(certDscDe), + Base64.getEncoder().encodeToString(certDscDe.getEncoded()), + "sig1", + SignerInformationEntity.CertificateType.DSC + )); + + signerInformationRepository.save(new SignerInformationEntity( + null, + ZonedDateTime.now(), + "EU", + certificateUtils.getCertThumbprint(certDscEu), + Base64.getEncoder().encodeToString(certDscEu.getEncoded()), + "sig2", + SignerInformationEntity.CertificateType.DSC + )); + } + + @Test + void testTrustListDownloadNoFilter() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/trustList") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certDscDe, "DE", CertificateTypeDto.DSC, "sig1")) + .andExpect(c -> assertTrustListItem(c, certDscEu, "EU", CertificateTypeDto.DSC, "sig2")) + .andExpect(c -> assertTrustListItem(c, certCscaDe, "DE", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListItem(c, certCscaEu, "EU", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListItem(c, certUploadDe, "DE", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListItem(c, certUploadEu, "EU", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListItem(c, certAuthDe, "DE", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListItem(c, certAuthEu, "EU", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListLength(c, 8)); + } + + @Test + void testTrustListDownloadFilterByType() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/trustList/AUTHENTICATION") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certAuthDe, "DE", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListItem(c, certAuthEu, "EU", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListLength(c, 2)); + + mockMvc.perform(get("/trustList/UPLOAD") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certUploadDe, "DE", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListItem(c, certUploadEu, "EU", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListLength(c, 2)); + + mockMvc.perform(get("/trustList/CSCA") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certCscaDe, "DE", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListItem(c, certCscaEu, "EU", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListLength(c, 2)); + + mockMvc.perform(get("/trustList/DSC") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certDscDe, "DE", CertificateTypeDto.DSC, "sig1")) + .andExpect(c -> assertTrustListItem(c, certDscEu, "EU", CertificateTypeDto.DSC, "sig2")) + .andExpect(c -> assertTrustListLength(c, 2)); + } + + @Test + void testTrustListDownloadFilterByTypeCaseInsensitive() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/trustList/aUtHeNtiCaTiOn") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certAuthDe, "DE", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListItem(c, certAuthEu, "EU", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListLength(c, 2)); + + mockMvc.perform(get("/trustList/uploAd") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certUploadDe, "DE", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListItem(c, certUploadEu, "EU", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListLength(c, 2)); + + mockMvc.perform(get("/trustList/csCA") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certCscaDe, "DE", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListItem(c, certCscaEu, "EU", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListLength(c, 2)); + + mockMvc.perform(get("/trustList/dsc") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certDscDe, "DE", CertificateTypeDto.DSC, "sig1")) + .andExpect(c -> assertTrustListItem(c, certDscEu, "EU", CertificateTypeDto.DSC, "sig2")) + .andExpect(c -> assertTrustListLength(c, 2)); + } + + @Test + void testTrustListDownloadFilterByTypeAndCountry() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/trustList/AUTHENTICATION/DE") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certAuthDe, "DE", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/AUTHENTICATION/EU") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certAuthEu, "EU", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/UPLOAD/DE") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certUploadDe, "DE", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/UPLOAD/EU") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certUploadEu, "EU", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/CSCA/DE") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certCscaDe, "DE", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/CSCA/EU") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certCscaEu, "EU", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/DSC/DE") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certDscDe, "DE", CertificateTypeDto.DSC, "sig1")) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/DSC/EU") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certDscEu, "EU", CertificateTypeDto.DSC, "sig2")) + .andExpect(c -> assertTrustListLength(c, 1)); + } + + @Test + void testTrustListDownloadFilterByTypeAndCountryLowercase() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/trustList/AUTHENTICATION/de") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certAuthDe, "DE", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/AUTHENTICATION/eu") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certAuthEu, "EU", CertificateTypeDto.AUTHENTICATION, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/UPLOAD/de") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certUploadDe, "DE", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/UPLOAD/eu") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certUploadEu, "EU", CertificateTypeDto.UPLOAD, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/CSCA/de") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certCscaDe, "DE", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/CSCA/eu") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certCscaEu, "EU", CertificateTypeDto.CSCA, null)) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/DSC/de") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certDscDe, "DE", CertificateTypeDto.DSC, "sig1")) + .andExpect(c -> assertTrustListLength(c, 1)); + + mockMvc.perform(get("/trustList/DSC/eu") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(c -> assertTrustListItem(c, certDscEu, "EU", CertificateTypeDto.DSC, "sig2")) + .andExpect(c -> assertTrustListLength(c, 1)); + } + + @Test + void testTrustListDownloadEmptyList() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + signerInformationRepository.deleteAll(); + + mockMvc.perform(get("/trustList/DSC") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json("[]")); + } + + @Test + void testTrustListWrongType() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/trustList/XXX") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + } + + @Test + void testTrustListWrongCountryCode() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/trustList/DSC/XXX") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + } + + private void assertTrustListItem(MvcResult result, X509Certificate certificate, String country, CertificateTypeDto certificateTypeDto, String signature) throws CertificateEncodingException, UnsupportedEncodingException, JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()); + List trustList = objectMapper.readValue(result.getResponse().getContentAsString(), new TypeReference<>() { + }); + + Optional trustListOptional = trustList + .stream() + .filter(tl -> tl.getKid().equals(certificateUtils.getCertKid(certificate))) + .findFirst(); + + Assertions.assertTrue(trustListOptional.isPresent()); + + TrustListDto trustListItem = trustListOptional.get(); + + Assertions.assertEquals(certificateUtils.getCertKid(certificate), trustListItem.getKid()); + Assertions.assertEquals(country, trustListItem.getCountry()); + Assertions.assertEquals(certificateTypeDto, trustListItem.getCertificateType()); + Assertions.assertEquals(certificateUtils.getCertThumbprint(certificate), trustListItem.getThumbprint()); + Assertions.assertEquals(Base64.getEncoder().encodeToString(certificate.getEncoded()), trustListItem.getRawData()); + + if (signature != null) { + Assertions.assertEquals(signature, trustListItem.getSignature()); + } + } + + private void assertTrustListLength(MvcResult result, int expectedLength) throws UnsupportedEncodingException, JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()); + List trustList = objectMapper.readValue(result.getResponse().getContentAsString(), new TypeReference<>() { + }); + Assertions.assertEquals(expectedLength, trustList.size()); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java new file mode 100644 index 00000000..ea759c3c --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java @@ -0,0 +1,1215 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import static eu.europa.ec.dgc.gateway.testdata.CertificateTestUtils.assertEquals; +import static eu.europa.ec.dgc.gateway.testdata.CertificateTestUtils.getDummyValidationRule; +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer; +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.connector.model.ValidationRule; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.entity.ValidationRuleEntity; +import eu.europa.ec.dgc.gateway.repository.AuditEventRepository; +import eu.europa.ec.dgc.gateway.repository.ValidationRuleRepository; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.signing.SignedStringMessageBuilder; +import eu.europa.ec.dgc.signing.SignedStringMessageParser; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +@Slf4j +class ValidationRuleIntegrationTest { + + @Autowired + DgcConfigProperties dgcConfigProperties; + + @Autowired + CertificateUtils certificateUtils; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + AuditEventRepository auditEventRepository; + + @Autowired + ValidationRuleRepository validationRuleRepository; + + ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_DATE) + .appendLiteral('T') + .appendValue(HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2) + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .appendOffsetId() + .toFormatter(); + + private static final String countryCode = "EU"; + private static final String authCertSubject = "C=" + countryCode; + + @BeforeEach + public void setup() { + validationRuleRepository.deleteAll(); + auditEventRepository.deleteAll(); + + objectMapper = new ObjectMapper(); + + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer( + new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH:mm:ssXXX").toFormatter() + )); + + objectMapper.registerModule(javaTimeModule); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + + } + + @Test + void testSuccessfulUpload() throws Exception { + long validationRulesInDb = validationRuleRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + Assertions.assertEquals(validationRulesInDb + 1, validationRuleRepository.count()); + Optional createdValidationRule = + validationRuleRepository.getByRuleIdAndVersion(validationRule.getIdentifier(), validationRule.getVersion()); + + Assertions.assertTrue(createdValidationRule.isPresent()); + + Assertions.assertEquals(auditEventEntitiesInDb + 1, auditEventRepository.count()); + Assertions.assertEquals(validationRule.getValidFrom().toEpochSecond(), createdValidationRule.get().getValidFrom().toEpochSecond()); + Assertions.assertEquals(validationRule.getValidTo().toEpochSecond(), createdValidationRule.get().getValidTo().toEpochSecond()); + Assertions.assertEquals(validationRule.getCountry(), createdValidationRule.get().getCountry()); + Assertions.assertEquals(validationRule.getType().toUpperCase(Locale.ROOT), createdValidationRule.get().getValidationRuleType().toString()); + + SignedStringMessageParser parser = new SignedStringMessageParser(createdValidationRule.get().getCms()); + ValidationRule parsedValidationRule = objectMapper.readValue(parser.getPayload(), ValidationRule.class); + + assertEquals(validationRule, parsedValidationRule); + } + + @Test + void testSuccessfulUploadWithoutRegionProperty() throws Exception { + long validationRulesInDb = validationRuleRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setRegion(null); + String json = objectMapper.writeValueAsString(validationRule); + json = json.replace("\"Region\":null,", ""); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(json) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + Assertions.assertEquals(validationRulesInDb + 1, validationRuleRepository.count()); + Optional createdValidationRule = + validationRuleRepository.getByRuleIdAndVersion(validationRule.getIdentifier(), validationRule.getVersion()); + + Assertions.assertTrue(createdValidationRule.isPresent()); + + Assertions.assertEquals(auditEventEntitiesInDb + 1, auditEventRepository.count()); + Assertions.assertEquals(validationRule.getValidFrom().toEpochSecond(), createdValidationRule.get().getValidFrom().toEpochSecond()); + Assertions.assertEquals(validationRule.getValidTo().toEpochSecond(), createdValidationRule.get().getValidTo().toEpochSecond()); + Assertions.assertEquals(validationRule.getCountry(), createdValidationRule.get().getCountry()); + Assertions.assertEquals(validationRule.getType().toUpperCase(Locale.ROOT), createdValidationRule.get().getValidationRuleType().toString()); + + SignedStringMessageParser parser = new SignedStringMessageParser(createdValidationRule.get().getCms()); + ValidationRule parsedValidationRule = objectMapper.readValue(parser.getPayload(), ValidationRule.class); + + assertEquals(validationRule, parsedValidationRule); + } + + @Test + void testInputOnlyContainsJson() throws Exception { + long validationRulesInDb = validationRuleRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule) + "\n" + objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule) + "x") + .buildAsString(); + + authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()); + + Assertions.assertEquals(validationRulesInDb, validationRuleRepository.count()); + } + + @Test + void testJsonSchemaValidation() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + Map invalidValidationRules = new HashMap<>(); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setIdentifier("XXXXXXXX"); + invalidValidationRules.put("Invalid ID Pattern", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setType("XXXXX"); + invalidValidationRules.put("Invalid Type", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setCountry("EUX"); + invalidValidationRules.put("Invalid Country Pattern", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setRegion("XXXXXX"); + invalidValidationRules.put("Invalid Region Pattern", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setVersion("1.0.0.0"); + invalidValidationRules.put("Invalid Version Pattern", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setSchemaVersion("1.0.0.0"); + invalidValidationRules.put("Invalid Schema Version Pattern", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setEngineVersion("1.2.3.aaaaa"); + invalidValidationRules.put("Invalid EngineVersion Pattern", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setDescription(List.of(new ValidationRule.DescriptionItem("xx", "1".repeat(20)))); + invalidValidationRules.put("Missing Description EN", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.getDescription().get(0).setDescription("shorttext"); + invalidValidationRules.put("Description to short", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setAffectedFields(Collections.emptyList()); + invalidValidationRules.put("AffectedFields No Values", validationRule); + + validationRule = getDummyValidationRule(); + validationRule.setLogic(JsonNodeFactory.instance.objectNode()); + invalidValidationRules.put("Logic Empty", validationRule); + + for (String ruleKey : invalidValidationRules.keySet()) { + log.info("JSON Schema Check: {}", ruleKey); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(invalidValidationRules.get(ruleKey))) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x200")); + } + } + + @Test + void testValidationCountry() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setIdentifier("GR-DE-0001"); + validationRule.setCountry("DE"); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.code").value("0x210")); + + validationRule.setCountry("EU"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.code").value("0x210")); + } + + @Test + void testValidationVersion() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x220")); + + validationRule.setVersion("0.9.0"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x220")); + } + + @Test + void testValidationUploadCert() throws Exception { + ValidationRule validationRule = getDummyValidationRule(); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate( + certificateUtils.convertCertificate(trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "EU")), + trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, "EU")) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x230")); + } + + @Test + void testValidationTimestamps() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setValidFrom(ZonedDateTime.now().plus(1, ChronoUnit.DAYS)); + validationRule.setValidTo(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x240")); + + validationRule = getDummyValidationRule(); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + validationRule.setVersion("1.0.1"); + validationRule.setValidFrom(validationRule.getValidFrom().minus(1, ChronoUnit.SECONDS)); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x240")); + + validationRule.setValidFrom(ZonedDateTime.now().plus(4, ChronoUnit.WEEKS)); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x240")); + } + + @Test + void testValidationTimestamps2() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setIdentifier("IR-EU-0001"); + validationRule.setType("Invalidation"); + validationRule.setValidFrom(ZonedDateTime.now().plus(1, ChronoUnit.SECONDS)); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + validationRule = getDummyValidationRule(); + validationRule.setValidFrom(ZonedDateTime.now()); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x240")); + } + + @Test + void testValidationTimestamps3() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setValidFrom(ZonedDateTime.now().plus(3, ChronoUnit.DAYS)); + validationRule.setValidTo(ZonedDateTime.now() + .plus(6, ChronoUnit.DAYS) + .minus(1, ChronoUnit.SECONDS)); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x240")); + } + + @Test + void testValidationRuleId() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setIdentifier("GR-EU-0001"); + validationRule.setType("Invalidation"); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x250")); + + validationRule.setIdentifier("IR-EU-0001"); + validationRule.setType("Acceptance"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x250")); + + validationRule.setIdentifier("IR-EU-0001"); + validationRule.setType("Invalidation"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + validationRule.setIdentifier("GR-EU-0001"); + validationRule.setType("Acceptance"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + } + + @Test + void testValidationRuleInvalidIdPrefix() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + validationRule.setIdentifier("TR-EU-0001"); + validationRule.setCertificateType("Vaccination"); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x250")); + + validationRule.setIdentifier("VR-EU-0001"); + validationRule.setCertificateType("Test"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x250")); + + validationRule.setIdentifier("RR-EU-0001"); + validationRule.setCertificateType("Vaccination"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x250")); + + validationRule.setIdentifier("GR-EU-0001"); + validationRule.setCertificateType("Vaccination"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x250")); + } + + @Test + void testDelete() throws Exception { + long validationRulesInDb = validationRuleRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + validationRule.setVersion("1.0.1"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + Assertions.assertEquals(validationRulesInDb + 2, validationRuleRepository.count()); + + String deletePayload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(validationRule.getIdentifier()) + .buildAsString(); + + mockMvc.perform(delete("/rules") + .content(deletePayload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isNoContent()); + + Assertions.assertEquals(validationRulesInDb, validationRuleRepository.count()); + } + + @Test + void testDeleteFailNotFound() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString("IR-EU-0001")) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(delete("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isNotFound()); + } + + @Test + void testDeleteFailInvalidUploadCertificate() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString("IR-EU-0001")) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(delete("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x230")); + } + + @Test + void testDeleteFailInvalidIdString() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString("XXXX-TESST-!!!!!")) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(delete("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("0x250")); + } + + @Test + void testDeleteFailInvalidCountryCode() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString("IR-DE-0001")) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(delete("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.code").value("0x210")); + } + + @Test + void testDownloadReturnAll() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule1 = getDummyValidationRule(); + validationRule1.setValidFrom(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)); + + String payload1 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule1)) + .buildAsString(); + + ValidationRuleEntity vr1 = new ValidationRuleEntity(); + vr1.setRuleId(validationRule1.getIdentifier()); + vr1.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule1.getType().toUpperCase(Locale.ROOT))); + vr1.setValidTo(validationRule1.getValidTo()); + vr1.setValidFrom(validationRule1.getValidFrom()); + vr1.setCountry(validationRule1.getCountry()); + vr1.setCms(payload1); + vr1.setVersion(validationRule1.getVersion()); + + validationRuleRepository.save(vr1); + + ValidationRule validationRule2 = getDummyValidationRule(); + validationRule2.setValidFrom(ZonedDateTime.now().plus(2, ChronoUnit.DAYS)); + validationRule2.setVersion("1.0.1"); + + String payload2 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule2)) + .buildAsString(); + + ValidationRuleEntity vr2 = new ValidationRuleEntity(); + vr2.setRuleId(validationRule2.getIdentifier()); + vr2.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule2.getType().toUpperCase(Locale.ROOT))); + vr2.setValidTo(validationRule2.getValidTo()); + vr2.setValidFrom(validationRule2.getValidFrom()); + vr2.setCountry(validationRule2.getCountry()); + vr2.setCms(payload2); + vr2.setVersion(validationRule2.getVersion()); + + validationRuleRepository.save(vr2); + + ValidationRule validationRule3 = getDummyValidationRule(); + validationRule3.setIdentifier("GR-EU-0002"); + + String payload3 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule3)) + .buildAsString(); + + ValidationRuleEntity vr3 = new ValidationRuleEntity(); + vr3.setRuleId(validationRule3.getIdentifier()); + vr3.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule3.getType().toUpperCase(Locale.ROOT))); + vr3.setValidTo(validationRule3.getValidTo()); + vr3.setValidFrom(validationRule3.getValidFrom()); + vr3.setCountry(validationRule3.getCountry()); + vr3.setCms(payload3); + vr3.setVersion(validationRule3.getVersion()); + + validationRuleRepository.save(vr3); + + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/rules/EU") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.['GR-EU-0001'].length()").value(2)) + .andExpect(jsonPath("$.['GR-EU-0001'][0].version").value(vr2.getVersion())) + .andExpect(jsonPath("$.['GR-EU-0001'][0].cms").value(vr2.getCms())) + .andExpect(jsonPath("$.['GR-EU-0001'][0].validTo").value(vr2.getValidTo().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][0].validFrom").value(vr2.getValidFrom().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][1].version").value(vr1.getVersion())) + .andExpect(jsonPath("$.['GR-EU-0001'][1].cms").value(vr1.getCms())) + .andExpect(jsonPath("$.['GR-EU-0001'][1].validTo").value(vr1.getValidTo().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][1].validFrom").value(vr1.getValidFrom().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0002'].length()").value(1)) + .andExpect(jsonPath("$.['GR-EU-0002'][0].version").value(vr3.getVersion())) + .andExpect(jsonPath("$.['GR-EU-0002'][0].cms").value(vr3.getCms())) + .andExpect(jsonPath("$.['GR-EU-0002'][0].validTo").value(vr3.getValidTo().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0002'][0].validFrom").value(vr3.getValidFrom().format(formatter))); + } + + @Test + void testDownloadReturnOnlyValid() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule1 = getDummyValidationRule(); + validationRule1.setValidFrom(ZonedDateTime.now().minus(4, ChronoUnit.DAYS)); + + String payload1 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule1)) + .buildAsString(); + + ValidationRuleEntity vr1 = new ValidationRuleEntity(); + vr1.setRuleId(validationRule1.getIdentifier()); + vr1.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule1.getType().toUpperCase(Locale.ROOT))); + vr1.setValidTo(validationRule1.getValidTo()); + vr1.setValidFrom(validationRule1.getValidFrom()); + vr1.setCountry(validationRule1.getCountry()); + vr1.setCms(payload1); + vr1.setVersion(validationRule1.getVersion()); + + validationRuleRepository.save(vr1); + + ValidationRule validationRule2 = getDummyValidationRule(); + validationRule2.setValidFrom(ZonedDateTime.now().minus(2, ChronoUnit.DAYS)); + validationRule2.setVersion("1.0.1"); + + String payload2 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule2)) + .buildAsString(); + + ValidationRuleEntity vr2 = new ValidationRuleEntity(); + vr2.setRuleId(validationRule2.getIdentifier()); + vr2.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule2.getType().toUpperCase(Locale.ROOT))); + vr2.setValidTo(validationRule2.getValidTo()); + vr2.setValidFrom(validationRule2.getValidFrom()); + vr2.setCountry(validationRule2.getCountry()); + vr2.setCms(payload2); + vr2.setVersion(validationRule2.getVersion()); + + validationRuleRepository.save(vr2); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/rules/EU") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.['GR-EU-0001'].length()").value(1)) + .andExpect(jsonPath("$.['GR-EU-0001'][0].version").value(vr2.getVersion())) + .andExpect(jsonPath("$.['GR-EU-0001'][0].cms").value(vr2.getCms())) + .andExpect(jsonPath("$.['GR-EU-0001'][0].validTo").value(vr2.getValidTo().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][0].validFrom").value(vr2.getValidFrom().format(formatter))); + } + + @Test + void testDownloadDbContainsOnlyRulesValidInFutureShouldReturnAll() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule1 = getDummyValidationRule(); + validationRule1.setValidFrom(ZonedDateTime.now().plus(1, ChronoUnit.DAYS)); + + String payload1 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule1)) + .buildAsString(); + + ValidationRuleEntity vr1 = new ValidationRuleEntity(); + vr1.setRuleId(validationRule1.getIdentifier()); + vr1.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule1.getType().toUpperCase(Locale.ROOT))); + vr1.setValidTo(validationRule1.getValidTo()); + vr1.setValidFrom(validationRule1.getValidFrom()); + vr1.setCountry(validationRule1.getCountry()); + vr1.setCms(payload1); + vr1.setVersion(validationRule1.getVersion()); + + validationRuleRepository.save(vr1); + + ValidationRule validationRule2 = getDummyValidationRule(); + validationRule2.setValidFrom(ZonedDateTime.now().plus(2, ChronoUnit.DAYS)); + validationRule2.setVersion("1.0.1"); + + String payload2 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule2)) + .buildAsString(); + + ValidationRuleEntity vr2 = new ValidationRuleEntity(); + vr2.setRuleId(validationRule2.getIdentifier()); + vr2.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule2.getType().toUpperCase(Locale.ROOT))); + vr2.setValidTo(validationRule2.getValidTo()); + vr2.setValidFrom(validationRule2.getValidFrom()); + vr2.setCountry(validationRule2.getCountry()); + vr2.setCms(payload2); + vr2.setVersion(validationRule2.getVersion()); + + validationRuleRepository.save(vr2); + + ValidationRule validationRule3 = getDummyValidationRule(); + validationRule3.setValidFrom(ZonedDateTime.now().plus(3, ChronoUnit.DAYS)); + validationRule3.setVersion("1.1.0"); + + String payload3 = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule3)) + .buildAsString(); + + ValidationRuleEntity vr3 = new ValidationRuleEntity(); + vr3.setRuleId(validationRule3.getIdentifier()); + vr3.setValidationRuleType(ValidationRuleEntity.ValidationRuleType.valueOf(validationRule3.getType().toUpperCase(Locale.ROOT))); + vr3.setValidTo(validationRule3.getValidTo()); + vr3.setValidFrom(validationRule3.getValidFrom()); + vr3.setCountry(validationRule3.getCountry()); + vr3.setCms(payload3); + vr3.setVersion(validationRule3.getVersion()); + + validationRuleRepository.save(vr3); + + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/rules/EU") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.['GR-EU-0001'].length()").value(3)) + .andExpect(jsonPath("$.['GR-EU-0001'][0].version").value(vr3.getVersion())) + .andExpect(jsonPath("$.['GR-EU-0001'][0].cms").value(vr3.getCms())) + .andExpect(jsonPath("$.['GR-EU-0001'][0].validTo").value(vr3.getValidTo().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][0].validFrom").value(vr3.getValidFrom().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][1].version").value(vr2.getVersion())) + .andExpect(jsonPath("$.['GR-EU-0001'][1].cms").value(vr2.getCms())) + .andExpect(jsonPath("$.['GR-EU-0001'][1].validTo").value(vr2.getValidTo().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][1].validFrom").value(vr2.getValidFrom().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][2].version").value(vr1.getVersion())) + .andExpect(jsonPath("$.['GR-EU-0001'][2].cms").value(vr1.getCms())) + .andExpect(jsonPath("$.['GR-EU-0001'][2].validTo").value(vr1.getValidTo().format(formatter))) + .andExpect(jsonPath("$.['GR-EU-0001'][2].validFrom").value(vr1.getValidFrom().format(formatter))); + + } + + @Test + void testDeleteAliasEndpoint() throws Exception { + long validationRulesInDb = validationRuleRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + validationRule.setVersion("1.0.1"); + + payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + Assertions.assertEquals(validationRulesInDb + 2, validationRuleRepository.count()); + + String deletePayload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(validationRule.getIdentifier()) + .buildAsString(); + + mockMvc.perform(post("/rules/delete") + .content(deletePayload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isNoContent()); + + Assertions.assertEquals(validationRulesInDb, validationRuleRepository.count()); + } + + @Test + void testSuccessfulUploadWithOldContentType() throws Exception { + long validationRulesInDb = validationRuleRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + ValidationRule validationRule = getDummyValidationRule(); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(validationRule)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/rules") + .content(payload) + .contentType("application/cms-text") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isCreated()); + + Assertions.assertEquals(validationRulesInDb + 1, validationRuleRepository.count()); + Optional createdValidationRule = + validationRuleRepository.getByRuleIdAndVersion(validationRule.getIdentifier(), validationRule.getVersion()); + + Assertions.assertTrue(createdValidationRule.isPresent()); + + Assertions.assertEquals(auditEventEntitiesInDb + 1, auditEventRepository.count()); + Assertions.assertEquals(validationRule.getValidFrom().toEpochSecond(), createdValidationRule.get().getValidFrom().toEpochSecond()); + Assertions.assertEquals(validationRule.getValidTo().toEpochSecond(), createdValidationRule.get().getValidTo().toEpochSecond()); + Assertions.assertEquals(validationRule.getCountry(), createdValidationRule.get().getCountry()); + Assertions.assertEquals(validationRule.getType().toUpperCase(Locale.ROOT), createdValidationRule.get().getValidationRuleType().toString()); + + SignedStringMessageParser parser = new SignedStringMessageParser(createdValidationRule.get().getCms()); + ValidationRule parsedValidationRule = objectMapper.readValue(parser.getPayload(), ValidationRule.class); + + assertEquals(validationRule, parsedValidationRule); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetIntegrationTest.java new file mode 100644 index 00000000..9d5e40c9 --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValuesetIntegrationTest.java @@ -0,0 +1,137 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.controller; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.entity.ValuesetEntity; +import eu.europa.ec.dgc.gateway.repository.ValuesetRepository; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +class ValuesetIntegrationTest { + + @Autowired + ValuesetRepository valuesetRepository; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + DgcConfigProperties dgcConfigProperties; + + @Autowired + private MockMvc mockMvc; + + private static final String countryCode = "EU"; + private static final String authCertSubject = "C=" + countryCode; + + private static final ValuesetEntity valuesetEntity1 = + new ValuesetEntity("vs-dummy-1", "{ \"key1\": \"content1\" }"); + private static final ValuesetEntity valuesetEntity2 = + new ValuesetEntity("vs-dummy-2", "{ \"key2\": \"content2\" }"); + private static final ValuesetEntity valuesetEntity3 = + new ValuesetEntity("vs-dummy-3", "{ \"key3\": \"content3\" }"); + + @BeforeEach + void testData() { + valuesetRepository.deleteAll(); + + valuesetRepository.save(valuesetEntity1); + valuesetRepository.save(valuesetEntity2); + valuesetRepository.save(valuesetEntity3); + } + + @Test + void testGetValuesetIds() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/valuesets") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.length()").value(equalTo(3))) + .andExpect(jsonPath("$[0]").value(equalTo(valuesetEntity1.getId()))) + .andExpect(jsonPath("$[1]").value(equalTo(valuesetEntity2.getId()))) + .andExpect(jsonPath("$[2]").value(equalTo(valuesetEntity3.getId()))); + } + + @Test + void testGetValueset() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/valuesets/" + valuesetEntity1.getId()) + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.key1").value(equalTo("content1"))); + + mockMvc.perform(get("/valuesets/" + valuesetEntity2.getId()) + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.key2").value(equalTo("content2"))); + + mockMvc.perform(get("/valuesets/" + valuesetEntity3.getId()) + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.key3").value(equalTo("content3"))); + } + + @Test + void testGetValuesetNotFound() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(get("/valuesets/randomId") + .accept(MediaType.APPLICATION_JSON_VALUE) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isNotFound()); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/filter/CertAuthFilterTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/filter/CertAuthFilterTest.java new file mode 100644 index 00000000..83e1c195 --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/filter/CertAuthFilterTest.java @@ -0,0 +1,213 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.restapi.filter; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import java.math.BigInteger; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +class CertAuthFilterTest { + + @Autowired + private DgcConfigProperties properties; + + @Autowired + private TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + MockMvc mockMvc; + + private final String countryCode = "EU"; + private final String authDn = "C=" + countryCode; + + @Test + void testRequestShouldFailIfDNHeaderIsMissing() throws Exception { + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), certHash) + ).andExpect(status().isUnauthorized()); + } + + @Test + void testRequestShouldFailIfThumbprintHeaderIsMissing() throws Exception { + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), authDn) + ).andExpect(status().isUnauthorized()); + } + + @Test + void testRequestShouldFailIfCertHeadersAreMissing() throws Exception { + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + ).andExpect(status().isUnauthorized()); + } + + @Test + void testRequestShouldFailIfCertIsNotOnWhitelist() throws Exception { + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), "randomString") + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), authDn) + ).andExpect(status().isUnauthorized()); + } + + @Test + void testFilterShouldAppendCountryAndThumbprintToRequestObject() throws Exception { + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), certHash) + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), authDn) + ).andExpect(mvcResult -> { + Assertions.assertEquals("EU", mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY)); + Assertions.assertEquals( + certHash, + mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) + ); + }); + } + + @Test + void testFilterShouldDecodeDnString() throws Exception { + String encodedDnString = "ST%3dSome-State%2c%20C%3dEU%2c%20O%3dInternet%20Widgits%20Pty%20Ltd%2c%20CN%3dTest%20Cert"; + + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), certHash) + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), encodedDnString) + ).andExpect(mvcResult -> { + Assertions.assertEquals("EU", mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY)); + Assertions.assertEquals( + certHash, + mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) + ); + }); + } + + @Test + void testFilterShouldDecodeBase64AndUrlEncodedCertThumbprint() throws Exception { + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + byte[] certHashBytes = new BigInteger(certHash, 16).toByteArray(); + + if (certHashBytes[0] == 0) { + byte[] truncatedCertHashBytes = new byte[certHashBytes.length - 1]; + System.arraycopy(certHashBytes, 1, truncatedCertHashBytes, 0, truncatedCertHashBytes.length); + certHashBytes = truncatedCertHashBytes; + } + + String encodedThumbprint = + URLEncoder.encode(Base64.getEncoder().encodeToString(certHashBytes), StandardCharsets.UTF_8); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), encodedThumbprint) + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), "O=Test Firma GmbH,C=EU,U=,TR,TT=43") + ).andExpect(mvcResult -> { + Assertions.assertEquals("EU", mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY)); + Assertions.assertEquals( + certHash, + mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) + ); + }); + } + + @Test + void testFilterShouldDecodeBase64EncodedCertThumbprint() throws Exception { + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + byte[] certHashBytes = new BigInteger(certHash, 16).toByteArray(); + + if (certHashBytes[0] == 0) { + byte[] truncatedCertHashBytes = new byte[certHashBytes.length - 1]; + System.arraycopy(certHashBytes, 1, truncatedCertHashBytes, 0, truncatedCertHashBytes.length); + certHashBytes = truncatedCertHashBytes; + } + + String encodedThumbprint = Base64.getEncoder().encodeToString(certHashBytes); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), encodedThumbprint) + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), "O=Test Firma GmbH,C=EU,U=,TR,TT=43") + ).andExpect(mvcResult -> { + Assertions.assertEquals("EU", mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY)); + Assertions.assertEquals( + certHash, + mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_THUMBPRINT) + ); + }); + } + + + @Test + void testRequestShouldFailIfCountryIsNotPresentInDnString() throws Exception { + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), certHash) + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), "O=Test Firma GmbH,U=Abteilung XYZ,TR=test") + ).andExpect(status().isBadRequest()); + } + + @Test + void testFilterShouldFindCountryEvenOnMalformedDnString() throws Exception { + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), certHash) + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), "O=Test Firma GmbH,C=EU,U=,TR,TT=43") + ).andExpect(mvcResult -> Assertions.assertEquals("EU", mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY))); + } + + @Test + void testRequestShouldNotFailIfDnStringContainsDuplicatedKeys() throws Exception { + String certHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + mockMvc.perform(post("/signerCertificate/") + .contentType("application/cms") + .header(properties.getCertAuth().getHeaderFields().getThumbprint(), certHash) + .header(properties.getCertAuth().getHeaderFields().getDistinguishedName(), "O=Test Firma GmbH,O=XXX,C=EU,U=Abteilung XYZ,TR=test") + ).andExpect(mvcResult -> Assertions.assertEquals("EU", mvcResult.getRequest().getAttribute(CertificateAuthenticationFilter.REQUEST_PROP_COUNTRY))); + } +} + diff --git a/src/test/java/eu/europa/ec/dgc/gateway/service/AuditServiceTest.java b/src/test/java/eu/europa/ec/dgc/gateway/service/AuditServiceTest.java new file mode 100644 index 00000000..35e4ebcd --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/service/AuditServiceTest.java @@ -0,0 +1,93 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.AuditEventEntity; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.AuditEventRepository; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.utils.CertificateUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AuditServiceTest { + + @Autowired + AuditService auditService; + + @Autowired + AuditEventRepository auditEventRepository; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + CertificateUtils certificateUtils; + + private static final String countryCode = "EU"; + private static final String dummySignature = "randomStringAsSignatureWhichIsNotValidatedInServiceLevel"; + + @Test + void testSuccessfulCreateAuditEvent() { + auditEventRepository.deleteAll(); + String exampleEvent = "postVerificationInformation_ALREADY_EXIST_CHECK_FAILED"; + String exampleEventDescription = "ALREADY_EXIST_CHECK_FAILED"; + + auditService.addAuditEvent(countryCode, dummySignature, + dummySignature, exampleEvent, exampleEventDescription); + + Assertions.assertEquals(1, auditEventRepository.count()); + AuditEventEntity auditEvent = auditEventRepository.findAll().get(0); + + Assertions.assertEquals(countryCode, auditEvent.getCountry()); + Assertions.assertEquals(dummySignature, auditEvent.getAuthenticationSha256Fingerprint()); + Assertions.assertEquals(dummySignature, auditEvent.getUploaderSha256Fingerprint()); + Assertions.assertEquals(exampleEvent, auditEvent.getEvent()); + Assertions.assertEquals(exampleEventDescription, auditEvent.getDescription()); + } + + @Test + void testSuccessfulCreateAuditEventWithCertificate() throws Exception { + auditEventRepository.deleteAll(); + String exampleEvent = "postVerificationInformation_ALREADY_EXIST_CHECK_FAILED"; + String exampleEventDescription = "ALREADY_EXIST_CHECK_FAILED"; + + auditService.addAuditEvent( + countryCode, + certificateUtils.convertCertificate( + trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode)), + dummySignature, + exampleEvent, + exampleEventDescription); + + Assertions.assertEquals(1, auditEventRepository.count()); + AuditEventEntity auditEvent = auditEventRepository.findAll().get(0); + + Assertions.assertEquals(countryCode, auditEvent.getCountry()); + Assertions.assertEquals(dummySignature, auditEvent.getAuthenticationSha256Fingerprint()); + Assertions.assertEquals(trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.UPLOAD, countryCode), auditEvent.getUploaderSha256Fingerprint()); + Assertions.assertEquals(exampleEvent, auditEvent.getEvent()); + Assertions.assertEquals(exampleEventDescription, auditEvent.getDescription()); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateServiceTest.java b/src/test/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateServiceTest.java new file mode 100644 index 00000000..fc73b5e6 --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/service/RatValuesetUpdateServiceTest.java @@ -0,0 +1,475 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.dgc.gateway.client.JrcClient; +import eu.europa.ec.dgc.gateway.entity.ValuesetEntity; +import eu.europa.ec.dgc.gateway.model.JrcRatValueset; +import eu.europa.ec.dgc.gateway.model.JrcRatValuesetResponse; +import eu.europa.ec.dgc.gateway.model.RatValueset; +import eu.europa.ec.dgc.gateway.model.Valueset; +import eu.europa.ec.dgc.gateway.repository.ValuesetRepository; +import feign.FeignException; +import feign.Request; +import feign.RequestTemplate; +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +@SpringBootTest +class RatValuesetUpdateServiceTest { + + @MockBean + JrcClient jrcClientMock; + + @Autowired + RatValuesetUpdateService ratValuesetUpdateService; + + @Autowired + ValuesetRepository valuesetRepository; + + @Autowired + ValuesetService valuesetService; + + @Autowired + ObjectMapper objectMapper; + + private static final String RAT_VALUESET_ID = "covid-19-lab-test-manufacturer-and-name"; + + private RatValueset rat1, rat2; + private final static String RAT1_ID = "1234"; + private final static String RAT2_ID = "5678"; + private ValuesetEntity otherValuesetEntity, valuesetEntity; + private Valueset valueset; + private static final TypeReference> typeReference = new TypeReference<>() { + }; + public static final Request dummyRequest = + Request.create(Request.HttpMethod.GET, "url", new HashMap<>(), null, new RequestTemplate()); + + @BeforeEach + void setup() throws JsonProcessingException { + valuesetRepository.deleteAll(); + + // Create a dummy valueset which should not be touched + otherValuesetEntity = new ValuesetEntity( + "other-valueset-with-different-id", + "this-should-not-be-changes" + ); + otherValuesetEntity = valuesetRepository.save(otherValuesetEntity); + + // Create a RAT valueset which should be updated + rat1 = new RatValueset(); + rat1.setActive(true); + rat1.setVersion(ZonedDateTime.now().minus(5, ChronoUnit.DAYS)); + rat1.setDisplay("RAT 1"); + + rat2 = new RatValueset(); + rat2.setActive(true); + rat2.setVersion(ZonedDateTime.now().minus(6, ChronoUnit.DAYS)); + rat2.setDisplay("RAT 2"); + + valueset = new Valueset<>( + RAT_VALUESET_ID, + LocalDate.now().minus(1, ChronoUnit.DAYS), + Map.of( + RAT1_ID, rat1, + RAT2_ID, rat2 + ) + ); + + valuesetEntity = new ValuesetEntity( + RAT_VALUESET_ID, + objectMapper.writeValueAsString(valueset) + ); + valuesetEntity = valuesetRepository.save(valuesetEntity); + } + + @Test + void testRatValuesetUpdateActiveFalse() throws JsonProcessingException { + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset.HscListHistory history1 = new JrcRatValueset.HscListHistory(); + history1.setInCommonList(true); + history1.setInMutualRecognition(true); + history1.setListDate(ZonedDateTime.now().minus(3, ChronoUnit.DAYS)); + + JrcRatValueset.HscListHistory history2 = new JrcRatValueset.HscListHistory(); + history2.setInCommonList(false); + history2.setInMutualRecognition(true); + history2.setListDate(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(List.of(history1, history2)); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not updated."); + Assertions.assertEquals(2, updatedValueset.getValue().size(), "Valueset List size has been changed"); + Assertions.assertFalse(updatedValueset.getValue().get(RAT1_ID).getActive()); + Assertions.assertEquals(String.format("%s, %s", manufacturer.getName(), jrcValueset.getCommercialName()), updatedValueset.getValue().get(RAT1_ID).getDisplay()); + Assertions.assertEquals(history2.getListDate().toEpochSecond(), updatedValueset.getValue().get(RAT1_ID).getVersion().toEpochSecond()); + } + + @Test + void testRatValuesetUpdateActiveTrue() throws JsonProcessingException { + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset.HscListHistory history1 = new JrcRatValueset.HscListHistory(); + history1.setInCommonList(false); + history1.setInMutualRecognition(true); + history1.setListDate(ZonedDateTime.now().minus(3, ChronoUnit.DAYS)); + + JrcRatValueset.HscListHistory history2 = new JrcRatValueset.HscListHistory(); + history2.setInCommonList(true); + history2.setInMutualRecognition(true); + history2.setListDate(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(List.of(history1, history2)); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not updated."); + Assertions.assertEquals(2, updatedValueset.getValue().size(), "Valueset List size has been changed"); + Assertions.assertTrue(updatedValueset.getValue().get(RAT1_ID).getActive()); + Assertions.assertEquals(String.format("%s, %s", manufacturer.getName(), jrcValueset.getCommercialName()), updatedValueset.getValue().get(RAT1_ID).getDisplay()); + Assertions.assertEquals(history2.getListDate().toEpochSecond(), updatedValueset.getValue().get(RAT1_ID).getVersion().toEpochSecond()); + } + + @Test + void testRatValuesetInsertedIfNotExist() throws JsonProcessingException { + valuesetRepository.deleteAll(); + + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset.HscListHistory history1 = new JrcRatValueset.HscListHistory(); + history1.setInCommonList(false); + history1.setInMutualRecognition(true); + history1.setListDate(ZonedDateTime.now().minus(3, ChronoUnit.DAYS)); + + JrcRatValueset.HscListHistory history2 = new JrcRatValueset.HscListHistory(); + history2.setInCommonList(true); + history2.setInMutualRecognition(true); + history2.setListDate(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(List.of(history1, history2)); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not updated."); + Assertions.assertEquals(1, updatedValueset.getValue().size(), "Valueset List size has been changed"); + Assertions.assertTrue(updatedValueset.getValue().get(RAT1_ID).getActive()); + Assertions.assertEquals(String.format("%s, %s", manufacturer.getName(), jrcValueset.getCommercialName()), updatedValueset.getValue().get(RAT1_ID).getDisplay()); + Assertions.assertEquals(history2.getListDate().toEpochSecond(), updatedValueset.getValue().get(RAT1_ID).getVersion().toEpochSecond()); + } + + @Test + void testRatValuesetUpdatedIfJsonInDbIsInvalid() throws JsonProcessingException { + valuesetEntity.setJson("blablabla"); + valuesetRepository.save(valuesetEntity); + + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset.HscListHistory history1 = new JrcRatValueset.HscListHistory(); + history1.setInCommonList(false); + history1.setInMutualRecognition(true); + history1.setListDate(ZonedDateTime.now().minus(3, ChronoUnit.DAYS)); + + JrcRatValueset.HscListHistory history2 = new JrcRatValueset.HscListHistory(); + history2.setInCommonList(true); + history2.setInMutualRecognition(true); + history2.setListDate(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(List.of(history1, history2)); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not set."); + Assertions.assertEquals(1, updatedValueset.getValue().size()); + Assertions.assertTrue(updatedValueset.getValue().get(RAT1_ID).getActive()); + Assertions.assertEquals(String.format("%s, %s", manufacturer.getName(), jrcValueset.getCommercialName()), updatedValueset.getValue().get(RAT1_ID).getDisplay()); + Assertions.assertEquals(history2.getListDate().toEpochSecond(), updatedValueset.getValue().get(RAT1_ID).getVersion().toEpochSecond()); + } + + @Test + void testRatValuesetUpdatedSkipIfHistoryEmpty() throws JsonProcessingException { + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(Collections.emptyList()); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not set."); + Assertions.assertEquals(2, updatedValueset.getValue().size()); + Assertions.assertTrue(updatedValueset.getValue().get(RAT1_ID).getActive()); + assertEquals(rat1, updatedValueset.getValue().get(RAT1_ID)); + } + + @Test + void testRatValuesetUpdatedSkipIfHistoryNull() throws JsonProcessingException { + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(null); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not set."); + Assertions.assertEquals(2, updatedValueset.getValue().size()); + Assertions.assertTrue(updatedValueset.getValue().get(RAT1_ID).getActive()); + assertEquals(rat1, updatedValueset.getValue().get(RAT1_ID)); + } + + @Test + void testRatValuesetUpdateShouldNotUpdateWhenRequestFails() throws JsonProcessingException { + + doThrow(new FeignException.Unauthorized("", dummyRequest, null)) + .when(jrcClientMock).downloadRatValues(); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now().minus(1, ChronoUnit.DAYS), updatedValueset.getDate(), "Valueset Date has been updated."); + Assertions.assertEquals(2, updatedValueset.getValue().size(), "Valueset List size has been changed"); + assertEquals(rat1, updatedValueset.getValue().get(RAT1_ID)); + assertEquals(rat2, updatedValueset.getValue().get(RAT2_ID)); + } + + @Test + void testRatValuesetUpdateLatestAllHistoryEntriesAreInFuture() throws JsonProcessingException { + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset.HscListHistory history1 = new JrcRatValueset.HscListHistory(); + history1.setInCommonList(false); + history1.setInMutualRecognition(true); + history1.setListDate(ZonedDateTime.now().plus(3, ChronoUnit.DAYS)); + + JrcRatValueset.HscListHistory history2 = new JrcRatValueset.HscListHistory(); + history2.setInCommonList(true); + history2.setInMutualRecognition(true); + history2.setListDate(ZonedDateTime.now().plus(1, ChronoUnit.DAYS)); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(List.of(history1, history2)); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not updated."); + Assertions.assertEquals(2, updatedValueset.getValue().size(), "Valueset List size has been changed"); + Assertions.assertNull(updatedValueset.getValue().get(RAT1_ID).getActive()); + Assertions.assertEquals(String.format("%s, %s", manufacturer.getName(), jrcValueset.getCommercialName()), updatedValueset.getValue().get(RAT1_ID).getDisplay()); + Assertions.assertNull(updatedValueset.getValue().get(RAT1_ID).getVersion()); + assertEquals(history1.getListDate(), updatedValueset.getValue().get(RAT1_ID).getValidUntil()); + } + + @Test + void testRatValuesetUpdateLatestHistoryEntryNotInFuture() throws JsonProcessingException { + JrcRatValueset.Manufacturer manufacturer = new JrcRatValueset.Manufacturer(); + manufacturer.setId("1111"); + manufacturer.setCountry("eu"); + manufacturer.setName("Manufacturer Name"); + manufacturer.setWebsite("https://example.org"); + + JrcRatValueset.HscListHistory history1 = new JrcRatValueset.HscListHistory(); + history1.setInCommonList(false); + history1.setInMutualRecognition(true); + history1.setListDate(ZonedDateTime.now().minus(3, ChronoUnit.DAYS)); + + JrcRatValueset.HscListHistory history2 = new JrcRatValueset.HscListHistory(); + history2.setInCommonList(true); + history2.setInMutualRecognition(true); + history2.setListDate(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)); + + JrcRatValueset.HscListHistory history3 = new JrcRatValueset.HscListHistory(); + history3.setInCommonList(true); + history3.setInMutualRecognition(true); + history3.setListDate(ZonedDateTime.now().plus(1, ChronoUnit.DAYS)); + + JrcRatValueset jrcValueset = new JrcRatValueset(); + jrcValueset.setIdDevice(RAT1_ID); + jrcValueset.setCommercialName("New Com Name"); + jrcValueset.setManufacturer(manufacturer); + jrcValueset.setHscListHistory(List.of(history1, history2, history3)); + + JrcRatValuesetResponse jrcResponse = new JrcRatValuesetResponse(); + jrcResponse.setExtractedOn(ZonedDateTime.now()); + jrcResponse.setDeviceList(List.of(jrcValueset)); + + when(jrcClientMock.downloadRatValues()).thenReturn(jrcResponse); + + ratValuesetUpdateService.update(); + + String updatedValuesetJson = valuesetService.getValueSetById(RAT_VALUESET_ID).orElseThrow(); + Valueset updatedValueset = objectMapper.readValue(updatedValuesetJson, typeReference); + + Assertions.assertEquals(LocalDate.now(), updatedValueset.getDate(), "Valueset Date was not updated."); + Assertions.assertEquals(2, updatedValueset.getValue().size(), "Valueset List size has been changed"); + Assertions.assertEquals(history2.getInCommonList(), updatedValueset.getValue().get(RAT1_ID).getActive()); + Assertions.assertEquals(String.format("%s, %s", manufacturer.getName(), jrcValueset.getCommercialName()), updatedValueset.getValue().get(RAT1_ID).getDisplay()); + assertEquals(history2.getListDate(), updatedValueset.getValue().get(RAT1_ID).getVersion()); + assertEquals(history3.getListDate(), updatedValueset.getValue().get(RAT1_ID).getValidUntil()); + } + + void assertEquals(ZonedDateTime expected, ZonedDateTime given) { + Assertions.assertEquals(expected.toEpochSecond(), given.toEpochSecond()); + } + + void assertEquals(RatValueset expected, RatValueset given) { + Assertions.assertEquals(expected.getVersion().toEpochSecond(), given.getVersion().toEpochSecond()); + Assertions.assertEquals(expected.getActive(), given.getActive()); + Assertions.assertEquals(expected.getDisplay(), given.getDisplay()); + Assertions.assertEquals(expected.getLang(), given.getLang()); + Assertions.assertEquals(expected.getSystem(), given.getSystem()); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/service/SignerInformationServiceTest.java b/src/test/java/eu/europa/ec/dgc/gateway/service/SignerInformationServiceTest.java new file mode 100644 index 00000000..dc134e03 --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/service/SignerInformationServiceTest.java @@ -0,0 +1,360 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.SignerInformationRepository; +import eu.europa.ec.dgc.gateway.testdata.CertificateTestUtils; +import eu.europa.ec.dgc.gateway.testdata.DgcTestKeyStore; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Optional; +import org.bouncycastle.cert.X509CertificateHolder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SignerInformationServiceTest { + + @Autowired + DgcConfigProperties dgcConfigProperties; + + @Autowired + CertificateUtils certificateUtils; + + @Autowired + DgcTestKeyStore dgcTestKeyStore; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + SignerInformationRepository signerInformationRepository; + + @Autowired + SignerInformationService signerInformationService; + + private static final String countryCode = "EU"; + private static final String dummySignature = "randomStringAsSignatureWhichIsNotValidatedInServiceLevel"; + + @Test + void testSuccessfulAddingNewSignerInformationAndDelete() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + + Assertions.assertEquals(signerInformationEntitiesInDb + 1, signerInformationRepository.count()); + Optional createdSignerInformationEntity = + signerInformationRepository.getFirstByThumbprint(certificateUtils.getCertThumbprint(payloadCertificate)); + + Assertions.assertTrue(createdSignerInformationEntity.isPresent()); + + Assertions.assertEquals(SignerInformationEntity.CertificateType.DSC, createdSignerInformationEntity.get().getCertificateType()); + Assertions.assertEquals(countryCode, createdSignerInformationEntity.get().getCountry()); + Assertions.assertEquals(dummySignature, createdSignerInformationEntity.get().getSignature()); + Assertions.assertEquals(Base64.getEncoder().encodeToString(payloadCertificate.getEncoded()), createdSignerInformationEntity.get().getRawData()); + + signerInformationService.deleteSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + countryCode + ); + + Optional deletedSignerInformationEntity = + signerInformationRepository.getFirstByThumbprint(certificateUtils.getCertThumbprint(payloadCertificate)); + + Assertions.assertTrue(deletedSignerInformationEntity.isEmpty()); + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testAddingFailedConflict() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + + try { + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.ALREADY_EXIST_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb + 1, signerInformationRepository.count()); + } + + @Test + void testAddingFailedKidConflict() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + + Optional certInDbOptional = signerInformationRepository.getFirstByThumbprint(certificateUtils.getCertThumbprint(payloadCertificate)); + + Assertions.assertTrue(certInDbOptional.isPresent()); + + SignerInformationEntity certInDb = certInDbOptional.get(); + certInDb.setThumbprint(certInDb.getThumbprint().substring(0, 40) + "x".repeat(24)); // Generate new Hash with first 40 chars from ogirinal hash and add 24 x + + signerInformationRepository.save(certInDb); + + try { + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.KID_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb + 1, signerInformationRepository.count()); + } + + @Test + void testUploadFailedInvalidCSCA() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + // sign with TrustAnchor + X509Certificate cscaCertificate = dgcTestKeyStore.getTrustAnchor(); + PrivateKey cscaPrivateKey = dgcTestKeyStore.getTrustAnchorPrivateKey(); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + try { + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.CSCA_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testUploadFailedInvalidCSCAWrongCountryCode() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + // sign with CSCA from another country + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "XX"); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, "XX"); + + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + try { + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.CSCA_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testUploadFailedPayloadCertCountryWrong() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, "XX", "Payload Cert", cscaCertificate, cscaPrivateKey); + + try { + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.COUNTRY_OF_ORIGIN_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testUploadFailedWrongSignerCertificate() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "XX"); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + try { + signerInformationService.addSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + dummySignature, + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.UPLOADER_CERT_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testDeleteFailedNotExists() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + try { + signerInformationService.deleteSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.EXIST_CHECK_FAILED, e.getReason()); + } + } + + @Test + void testDeleteFailedPayloadCertCountryWrong() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, "XX", "Payload Cert", cscaCertificate, cscaPrivateKey); + + try { + signerInformationService.deleteSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.COUNTRY_OF_ORIGIN_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } + + @Test + void testDeleteFailedWrongSignerCertificate() throws Exception { + long signerInformationEntitiesInDb = signerInformationRepository.count(); + + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "XX"); + + X509Certificate cscaCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode); + PrivateKey cscaPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.CSCA, countryCode); + + KeyPair payloadKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate payloadCertificate = CertificateTestUtils.generateCertificate(payloadKeyPair, countryCode, "Payload Cert", cscaCertificate, cscaPrivateKey); + + try { + signerInformationService.deleteSignerCertificate( + new X509CertificateHolder(payloadCertificate.getEncoded()), + new X509CertificateHolder(signerCertificate.getEncoded()), + countryCode + ); + } catch (SignerInformationService.SignerCertCheckException e) { + Assertions.assertEquals(SignerInformationService.SignerCertCheckException.Reason.UPLOADER_CERT_CHECK_FAILED, e.getReason()); + } + + Assertions.assertEquals(signerInformationEntitiesInDb, signerInformationRepository.count()); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/service/TrustListServiceTest.java b/src/test/java/eu/europa/ec/dgc/gateway/service/TrustListServiceTest.java new file mode 100644 index 00000000..c75f6452 --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/service/TrustListServiceTest.java @@ -0,0 +1,193 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity; +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.model.TrustList; +import eu.europa.ec.dgc.gateway.model.TrustListType; +import eu.europa.ec.dgc.gateway.repository.SignerInformationRepository; +import eu.europa.ec.dgc.gateway.repository.TrustedPartyRepository; +import eu.europa.ec.dgc.gateway.testdata.CertificateTestUtils; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.security.KeyPairGenerator; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.time.ZonedDateTime; +import java.util.Base64; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class TrustListServiceTest { + + @Autowired + SignerInformationRepository signerInformationRepository; + + @Autowired + TrustedPartyRepository trustedPartyRepository; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + TrustListService trustListService; + + @Autowired + CertificateUtils certificateUtils; + + X509Certificate certUploadDe, certUploadEu, certCscaDe, certCscaEu, certAuthDe, certAuthEu, certDscDe, certDscEu; + + @BeforeEach + void testData() throws Exception { + trustedPartyRepository.deleteAll(); + signerInformationRepository.deleteAll(); + + certUploadDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "DE"); + certUploadEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "EU"); + certCscaDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "DE"); + certCscaEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "EU"); + certAuthDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "DE"); + certAuthEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU"); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ec"); + certDscDe = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "DE", "Test"); + certDscEu = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EU", "Test"); + + signerInformationRepository.save(new SignerInformationEntity( + null, + ZonedDateTime.now(), + "DE", + certificateUtils.getCertThumbprint(certDscDe), + Base64.getEncoder().encodeToString(certDscDe.getEncoded()), + "sig1", + SignerInformationEntity.CertificateType.DSC + )); + + signerInformationRepository.save(new SignerInformationEntity( + null, + ZonedDateTime.now(), + "EU", + certificateUtils.getCertThumbprint(certDscEu), + Base64.getEncoder().encodeToString(certDscEu.getEncoded()), + "sig2", + SignerInformationEntity.CertificateType.DSC + )); + } + + @Test + void testTrustListWithoutFilter() throws Exception { + List trustList = trustListService.getTrustList(); + + Assertions.assertEquals(8, trustList.size()); + + assertTrustListItem(trustList, certDscDe, "DE", TrustListType.DSC, "sig1"); + assertTrustListItem(trustList, certDscEu, "EU", TrustListType.DSC, "sig2"); + assertTrustListItem(trustList, certCscaDe, "DE", TrustListType.CSCA, null); + assertTrustListItem(trustList, certCscaEu, "EU", TrustListType.CSCA, null); + assertTrustListItem(trustList, certUploadDe, "DE", TrustListType.UPLOAD, null); + assertTrustListItem(trustList, certUploadEu, "EU", TrustListType.UPLOAD, null); + assertTrustListItem(trustList, certAuthDe, "DE", TrustListType.AUTHENTICATION, null); + assertTrustListItem(trustList, certAuthEu, "EU", TrustListType.AUTHENTICATION, null); + } + + @Test + void testTrustListFilterByType() throws Exception { + List trustList = trustListService.getTrustList(TrustListType.DSC); + Assertions.assertEquals(2, trustList.size()); + assertTrustListItem(trustList, certDscDe, "DE", TrustListType.DSC, "sig1"); + assertTrustListItem(trustList, certDscEu, "EU", TrustListType.DSC, "sig2"); + + trustList = trustListService.getTrustList(TrustListType.CSCA); + Assertions.assertEquals(2, trustList.size()); + assertTrustListItem(trustList, certCscaDe, "DE", TrustListType.CSCA, null); + assertTrustListItem(trustList, certCscaEu, "EU", TrustListType.CSCA, null); + + trustList = trustListService.getTrustList(TrustListType.UPLOAD); + Assertions.assertEquals(2, trustList.size()); + assertTrustListItem(trustList, certUploadDe, "DE", TrustListType.UPLOAD, null); + assertTrustListItem(trustList, certUploadEu, "EU", TrustListType.UPLOAD, null); + + trustList = trustListService.getTrustList(TrustListType.AUTHENTICATION); + Assertions.assertEquals(2, trustList.size()); + assertTrustListItem(trustList, certAuthDe, "DE", TrustListType.AUTHENTICATION, null); + assertTrustListItem(trustList, certAuthEu, "EU", TrustListType.AUTHENTICATION, null); + } + + @Test + void testTrustListFilterByTypeAndCountry() throws Exception { + List trustList = trustListService.getTrustList(TrustListType.DSC, "DE"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certDscDe, "DE", TrustListType.DSC, "sig1"); + trustList = trustListService.getTrustList(TrustListType.DSC, "EU"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certDscEu, "EU", TrustListType.DSC, "sig2"); + + trustList = trustListService.getTrustList(TrustListType.CSCA, "DE"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certCscaDe, "DE", TrustListType.CSCA, null); + trustList = trustListService.getTrustList(TrustListType.CSCA, "EU"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certCscaEu, "EU", TrustListType.CSCA, null); + + trustList = trustListService.getTrustList(TrustListType.UPLOAD, "DE"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certUploadDe, "DE", TrustListType.UPLOAD, null); + trustList = trustListService.getTrustList(TrustListType.UPLOAD, "EU"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certUploadEu, "EU", TrustListType.UPLOAD, null); + + trustList = trustListService.getTrustList(TrustListType.AUTHENTICATION, "DE"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certAuthDe, "DE", TrustListType.AUTHENTICATION, null); + trustList = trustListService.getTrustList(TrustListType.AUTHENTICATION, "EU"); + Assertions.assertEquals(1, trustList.size()); + assertTrustListItem(trustList, certAuthEu, "EU", TrustListType.AUTHENTICATION, null); + } + + private void assertTrustListItem(List trustList, X509Certificate certificate, String country, TrustListType trustListType, String signature) throws CertificateEncodingException { + Optional trustListOptional = trustList + .stream() + .filter(tl -> tl.getKid().equals(certificateUtils.getCertKid(certificate))) + .findFirst(); + + Assertions.assertTrue(trustListOptional.isPresent()); + + TrustList trustListItem = trustListOptional.get(); + + Assertions.assertEquals(certificateUtils.getCertKid(certificate), trustListItem.getKid()); + Assertions.assertEquals(country, trustListItem.getCountry()); + Assertions.assertEquals(trustListType, trustListItem.getCertificateType()); + Assertions.assertEquals(certificateUtils.getCertThumbprint(certificate), trustListItem.getThumbprint()); + Assertions.assertEquals(Base64.getEncoder().encodeToString(certificate.getEncoded()), trustListItem.getRawData()); + + if (signature != null) { + Assertions.assertEquals(signature, trustListItem.getSignature()); + } + } + +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/service/TrustedPartyServiceTest.java b/src/test/java/eu/europa/ec/dgc/gateway/service/TrustedPartyServiceTest.java new file mode 100644 index 00000000..51c6b28d --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/service/TrustedPartyServiceTest.java @@ -0,0 +1,162 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.TrustedPartyRepository; +import eu.europa.ec.dgc.gateway.testdata.DgcTestKeyStore; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.signing.SignedCertificateMessageBuilder; +import java.util.Optional; +import org.bouncycastle.cert.X509CertificateHolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class TrustedPartyServiceTest { + + @Autowired + TrustedPartyRepository trustedPartyRepository; + + @Autowired + TrustedPartyService trustedPartyService; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + DgcTestKeyStore dgcTestKeyStore; + + private static final String countryCode = "EU"; + + @AfterEach + void cleanUp() { + // We have to delete all certs after each test because some tests are manipulating certs in DB. + trustedPartyRepository.deleteAll(); + } + + @Test + void trustedPartyServiceShouldReturnCertificate() throws Exception { + String hash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + Optional certOptional = trustedPartyService.getCertificate(hash, countryCode, TrustedPartyEntity.CertificateType.UPLOAD); + Assertions.assertTrue(certOptional.isPresent()); + Assertions.assertEquals(hash, certOptional.get().getThumbprint()); + + hash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.CSCA, countryCode); + certOptional = trustedPartyService.getCertificate(hash, countryCode, TrustedPartyEntity.CertificateType.CSCA); + Assertions.assertTrue(certOptional.isPresent()); + Assertions.assertEquals(hash, certOptional.get().getThumbprint()); + + hash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + certOptional = trustedPartyService.getCertificate(hash, countryCode, TrustedPartyEntity.CertificateType.AUTHENTICATION); + Assertions.assertTrue(certOptional.isPresent()); + Assertions.assertEquals(hash, certOptional.get().getThumbprint()); + } + + @Test + void trustedPartyServiceShouldNotReturnCertificateIfIntegrityOfRawDataIsViolated() throws Exception { + Optional certOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.CSCA, countryCode), countryCode, TrustedPartyEntity.CertificateType.CSCA); + + Optional anotherCertOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode), countryCode, TrustedPartyEntity.CertificateType.AUTHENTICATION); + + Assertions.assertTrue(certOptional.isPresent()); + Assertions.assertTrue(anotherCertOptional.isPresent()); + + TrustedPartyEntity cert = certOptional.get(); + cert.setRawData(anotherCertOptional.get().getRawData()); + + trustedPartyRepository.save(cert); + + certOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.CSCA, countryCode), countryCode, TrustedPartyEntity.CertificateType.CSCA); + Assertions.assertTrue(certOptional.isEmpty()); + } + + @Test + void trustedPartyServiceShouldNotReturnCertificateIfIntegrityOfSignatureIsViolated() throws Exception { + Optional certOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.CSCA, countryCode), countryCode, TrustedPartyEntity.CertificateType.CSCA); + + Optional anotherCertOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode), countryCode, TrustedPartyEntity.CertificateType.AUTHENTICATION); + + Assertions.assertTrue(certOptional.isPresent()); + Assertions.assertTrue(anotherCertOptional.isPresent()); + + TrustedPartyEntity cert = certOptional.get(); + cert.setSignature(anotherCertOptional.get().getSignature()); + + trustedPartyRepository.save(cert); + + certOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.CSCA, countryCode), countryCode, TrustedPartyEntity.CertificateType.CSCA); + Assertions.assertTrue(certOptional.isEmpty()); + } + + @Test + void trustedPartyServiceShouldNotReturnCertificateIfIntegrityOfThumbprintIsViolated() throws Exception { + Optional certOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.CSCA, countryCode), countryCode, TrustedPartyEntity.CertificateType.CSCA); + + Optional anotherCertOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode), countryCode, TrustedPartyEntity.CertificateType.AUTHENTICATION); + + Assertions.assertTrue(certOptional.isPresent()); + Assertions.assertTrue(anotherCertOptional.isPresent()); + + trustedPartyRepository.delete(anotherCertOptional.get()); + + TrustedPartyEntity cert = certOptional.get(); + cert.setThumbprint(anotherCertOptional.get().getThumbprint()); + + trustedPartyRepository.save(cert); + + certOptional = trustedPartyService.getCertificate( + cert.getThumbprint(), countryCode, TrustedPartyEntity.CertificateType.CSCA); + Assertions.assertTrue(certOptional.isEmpty()); + } + + @Test + void trustedPartyServiceShouldNotReturnCertificateIfSignatureIsFromUnknownTrustAnchor() throws Exception { + Optional certOptional = trustedPartyService.getCertificate( + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.CSCA, countryCode), countryCode, TrustedPartyEntity.CertificateType.CSCA); + + // Create new signature with a random non TrustAnchor certificate + String newSignature = new SignedCertificateMessageBuilder() + .withSigningCertificate(new X509CertificateHolder(trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "XX").getEncoded()), trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, "XX")) + .withPayload(new X509CertificateHolder(trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, countryCode).getEncoded())) + .buildAsString(true); + + Assertions.assertTrue(certOptional.isPresent()); + + TrustedPartyEntity trustedPartyEntity = certOptional.get(); + trustedPartyEntity.setSignature(newSignature); + trustedPartyRepository.save(trustedPartyEntity); + + certOptional = trustedPartyService.getCertificate(trustedPartyEntity.getThumbprint(), countryCode, TrustedPartyEntity.CertificateType.CSCA); + Assertions.assertTrue(certOptional.isEmpty()); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/service/ValuesetServiceTest.java b/src/test/java/eu/europa/ec/dgc/gateway/service/ValuesetServiceTest.java new file mode 100644 index 00000000..e5883bde --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/service/ValuesetServiceTest.java @@ -0,0 +1,85 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.service; + +import eu.europa.ec.dgc.gateway.entity.ValuesetEntity; +import eu.europa.ec.dgc.gateway.repository.ValuesetRepository; +import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ValuesetServiceTest { + + @Autowired + ValuesetService valuesetService; + + @Autowired + ValuesetRepository valuesetRepository; + + @Autowired + TrustedPartyTestHelper trustedPartyTestHelper; + + @Autowired + CertificateUtils certificateUtils; + + @BeforeEach + void setup() { + valuesetRepository.deleteAll(); + } + + @Test + void testGetValuesetIds() { + ValuesetEntity valuesetEntity1 = new ValuesetEntity("vs-dummy-1", "content1"); + ValuesetEntity valuesetEntity2 = new ValuesetEntity("vs-dummy-2", "content2"); + ValuesetEntity valuesetEntity3 = new ValuesetEntity("vs-dummy-3", "content3"); + + valuesetRepository.save(valuesetEntity1); + valuesetRepository.save(valuesetEntity2); + valuesetRepository.save(valuesetEntity3); + + + List valuesetIds = valuesetService.getValuesetIds(); + Assertions.assertEquals(3, valuesetService.getValuesetIds().size()); + Assertions.assertTrue(valuesetIds.contains("vs-dummy-1")); + Assertions.assertTrue(valuesetIds.contains("vs-dummy-2")); + Assertions.assertTrue(valuesetIds.contains("vs-dummy-3")); + } + + @Test + void testGetValueset() { + ValuesetEntity valuesetEntity1 = new ValuesetEntity("vs-dummy-1", "content1"); + ValuesetEntity valuesetEntity2 = new ValuesetEntity("vs-dummy-2", "content2"); + + valuesetRepository.save(valuesetEntity1); + valuesetRepository.save(valuesetEntity2); + + Assertions.assertEquals(valuesetEntity1.getJson(), valuesetService.getValueSetById(valuesetEntity1.getId()).orElseThrow()); + Assertions.assertEquals(valuesetEntity2.getJson(), valuesetService.getValueSetById(valuesetEntity2.getId()).orElseThrow()); + } + + +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/testdata/CertificateTestUtils.java b/src/test/java/eu/europa/ec/dgc/gateway/testdata/CertificateTestUtils.java new file mode 100644 index 00000000..63b27c03 --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/testdata/CertificateTestUtils.java @@ -0,0 +1,142 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.testdata; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import eu.europa.ec.dgc.gateway.connector.model.ValidationRule; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.List; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.jupiter.api.Assertions; + +public class CertificateTestUtils { + + public static ValidationRule getDummyValidationRule() { + ValidationRule validationRule = new ValidationRule(); + + JsonNodeFactory jsonNodeFactory = JsonNodeFactory.instance; + + validationRule.setLogic(jsonNodeFactory.objectNode().set("field1", jsonNodeFactory.textNode("value1"))); + validationRule.setValidTo(ZonedDateTime.now().plus(1, ChronoUnit.WEEKS)); + validationRule.setValidFrom(ZonedDateTime.now().plus(3, ChronoUnit.DAYS)); + validationRule.setCertificateType("General"); + validationRule.setDescription(List.of(new ValidationRule.DescriptionItem("en", "de".repeat(10)))); + validationRule.setEngine("CERTLOGIC"); + validationRule.setEngineVersion("1.0.0"); + validationRule.setVersion("1.0.0"); + validationRule.setAffectedFields(List.of("AB", "DE")); + validationRule.setRegion("BW"); + validationRule.setSchemaVersion("1.0.0"); + validationRule.setType("Acceptance"); + validationRule.setIdentifier("GR-EU-0001"); + validationRule.setCountry("EU"); + + return validationRule; + } + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName) throws Exception { + Date validFrom = Date.from(Instant.now().minus(1, ChronoUnit.DAYS)); + Date validTo = Date.from(Instant.now().plus(365, ChronoUnit.DAYS)); + + return generateCertificate(keyPair, country, commonName, validFrom, validTo); + } + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName, X509Certificate ca, PrivateKey caKey) throws Exception { + Date validFrom = Date.from(Instant.now().minus(1, ChronoUnit.DAYS)); + Date validTo = Date.from(Instant.now().plus(365, ChronoUnit.DAYS)); + + return generateCertificate(keyPair, country, commonName, validFrom, validTo, ca, caKey); + } + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName, Date validFrom, Date validTo) throws Exception { + X500Name subject = new X500NameBuilder() + .addRDN(X509ObjectIdentifiers.countryName, country) + .addRDN(X509ObjectIdentifiers.commonName, commonName) + .build(); + + BigInteger certSerial = new BigInteger(Long.toString(System.currentTimeMillis())); + + ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256withECDSA").build(keyPair.getPrivate()); + + JcaX509v3CertificateBuilder certBuilder = + new JcaX509v3CertificateBuilder(subject, certSerial, validFrom, validTo, subject, keyPair.getPublic()); + + BasicConstraints basicConstraints = new BasicConstraints(false); + certBuilder.addExtension(Extension.basicConstraints, true, basicConstraints); + + return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner)); + } + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName, Date validFrom, Date validTo, X509Certificate ca, PrivateKey caKey) throws Exception { + X500Name subject = new X500NameBuilder() + .addRDN(X509ObjectIdentifiers.countryName, country) + .addRDN(X509ObjectIdentifiers.commonName, commonName) + .build(); + + X500Name issuer = new X509CertificateHolder(ca.getEncoded()).getSubject(); + + BigInteger certSerial = new BigInteger(Long.toString(System.currentTimeMillis())); + + ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256withECDSA").build(caKey); + + JcaX509v3CertificateBuilder certBuilder = + new JcaX509v3CertificateBuilder(issuer, certSerial, validFrom, validTo, subject, keyPair.getPublic()); + + BasicConstraints basicConstraints = new BasicConstraints(false); + certBuilder.addExtension(Extension.basicConstraints, true, basicConstraints); + + return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner)); + } + + public static void assertEquals(ValidationRule v1, ValidationRule v2) { + Assertions.assertEquals(v1.getIdentifier(), v2.getIdentifier()); + Assertions.assertEquals(v1.getType(), v2.getType()); + Assertions.assertEquals(v1.getCountry(), v2.getCountry()); + Assertions.assertEquals(v1.getRegion(), v2.getRegion()); + Assertions.assertEquals(v1.getVersion(), v2.getVersion()); + Assertions.assertEquals(v1.getSchemaVersion(), v2.getSchemaVersion()); + Assertions.assertEquals(v1.getEngine(), v2.getEngine()); + Assertions.assertEquals(v1.getEngineVersion(), v2.getEngineVersion()); + Assertions.assertEquals(v1.getCertificateType(), v2.getCertificateType()); + Assertions.assertEquals(v1.getDescription(), v2.getDescription()); + Assertions.assertEquals(v1.getValidFrom().toEpochSecond(), v2.getValidFrom().toEpochSecond()); + Assertions.assertEquals(v1.getValidTo().toEpochSecond(), v2.getValidTo().toEpochSecond()); + Assertions.assertEquals(v1.getAffectedFields(), v2.getAffectedFields()); + Assertions.assertEquals(v1.getLogic(), v2.getLogic()); + } + +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/testdata/DgcTestKeyStore.java b/src/test/java/eu/europa/ec/dgc/gateway/testdata/DgcTestKeyStore.java new file mode 100644 index 00000000..330865d4 --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/testdata/DgcTestKeyStore.java @@ -0,0 +1,79 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.testdata; + +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +import eu.europa.ec.dgc.gateway.config.DgcConfigProperties; +import java.io.IOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreSpi; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import lombok.Getter; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +@TestConfiguration +public class DgcTestKeyStore { + + private final DgcConfigProperties configProperties; + + @Getter + private final X509Certificate trustAnchor; + + @Getter + private final PrivateKey trustAnchorPrivateKey; + + public DgcTestKeyStore(DgcConfigProperties configProperties) throws Exception { + this.configProperties = configProperties; + + KeyPair keyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + trustAnchorPrivateKey = keyPair.getPrivate(); + + trustAnchor = CertificateTestUtils.generateCertificate(keyPair, "DE", "DGCG Test TrustAnchor"); + + } + + /** + * Creates a KeyStore instance with keys for DGC. + */ + @Bean + @Primary + public KeyStore testKeyStore() throws IOException, CertificateException, NoSuchAlgorithmException { + KeyStoreSpi keyStoreSpiMock = mock(KeyStoreSpi.class); + KeyStore keyStoreMock = new KeyStore(keyStoreSpiMock, null, "test") { + }; + keyStoreMock.load(null); + + doAnswer((x) -> trustAnchor) + .when(keyStoreSpiMock).engineGetCertificate(configProperties.getTrustAnchor().getCertificateAlias()); + + return keyStoreMock; + } + +} diff --git a/src/test/java/eu/europa/ec/dgc/gateway/testdata/TrustedPartyTestHelper.java b/src/test/java/eu/europa/ec/dgc/gateway/testdata/TrustedPartyTestHelper.java new file mode 100644 index 00000000..8b54cffd --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/gateway/testdata/TrustedPartyTestHelper.java @@ -0,0 +1,126 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.gateway.testdata; + +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.repository.TrustedPartyRepository; +import eu.europa.ec.dgc.signing.SignedCertificateMessageBuilder; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.bouncycastle.cert.X509CertificateHolder; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class TrustedPartyTestHelper { + + private final Map> hashMap = Map.of( + TrustedPartyEntity.CertificateType.AUTHENTICATION, new HashMap<>(), + TrustedPartyEntity.CertificateType.CSCA, new HashMap<>(), + TrustedPartyEntity.CertificateType.UPLOAD, new HashMap<>() + ); + + private final Map> certificateMap = Map.of( + TrustedPartyEntity.CertificateType.AUTHENTICATION, new HashMap<>(), + TrustedPartyEntity.CertificateType.CSCA, new HashMap<>(), + TrustedPartyEntity.CertificateType.UPLOAD, new HashMap<>() + ); + + private final Map> privateKeyMap = Map.of( + TrustedPartyEntity.CertificateType.AUTHENTICATION, new HashMap<>(), + TrustedPartyEntity.CertificateType.CSCA, new HashMap<>(), + TrustedPartyEntity.CertificateType.UPLOAD, new HashMap<>() + ); + + private final TrustedPartyRepository trustedPartyRepository; + + private final CertificateUtils certificateUtils; + + private final DgcTestKeyStore testKeyStore; + + public String getHash(TrustedPartyEntity.CertificateType type, String countryCode) throws Exception { + prepareTestCert(type, countryCode); + return hashMap.get(type).get(countryCode); + } + + public X509Certificate getCert(TrustedPartyEntity.CertificateType type, String countryCode) throws Exception { + prepareTestCert(type, countryCode); + return certificateMap.get(type).get(countryCode); + } + + public PrivateKey getPrivateKey(TrustedPartyEntity.CertificateType type, String countryCode) throws Exception { + prepareTestCert(type, countryCode); + return privateKeyMap.get(type).get(countryCode); + } + + private void prepareTestCert(TrustedPartyEntity.CertificateType type, String countryCode) throws Exception { + // Check if a test certificate already exists + if (!hashMap.get(type).containsKey(countryCode)) { + createAndInsertCert(type, countryCode); + } + + // Check if generated certificate is (still) present in DB + if (trustedPartyRepository.getFirstByThumbprintAndCertificateType( + hashMap.get(type).get(countryCode), type + ).isEmpty()) { + insertTestCert(type, countryCode); + } + } + + private void createAndInsertCert(TrustedPartyEntity.CertificateType type, String countryCode) throws Exception { + KeyPair keyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + X509Certificate authCertificate = + CertificateTestUtils.generateCertificate(keyPair, countryCode, "DGC Test " + type.name() + " Cert"); + String certHash = certificateUtils.getCertThumbprint(authCertificate); + + certificateMap.get(type).put(countryCode, authCertificate); + hashMap.get(type).put(countryCode, certHash); + privateKeyMap.get(type).put(countryCode, keyPair.getPrivate()); + + insertTestCert(type, countryCode); + } + + private void insertTestCert(TrustedPartyEntity.CertificateType type, String countryCode) throws Exception { + String certRawData = Base64.getEncoder().encodeToString( + certificateMap.get(type).get(countryCode).getEncoded()); + + String signature = new SignedCertificateMessageBuilder() + .withPayload(new X509CertificateHolder(certificateMap.get(type).get(countryCode).getEncoded())) + .withSigningCertificate(new X509CertificateHolder(testKeyStore.getTrustAnchor().getEncoded()), testKeyStore.getTrustAnchorPrivateKey()) + .buildAsString(true); + + TrustedPartyEntity trustedPartyEntity = new TrustedPartyEntity(); + trustedPartyEntity.setCertificateType(type); + trustedPartyEntity.setCountry(countryCode); + trustedPartyEntity.setSignature(signature); + trustedPartyEntity.setRawData(certRawData); + trustedPartyEntity.setThumbprint(hashMap.get(type).get(countryCode)); + + trustedPartyRepository.save(trustedPartyEntity); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 00000000..ad16a75e --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,36 @@ +server: + port: ${SERVER_PORT:8090} +spring: + profiles: + active: + - test + include: + - dev + application: + name: eu-interop-federation-gateway + liquibase: + enabled: true + change-log: classpath:db/changelog.xml + main: + allow-bean-definition-overriding: true +springdoc: + api-docs: + path: /api/docs + swagger-ui: + path: /swagger + +dgc: + jrc: + url: https://covid-19-diagnostics.jrc.ec.europa.eu/devices/hsc-common-recognition-rat + validationRuleSchema: classpath:validation-rule.schema.json + dbencryption: + initVector: Ho^RDYDuGt0Ki`\x + password: G&B3zSk|fNE!.Pa9+Xv2kUYRx2zp|@=| + trustAnchor: + keyStorePath: keystore/dgc-ta.jks + keyStorePass: dgc-p4ssw0rd + certificateAlias: dgc_trust_anchor + cert-auth: + header-fields: + thumbprint: X-SSL-Client-SHA256 + distinguished-name: X-SSL-Client-DN diff --git a/templates/file-header.txt b/templates/file-header.txt new file mode 100644 index 00000000..9af5493d --- /dev/null +++ b/templates/file-header.txt @@ -0,0 +1,19 @@ +/*- + * ---license-start + * eu-digital-green-certificates / dgc-gateway + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ diff --git a/templates/third-party.ftl b/templates/third-party.ftl new file mode 100644 index 00000000..03befca6 --- /dev/null +++ b/templates/third-party.ftl @@ -0,0 +1,30 @@ +<#function artifactFormat p> + <#return p.groupId + ":" + p.artifactId + ":" + p.version> + +<#function licenseFormat licenses> + <#assign result = ""/> + <#list licenses as license> + <#assign result = result + license + " "/> + + <#return result> + +ThirdPartyNotices +----------------- +This project uses third-party software or other resources that +may be distributed under licenses different from this software. +In the event that we overlooked to list a required notice, please bring this +to our attention by contacting us via this email: +opensource@telekom.de + +ThirdParty Licenses +----------------- + +| Dependency | License | +| --- | --- | +<#compress> + <#list dependencyMap as e> + <#assign project = e.getKey() /> + <#assign license = e.getValue() /> + | ${artifactFormat(project)} | ${licenseFormat(license)} | + + \ No newline at end of file

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 z%0D-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 List with Rules 1, 2, 3, 4 and 5 + +Current Timestamp is 23.06.2021 22:00:00\ +GET /rules/EU --> List with Rules 3, 4 and 5 + +Current Timestamp is 27.06.2021 22:00:00\ +GET /rules/EU --> List only with Rule 5 + +## Validation Rules Validation + +This documents describes the validation which will be executed when uploading a new Validation Rule to the gateway. + +### Signing + +The JSON file containing the ValidationRule has to be uploaded as a signed CMS. The signed CMS can be created with +the [SignedStringMessageBuilder](https://github.com/eu-digital-green-certificates/dgc-lib/blob/cdd10ea33df19e702828a2e7acc4cd563da1f6ea/src/main/java/eu/europa/ec/dgc/signing/SignedStringMessageBuilder.java) +of [dgc-lib](https://github.com/eu-digital-green-certificates/dgc-lib) +To sign the CMS a valid (onboarded) upload certificate must used. + +### Syntax + +The uploaded JSON file will be checked if it aligns to +the [JSON-Schema for ValidationRules](../src/main/resources/validation-rule.schema.json) + +### Content Checks + +In addition the content of the fields of the ValidationRule will be checked. + +| Field | Concerns To | Validation | Possible Error Message | +| --- | --- | --- | --- | +| Identifier | Acceptance Rules | Identifier must start with GR, VR, TR or RR | 400, Invalid RuleID | +| Identifier | Invalidation Rules | Identifier must start with IR | 400, Invalid RuleID | +| Identifier | All | Country in Identifier must be equal to Country of your authentication certificate | 403, Invalid Country sent | +| Country | All | Must be equal to Country of your authentication certificate | 403, Invalid Country sent | +| Version | Rules with previous version | Version of uploaded Rule must be higher than version of the last uploaded rule | 400, Invalid Version | +| ValidFrom | All | Value of ValidFrom must be before value of ValidTo | 400, Invalid Timestamp(s) | +| ValidFrom | All | Value of ValidFrom must be within 2 weeks from today | 400, Invalid Timestamp(s) | +| ValidFrom | Acceptance Rules | Value of ValidFrom must be at least 48h in future from today | 400, Invalid Timestamp(s) | +| ValidFrom | Invalidation Rules | Value of ValidFrom must be in future from today | 400, Invalid Timestamp(s) | +| ValidFrom | Rules with previous version | Value of ValidFrom must be after or equal to ValidFrom from ValidationRule of previous version | 400, Invalid Timestamp(s) | +| ValidTo | All | Value of ValidTo must be after value of ValidFrom | 400, Invalid Timestamp(s) | diff --git a/docs/api.png b/docs/api.png new file mode 100644 index 0000000000000000000000000000000000000000..476b52def42cbd13c5025ffcd13bbdfbbfac5f8d GIT binary patch literal 61277 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 for outgoing TLS connections (for call back), to allow mTLS authentication +- public key of DGCGTA Trust Anchor + +These keys need to be stored seperate from the database. They are stored in two different Java KeyStore (https://en.wikipedia.org/wiki/Java_KeyStore) and deployed manually to the Tomcat instances. The keystores are protected with a password, the password is set as JVM property. + +### Certificate Verification during OnBoarding + +Note that the onboarding process is *not* part of the DGCG Gateway (software). It is included here to inform the future operators of the EDGCGS and the operators of the member-states of key technical steps. The entire onboarding process will be defined separately as part of the overall e-Health network process. + +**SecReq-023** The Ditital Green Certificate Gateway (DGCG) upload endpoint MUST validate the Signing Certificate, which is sent in the PKCS#7 object (see SecReq-017), based on the requirements specified below. The file format is PKCS#12 (pfx) with a password. The password is communicated by to the DGCG by the Designated Country Technical Contact (DCTC) during a verification call where the DGCG contacts the DCTC to verify the authenticity of the upload and get the password. + +**SecReq-###** The Relative Distinguished Name(RDN) 'C' in the Distinguished Name (DN) must match the country of the the Country. + +**SecReq-###** The RDN 'emailAddress' in the Distinguished Name (DN) must match the 24x7 email address of the Country. + +**SecReq-###** The RNDs CN, O and (optional OU) should be populated with a set of human readable and operationally correct set of values. Such as '/CN=DGCGS Netherlands/OU=National Health Institute/O=Ministry of Public Health/C=NL'. + +**SecReq-###** The PKCS#12 (pfx) Should contain the complete chain, where applicable. + +**SecReq-###** If the Signing Certificate should be valid for at least 3 (more) month. The expiration is determined by the "notAfter" field (see [RFC 5280](https://tools.ietf.org/html/rfc5280#page-22)) of the certificate. + +**SecReq-###** The DGCG upload endpoint MUST verify the signature of the Signing Certificate. If validation failed, the DGCG upload endpoint MUST abort Onboarding.. + +**SecReq-###** In order to ensure maximum interoperability in a short timeline fields such as the Key Usage, Extended Key Usage will be operationally *ignored*. + +**SecReq-###** The X.509 certificate will be of version X.509 v3 (RFC5280). + +**SecReq-###** The key-lengths will meet or exceed the BSI Recommendations(2020) and the ECRYPT-CSA Recommendations(2018) for near term production: 3072 bits (RSA) or 256 bits (EC) and SHA256. + +### Certificate Verification during subsequent use and Upload + +-Digital Green Certificate Gateway (DGCG) upload endpoint MUST validate the Signing Certificate. + +**SecReq-###** If the Signing Certificate has expired, the DGCG upload endpoint MUST reject the upload request. The expiration is determined by the "notAfter" field (see [RFC 5280](https://tools.ietf.org/html/rfc5280#page-22)) of the certificate. + +**SecReq-###** The DGCG upload endpoint MUST verify the signature of the Signing Certificate. If validation failed, the DGCG upload endpoint MUST reject the upload request. + +**SecReq-026** To verify whether a Signing Certificate is whitelisted, the DGCG upload endpoint MUST execute the next steps: + +1. Extract the *Origin* value from the + +2. Extract the *Country (C)* attribute from the X-SSL-Client-DN request header (see SecReq-011). + +3. Compare the *Origin* with the *Country*. + + 1. If the Origin is not equal to Country, the upload endpoint MUST reject the signature, and thus, reject the upload request. Otherwise, continue with step 4. + +4. Extract the signing certificate (DER encoded) from the PKCS#7 object. + +5. Calculate the SHA-256 value of the extracted signing certificate. + +6. Query the DGCG Database using the calculated SHA-256 value and the Country (C) attribute. Also, the certificate type (see SecReq-028) MUST be used in the query. In this case, the type is: SIGNING. + + 1. If the query does not return any record, the upload endpoint MUST reject the signature, and thus, reject the upload request. + + 2. If the query returns a record, the upload endpoint MUST verify that the certificate has not been revoked. If the certificate was already revoked, the upload endpoint MUST reject the signature, and thus, reject the upload request. + + +## Certificate Requirements + +**SecReq-033** All certificates MUST be complied with the X.509 version 3 certificate standard (see [RFC 5280](https://tools.ietf.org/html/rfc5280)). + +**SecReq-034** All certificates MUST contain a Distinguished Name (DN) in the subject field. + +**SecReq-035** The Distinguished Name (DN) MUST have the Country (C) attribute, containing the [country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements) (e.g., NL) of the National Backend. + +-The Signing Certificates, which are used to verify the batch signature, CAN be self-signed. (this subject is likely to change) + +**SecReq-037** The Signing Certificates SHOULD set the Key Usage extension to "digitalSignature" (see [RFC 5280](https://tools.ietf.org/html/rfc5280#section-4.2.1.3)). + +-The Authentication Certificates, which are used to authenticate the National Backends, SHOULD set the Key Extended Usage extension to "clientAuth" (see [RFC 5280](https://tools.ietf.org/html/rfc5280#section-4.2.1.12)). + +###Cryptographic Requirements + +**SecReq-042** The cryptographic operations performed with the National Backends certificates MUST fulfill the following requirements: + +| Signature Algorithm | Minimum Key Length | Hash Algorithm | +|---------------------|--------------------|----------------| +| RSA | 2024 | SHA-256
SHA-384
SHA-512 | +| ECDSA | 250 | SHA-256
SHA-384
SHA-512 | + +The above requirements were defined based on the [BSI recommendations](https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR02102/BSI-TR-02102.pdf?__blob=publicationFile&v=10) for cryptographic algorithms and key lengths. + + +# Deployment View +The system contains different stages which reflect the different perspectives +to the deployed software. + +Stages: +- DEV +- TEST +- ACC +- PROD + +## Generic Deployment View +This view is a generic view which outlines the structure, but does not contain +any specifics related to a stage. + +![Generic Deployment View](DGCG-deployment-template.png "Generic Deployment View") + + + +## General Software Versions and config + +Tomcat hosting service – tomcat instances that are controlled together. Deployment is performed using Nexus artefact repository. We confirmed in the meantime that the rolling upgrade is possible, but we still need to analyse the requirements against the service capabilities +- Tomcat version: Tomcat 9.0.37 +- JDK version : JDK 11 (OpenJDK) +- Heap Size (MB): 8GB +- Meta Space Size (MB): dynamic/default (currently we are not able to specify this) +- Direct Memory Size (MB): default (currently we are not able to specify this) + +MySQL – Supported version: 5.7 +- Required information to create the instance +- Character Set : utf8|latin1|utf16|utf32|other>: utf8 +- Estimated DB Size: 10 GB +- Required capacity of the VM (GB of memory and number of vCPU) - 4 cores 16 GB RAM +- Number of concurrent users: 1 User for the application with max 28 sessions to store data + + +## Stage DEV - Development +As per beginning of the project a dev environment exists in the OTC allowing quick +and easy access for developer. + +Scaling Level +- single worker node + + +Security Level +- full security + +Test Data +- has a number of countries preloaded + +### Sizing TEST +Proposal +- Worker Nodes 3x [4 x Cores, 16 GB RAM, 10 GB free Storage] +- Database equivalent to 2x [4 Cores and 16 GB RAM] + + +## Stage TEST +Scaling Level +- fully scaled + +Security Level +- full security + +Test Data +- has a number of countries preloaded + +### Sizing TEST +Proposal +- Worker Nodes 3x [4 x Cores, 16 GB RAM, 10 GB free Storage] +- Database equivalent to 2x [4 Cores and 16 GB RAM] + + +## Stage ACC +## Stage PROD +### Sizing PROD + +Proposal +- Worker Nodes 3x [4 x Cores, 16 GB RAM, 10 GB free Storage] +- Database equivalent to 2x [4 Cores and 16 GB RAM] + +# Data Deletion +The data base stores + +# Other Constraints and Conditions +Timezone all times and dates are interpreted as timestamps in UTC (https://en.wikipedia.org/wiki/Coordinated_Universal_Time) diff --git a/docs/triangle_of_trust.png b/docs/triangle_of_trust.png new file mode 100644 index 0000000000000000000000000000000000000000..c605792a6ed6115674f1e127654915a3bc85ac68 GIT binary patch literal 51142 zcmd>mg;$l^7O$Y_rld>hlr9laLN_2KA+XtWH;9tbC~mq01Oy2w>24$h>5!0=hK(TI z9dCW!d(V0AZ+MJx#u?{)ixsoyZ_Y`us69p3QI$11<&6FaZPOt_MUYW?m_{ojErm{VPPK>c^fo5_E*v_n_Sj3$>e-bmMAvl z7#X+P2^FN#(&CsAF77Z;x& zPQJACmSkjv-??w~8IyyEXo3$#OeFfmz#wJHbF0B|FF=Cq%n9&-8HL;`qR~X#gVd;rFg?UdJl9b5g>J^ zE%c7q=c=mj?<|VG_;tTwHHj~v!hP@e?s|Fu-&ec?ugJk8WY(>==RTTpiZrvdTpL;T zp9{GbO0Ou54*$!+YimB5xoTOxYd$9W&u@&AbL+@iSbQ;1l1A@9-xb*J-`7|jE9Tv5 zJS#FlaC~*$P&@0sI$bM& z?bCx7kfe!`13Ep&?n@L>?6V@k!yC@$k$d_yV9H~yaQEAFf)&6p+ED1JH=<)=M$-3_ zAG!?XC$&r=eRk1Z&@E+6pj*DZyf{-lSRGlKYkubutBzh??s4iD&n7R83=baxEEW69 zQg>b)rK_N~QmHqc7H>-ZORwcfk^{!UD z&TtqjyRBdERtiyrAEHLc+lzUA)9I15*A|wRJr_r_#h9;jbP^$s5JBImgCZj00`Tv4 ze|p)Ni>z&J55Lh~gkWzgz+f=Aqe zKKI3O>kdCil)TuFF<4SFHa6x!9j;HP1F0<&gT6m^aYG*5C|WIFvoO>9><}VE7C%@(78Dv9V_V#ync{t@ zJ;J!k15W&+BSrKc&>0J2Cx%mX`tC2fOStpuu>2iid#>Q%VCc75px>s7pcZ8mvK{@< zFL|X`?1($w)Tj{4F@;PC61xe0*(x>`H$@@kaRij?%s2Ku)_vK4*laj5=<4em$)El) zoU1;ve)0J;J+w&e@1k%@et4x{k^WcBVn;<-H>?--12!Nnt=R@7+klgUL*eqE*jN5& zbNYazx(e8;%8Z{S#^3j$dl*6YIB32_mvb>MB_#zG1Qv?jy>CUIt(*)=HrHDqw)I1Y z8BNiqmsp6_IL!T3w^NS0_ zkr+|b2w+Im7&Z*18WU9@@m-9CMMg>0W4rkl3n!;yF)O2^EigoAgqpf~mUg+tZm(ef z3L*~ToKS&Ldl(f&bX`b|i~TOY
wEEGZB&nrlsG%yO|=z7=d%qnAG+7ltLmwH$FFJ~)(E{Jy%Fy^`;UpgG_uE_2&4 zA#2T6`t$|d0U-k2LFC;{{OHw@{GOQxPp~37HI-LI{NTg=mos|A#KikHjOjR#=+Q2% z3}$e=IozDqE#6f)JK91vMPgjP30Xt35Fl1!IER5Re^s#SUas$N%^KQ_^oZIe?)deaX&q$THhM(fzv*y2$jZ+<}mm-TF8Lxugc&d|@F2R4k1@sMEk zd_V4g8h%H9tVsWWqpaM1`XSk{Yy@>KM0IL#6>Z=#8nUZ$TU}$F?ZrLzRo{w>iRnQU zV+ZsotI+@Inp+wH9CU+$%yT`$HDDCsD{YPAA&BE1JcFEcv=7vDHLb)-R+4kRwPzu%Tm^*9m;5UB