From ab1459011c78165c1d47fdddbf4e15e66e9e3be3 Mon Sep 17 00:00:00 2001 From: Felix Dittrich <31076102+f11h@users.noreply.github.com> Date: Wed, 12 Jan 2022 14:32:31 +0100 Subject: [PATCH] Feat: Authorisation for Revocation Endpoints (#146) * Add required authorisation for Revocation Endpoints * Remove ValueMappers Wrapper --- .../config/ValidationRuleSchemaProvider.java | 4 +- .../gateway/entity/TrustedPartyEntity.java | 27 + .../CertificateRevocationListController.java | 11 +- .../CertificateAuthenticationFilter.java | 48 ++ .../CertificateAuthenticationRequired.java | 7 + .../filter/CertificateAuthenticationRole.java | 29 ++ .../restapi/mapper/CertificateRoleMapper.java | 36 ++ src/main/resources/db/changelog.xml | 1 + .../changelog/add-certificate-roles-table.xml | 21 + src/main/resources/db/snapshot.json | 486 ++++++++++-------- ...tificateRevocationListIntegrationTest.java | 117 +++++ .../testdata/TrustedPartyTestHelper.java | 12 + 12 files changed, 575 insertions(+), 224 deletions(-) create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRole.java create mode 100644 src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/CertificateRoleMapper.java create mode 100644 src/main/resources/db/changelog/add-certificate-roles-table.xml 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 index 44d05382..0ad06a3c 100644 --- a/src/main/java/eu/europa/ec/dgc/gateway/config/ValidationRuleSchemaProvider.java +++ b/src/main/java/eu/europa/ec/dgc/gateway/config/ValidationRuleSchemaProvider.java @@ -20,8 +20,6 @@ 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; @@ -46,7 +44,7 @@ public class ValidationRuleSchemaProvider { private Schema validationRuleSchema; @PostConstruct - void setup() throws FileNotFoundException, IOException { + void setup() throws IOException { InputStream schemaInputStream = ResourceUtils.getURL(configProperties.getValidationRuleSchema()).openStream(); try { 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 index 9f0efd7d..51371fa1 100644 --- a/src/main/java/eu/europa/ec/dgc/gateway/entity/TrustedPartyEntity.java +++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/TrustedPartyEntity.java @@ -21,10 +21,14 @@ package eu.europa.ec.dgc.gateway.entity; import java.time.ZonedDateTime; +import java.util.List; +import javax.persistence.CollectionTable; import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -81,6 +85,12 @@ public class TrustedPartyEntity { @Enumerated(EnumType.STRING) CertificateType certificateType; + @Enumerated(EnumType.STRING) + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "trusted_party_roles") + @Column(name = "role") + List certificateRoles; + public enum CertificateType { /** * Certificate which the member state is using to authenticate at DGC Gateway (NBTLS). @@ -97,4 +107,21 @@ public enum CertificateType { */ CSCA } + + public enum CertificateRoles { + /** + * User with this certificate is allowed to download Revocation List. + */ + REVOCATION_LIST_READER, + + /** + * User with this certificate is allowed to upload Revocation List. + */ + REVOCATION_UPLOADER, + + /** + * User with this certificate is allowed to delete Revocation List. + */ + REVOCATION_DELETER + } } diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java index 713a34b2..cfcebdd0 100644 --- a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java @@ -31,6 +31,7 @@ import eu.europa.ec.dgc.gateway.restapi.dto.revocation.RevocationBatchListDto; 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.filter.CertificateAuthenticationRole; import eu.europa.ec.dgc.gateway.restapi.mapper.RevocationBatchMapper; import eu.europa.ec.dgc.gateway.service.RevocationListService; import io.swagger.v3.oas.annotations.Operation; @@ -79,7 +80,7 @@ public class CertificateRevocationListController { /** * Endpoint to download Revocation Batch List. */ - @CertificateAuthenticationRequired + @CertificateAuthenticationRequired(requiredRoles = CertificateAuthenticationRole.RevocationListReader) @GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE) @Operation( security = { @@ -129,7 +130,7 @@ public ResponseEntity downloadBatchList( /** * Endpoint to download Revocation Batch. */ - @CertificateAuthenticationRequired + @CertificateAuthenticationRequired(requiredRoles = CertificateAuthenticationRole.RevocationListReader) @GetMapping(value = "/{batchId}", produces = { CmsStringMessageConverter.CONTENT_TYPE_CMS_TEXT_VALUE, CmsStringMessageConverter.CONTENT_TYPE_CMS_VALUE}) @Operation( @@ -192,7 +193,7 @@ public ResponseEntity downloadBatch( /** * Endpoint to upload Revocation Batch. */ - @CertificateAuthenticationRequired + @CertificateAuthenticationRequired(requiredRoles = CertificateAuthenticationRole.RevocationUploader) @PostMapping(value = "", consumes = { CmsStringMessageConverter.CONTENT_TYPE_CMS_TEXT_VALUE, CmsStringMessageConverter.CONTENT_TYPE_CMS_VALUE}) @Operation( @@ -269,7 +270,7 @@ public ResponseEntity uploadBatch( /** * Endpoint to delete Revocation Batch. */ - @CertificateAuthenticationRequired + @CertificateAuthenticationRequired(requiredRoles = CertificateAuthenticationRole.RevocationDeleter) @DeleteMapping(value = "", consumes = { CmsStringMessageConverter.CONTENT_TYPE_CMS_TEXT_VALUE, CmsStringMessageConverter.CONTENT_TYPE_CMS_VALUE}) @Operation( @@ -344,7 +345,7 @@ public ResponseEntity deleteBatch( /** * Alternative endpoint to delete revocation batches. */ - @CertificateAuthenticationRequired + @CertificateAuthenticationRequired(requiredRoles = CertificateAuthenticationRole.RevocationDeleter) @PostMapping(value = "/delete", consumes = { CmsStringMessageConverter.CONTENT_TYPE_CMS_TEXT_VALUE, CmsStringMessageConverter.CONTENT_TYPE_CMS_VALUE}) public ResponseEntity deleteBatchAlternativeEndpoint( 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 index 7d780f0b..6647f831 100644 --- 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 @@ -23,6 +23,7 @@ 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.restapi.mapper.CertificateRoleMapper; import eu.europa.ec.dgc.gateway.service.TrustedPartyService; import eu.europa.ec.dgc.gateway.utils.DgcMdc; import java.io.IOException; @@ -65,6 +66,8 @@ public class CertificateAuthenticationFilter extends OncePerRequestFilter { private final HandlerExceptionResolver handlerExceptionResolver; + private final CertificateRoleMapper certificateRoleMapper; + @Override protected boolean shouldNotFilter(HttpServletRequest request) { try { @@ -180,6 +183,19 @@ protected void doFilterInternal( return; } + if (!checkRequiredRoles(httpServletRequest, certFromDb.get())) { + log.error("Missing permissions to access this endpoint."); + handlerExceptionResolver.resolveException( + httpServletRequest, httpServletResponse, null, + new DgcgResponseException( + HttpStatus.FORBIDDEN, + "0x403", + "Client is not authorized to access the endpoint", + "", "")); + + return; + } + log.info("Successful Authentication"); httpServletRequest.setAttribute(REQUEST_PROP_COUNTRY, distinguishNameMap.get("C")); httpServletRequest.setAttribute(REQUEST_PROP_THUMBPRINT, headerCertThumbprint); @@ -187,6 +203,38 @@ protected void doFilterInternal( filterChain.doFilter(httpServletRequest, httpServletResponse); } + private boolean checkRequiredRoles(HttpServletRequest request, TrustedPartyEntity entity) { + HandlerExecutionChain handlerExecutionChain; + try { + handlerExecutionChain = requestMap.getHandler(request); + } catch (Exception e) { + log.error("Failed to extract required roles from request."); + return false; + } + + if (handlerExecutionChain == null) { + log.error("Failed to extract required roles from request."); + return false; + } + + CertificateAuthenticationRole[] requiredRoles = ((HandlerMethod) handlerExecutionChain.getHandler()) + .getMethod().getAnnotation(CertificateAuthenticationRequired.class).requiredRoles(); + + if (requiredRoles.length == 0) { + log.debug("Endpoint requires no special roles."); + return true; + } + + for (CertificateAuthenticationRole requiredRole : requiredRoles) { + if (!entity.getCertificateRoles().contains(certificateRoleMapper.dtoToEntity(requiredRole))) { + log.error("Role {} is required to access endpoint", requiredRole.name()); + return false; + } + } + + return true; + } + /** * Parses a given Distinguish Name string (e.g. "C=DE,OU=Test Unit,O=Test Company"). * 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 index 5ddc1a20..4e5ec3de 100644 --- 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 @@ -28,4 +28,11 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CertificateAuthenticationRequired { + + /** + * List of {@link CertificateAuthenticationRole} which are required to access this endpoint. + * All mentioned roles must be assigned to the certificate. + */ + CertificateAuthenticationRole[] requiredRoles() default {}; + } diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRole.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRole.java new file mode 100644 index 00000000..93af0d41 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/filter/CertificateAuthenticationRole.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.restapi.filter; + +public enum CertificateAuthenticationRole { + + RevocationListReader, + RevocationUploader, + RevocationDeleter + +} diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/CertificateRoleMapper.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/CertificateRoleMapper.java new file mode 100644 index 00000000..ac49f906 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/mapper/CertificateRoleMapper.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.restapi.mapper; + +import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity; +import eu.europa.ec.dgc.gateway.restapi.filter.CertificateAuthenticationRole; +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; + +@Mapper(componentModel = "spring") +public interface CertificateRoleMapper { + + @ValueMapping(source = "RevocationListReader", target = "REVOCATION_LIST_READER") + @ValueMapping(source = "RevocationUploader", target = "REVOCATION_UPLOADER") + @ValueMapping(source = "RevocationDeleter", target = "REVOCATION_DELETER") + TrustedPartyEntity.CertificateRoles dtoToEntity(CertificateAuthenticationRole dto); + +} diff --git a/src/main/resources/db/changelog.xml b/src/main/resources/db/changelog.xml index 8d2049ac..222c0202 100644 --- a/src/main/resources/db/changelog.xml +++ b/src/main/resources/db/changelog.xml @@ -13,4 +13,5 @@ + diff --git a/src/main/resources/db/changelog/add-certificate-roles-table.xml b/src/main/resources/db/changelog/add-certificate-roles-table.xml new file mode 100644 index 00000000..bdd6ca8c --- /dev/null +++ b/src/main/resources/db/changelog/add-certificate-roles-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/snapshot.json b/src/main/resources/db/snapshot.json index abec0eb6..f304c4f0 100644 --- a/src/main/resources/db/snapshot.json +++ b/src/main/resources/db/snapshot.json @@ -1,14 +1,14 @@ { "snapshot": { - "created": "2021-12-14T17:40:13.849", + "created": "2022-01-12T13:31:11.203", "database": { - "productVersion": "2021.2.2", + "productVersion": "2021.2.4", "shortName": "intellijPsiClass", "majorVersion": "0", "minorVersion": "0", "user": "A34636994", "productName": "JPA Buddy Intellij", - "url": "jpab?generationContext=4d2a525d-6d08-4fbe-8d56-c9ef225eda29" + "url": "jpab?generationContext=a1bfd752-104e-46e2-9410-743e9728f086" }, "metadata": { "generationContext": { @@ -21,7 +21,7 @@ "catalog": { "default": true, "name": "JPA_BUDDY", - "snapshotId": "c0fc100" + "snapshotId": "100d940" } } ], @@ -31,8 +31,8 @@ "certainDataType": false, "name": "authentication_sha256_fingerprint", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc116", - "snapshotId": "c0fc122", + "relation": "liquibase.structure.core.Table#100d971", + "snapshotId": "100d977", "type": { "columnSize": "64!{java.lang.Integer}", "typeName": "VARCHAR" @@ -44,8 +44,8 @@ "certainDataType": false, "name": "batch_id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc104", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d944", "type": { "columnSize": "36!{java.lang.Integer}", "typeName": "VARCHAR" @@ -57,8 +57,8 @@ "certainDataType": false, "name": "certificate_type", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc126", - "snapshotId": "c0fc135", + "relation": "liquibase.structure.core.Table#100d981", + "snapshotId": "100d990", "type": { "columnSize": "255!{java.lang.Integer}", "typeName": "VARCHAR" @@ -70,8 +70,8 @@ "certainDataType": false, "name": "certificate_type", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc155", - "snapshotId": "c0fc164", + "relation": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d969", "type": { "columnSize": "255!{java.lang.Integer}", "typeName": "VARCHAR" @@ -83,8 +83,8 @@ "certainDataType": false, "name": "changed", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc109", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d949", "type": { "typeName": "DATETIME" } @@ -95,8 +95,8 @@ "certainDataType": false, "name": "country", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc116", - "snapshotId": "c0fc120", + "relation": "liquibase.structure.core.Table#100d971", + "snapshotId": "100d975", "type": { "columnSize": "2!{java.lang.Integer}", "typeName": "VARCHAR" @@ -108,8 +108,8 @@ "certainDataType": false, "name": "country", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc108", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d948", "type": { "columnSize": "2!{java.lang.Integer}", "typeName": "VARCHAR" @@ -121,8 +121,8 @@ "certainDataType": false, "name": "country", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc126", - "snapshotId": "c0fc132", + "relation": "liquibase.structure.core.Table#100d981", + "snapshotId": "100d987", "type": { "columnSize": "2!{java.lang.Integer}", "typeName": "VARCHAR" @@ -134,8 +134,8 @@ "certainDataType": false, "name": "country", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc155", - "snapshotId": "c0fc161", + "relation": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d966", "type": { "columnSize": "2!{java.lang.Integer}", "typeName": "VARCHAR" @@ -147,8 +147,8 @@ "certainDataType": false, "name": "country", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc147", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d1002", "type": { "columnSize": "2!{java.lang.Integer}", "typeName": "VARCHAR" @@ -160,8 +160,8 @@ "certainDataType": false, "name": "created_at", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc126", - "snapshotId": "c0fc131", + "relation": "liquibase.structure.core.Table#100d981", + "snapshotId": "100d986", "type": { "typeName": "DATETIME" } @@ -172,8 +172,8 @@ "certainDataType": false, "name": "created_at", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc155", - "snapshotId": "c0fc160", + "relation": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d965", "type": { "typeName": "DATETIME" } @@ -184,8 +184,8 @@ "certainDataType": false, "name": "created_at", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc143", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d998", "type": { "typeName": "DATETIME" } @@ -196,8 +196,8 @@ "certainDataType": false, "name": "deleted", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc111", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d951", "type": { "typeName": "BOOLEAN" } @@ -208,8 +208,8 @@ "certainDataType": false, "name": "description", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc116", - "snapshotId": "c0fc124", + "relation": "liquibase.structure.core.Table#100d971", + "snapshotId": "100d979", "type": { "columnSize": "64!{java.lang.Integer}", "typeName": "VARCHAR" @@ -221,8 +221,8 @@ "certainDataType": false, "name": "event", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc116", - "snapshotId": "c0fc123", + "relation": "liquibase.structure.core.Table#100d971", + "snapshotId": "100d978", "type": { "columnSize": "64!{java.lang.Integer}", "typeName": "VARCHAR" @@ -234,8 +234,8 @@ "certainDataType": false, "name": "expires", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc110", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d950", "type": { "typeName": "DATETIME" } @@ -250,8 +250,8 @@ "certainDataType": false, "name": "id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc116", - "snapshotId": "c0fc118", + "relation": "liquibase.structure.core.Table#100d971", + "snapshotId": "100d973", "type": { "typeName": "BIGINT" } @@ -266,8 +266,8 @@ "certainDataType": false, "name": "id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc106", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d946", "type": { "typeName": "BIGINT" } @@ -282,8 +282,8 @@ "certainDataType": false, "name": "id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc126", - "snapshotId": "c0fc130", + "relation": "liquibase.structure.core.Table#100d981", + "snapshotId": "100d985", "type": { "typeName": "BIGINT" } @@ -298,8 +298,8 @@ "certainDataType": false, "name": "id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc155", - "snapshotId": "c0fc159", + "relation": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d960", "type": { "typeName": "BIGINT" } @@ -314,8 +314,8 @@ "certainDataType": false, "name": "id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc142", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d997", "type": { "typeName": "BIGINT" } @@ -326,8 +326,8 @@ "certainDataType": false, "name": "id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc150", - "snapshotId": "c0fc152", + "relation": "liquibase.structure.core.Table#100d1005", + "snapshotId": "100d1007", "type": { "columnSize": "100!{java.lang.Integer}", "typeName": "VARCHAR" @@ -339,8 +339,8 @@ "certainDataType": false, "name": "json", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc150", - "snapshotId": "c0fc153", + "relation": "liquibase.structure.core.Table#100d1005", + "snapshotId": "100d1008", "type": { "typeName": "CLOB" } @@ -351,8 +351,8 @@ "certainDataType": false, "name": "kid", "nullable": true, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc113", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d953", "type": { "columnSize": "12!{java.lang.Integer}", "typeName": "VARCHAR" @@ -364,8 +364,8 @@ "certainDataType": false, "name": "raw_data", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc126", - "snapshotId": "c0fc133", + "relation": "liquibase.structure.core.Table#100d981", + "snapshotId": "100d988", "type": { "columnSize": "4096!{java.lang.Integer}", "typeName": "VARCHAR" @@ -377,21 +377,33 @@ "certainDataType": false, "name": "raw_data", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc155", - "snapshotId": "c0fc162", + "relation": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d967", "type": { "columnSize": "4096!{java.lang.Integer}", "typeName": "VARCHAR" } } }, + { + "column": { + "certainDataType": false, + "name": "role", + "nullable": true, + "relation": "liquibase.structure.core.Table#100d956", + "snapshotId": "100d958", + "type": { + "typeName": "VARCHAR" + } + } + }, { "column": { "certainDataType": false, "name": "rule_id", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc139", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d994", "type": { "columnSize": "100!{java.lang.Integer}", "typeName": "VARCHAR" @@ -403,8 +415,8 @@ "certainDataType": false, "name": "signature", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc126", - "snapshotId": "c0fc134", + "relation": "liquibase.structure.core.Table#100d981", + "snapshotId": "100d989", "type": { "columnSize": "6000!{java.lang.Integer}", "typeName": "VARCHAR" @@ -416,8 +428,8 @@ "certainDataType": false, "name": "signature", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc155", - "snapshotId": "c0fc163", + "relation": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d968", "type": { "columnSize": "6000!{java.lang.Integer}", "typeName": "VARCHAR" @@ -429,8 +441,8 @@ "certainDataType": false, "name": "signature", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc144", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d999", "type": { "columnSize": "10000!{java.lang.Integer}", "typeName": "VARCHAR" @@ -442,8 +454,8 @@ "certainDataType": false, "name": "signed_batch", "nullable": true, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc114", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d954", "type": { "typeName": "CLOB" } @@ -454,8 +466,8 @@ "certainDataType": false, "name": "thumbprint", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc126", - "snapshotId": "c0fc128", + "relation": "liquibase.structure.core.Table#100d981", + "snapshotId": "100d983", "type": { "columnSize": "64!{java.lang.Integer}", "typeName": "VARCHAR" @@ -467,8 +479,8 @@ "certainDataType": false, "name": "thumbprint", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc155", - "snapshotId": "c0fc157", + "relation": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d963", "type": { "columnSize": "64!{java.lang.Integer}", "typeName": "VARCHAR" @@ -480,20 +492,32 @@ "certainDataType": false, "name": "timestamp", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc116", - "snapshotId": "c0fc119", + "relation": "liquibase.structure.core.Table#100d971", + "snapshotId": "100d974", "type": { "typeName": "DATETIME" } } }, + { + "column": { + "certainDataType": false, + "name": "trusted_party_entity_id", + "nullable": false, + "relation": "liquibase.structure.core.Table#100d956", + "snapshotId": "100d957", + "type": { + "typeName": "BIGINT" + } + } + }, { "column": { "certainDataType": false, "name": "type", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc102", - "snapshotId": "c0fc112", + "relation": "liquibase.structure.core.Table#100d942", + "snapshotId": "100d952", "type": { "columnSize": "255!{java.lang.Integer}", "typeName": "VARCHAR" @@ -505,8 +529,8 @@ "certainDataType": false, "name": "type", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc148", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d1003", "type": { "columnSize": "255!{java.lang.Integer}", "typeName": "VARCHAR" @@ -518,8 +542,8 @@ "certainDataType": false, "name": "uploader_sha256_fingerprint", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc116", - "snapshotId": "c0fc121", + "relation": "liquibase.structure.core.Table#100d971", + "snapshotId": "100d976", "type": { "columnSize": "64!{java.lang.Integer}", "typeName": "VARCHAR" @@ -531,8 +555,8 @@ "certainDataType": false, "name": "valid_from", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc145", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d1000", "type": { "typeName": "DATETIME" } @@ -543,8 +567,8 @@ "certainDataType": false, "name": "valid_to", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc146", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d1001", "type": { "typeName": "DATETIME" } @@ -555,8 +579,8 @@ "certainDataType": false, "name": "version", "nullable": false, - "relation": "liquibase.structure.core.Table#c0fc137", - "snapshotId": "c0fc140", + "relation": "liquibase.structure.core.Table#100d992", + "snapshotId": "100d995", "type": { "columnSize": "30!{java.lang.Integer}", "typeName": "VARCHAR" @@ -564,81 +588,97 @@ } } ], + "liquibase.structure.core.ForeignKey": [ + { + "foreignKey": { + "foreignKeyColumns": [ + "liquibase.structure.core.Column#100d957" + ], + "foreignKeyTable": "liquibase.structure.core.Table#100d956", + "name": "fk_trusted_party_roles_on_trusted_party_entity", + "primaryKeyColumns": [ + "liquibase.structure.core.Column#100d960" + ], + "primaryKeyTable": "liquibase.structure.core.Table#100d961", + "snapshotId": "100d959" + } + } + ], "liquibase.structure.core.Index": [ { "index": { "columns": [ - "liquibase.structure.core.Column#c0fc118" + "liquibase.structure.core.Column#100d973" ], "name": "IX_pk_audit_event", - "snapshotId": "c0fc117", - "table": "liquibase.structure.core.Table#c0fc116", + "snapshotId": "100d972", + "table": "liquibase.structure.core.Table#100d971", "unique": true } }, { "index": { "columns": [ - "liquibase.structure.core.Column#c0fc106" + "liquibase.structure.core.Column#100d946" ], "name": "IX_pk_revocation_batch", - "snapshotId": "c0fc105", - "table": "liquibase.structure.core.Table#c0fc102", + "snapshotId": "100d945", + "table": "liquibase.structure.core.Table#100d942", "unique": true } }, { "index": { "columns": [ - "liquibase.structure.core.Column#c0fc130" + "liquibase.structure.core.Column#100d985" ], "name": "IX_pk_signer_information", - "snapshotId": "c0fc129", - "table": "liquibase.structure.core.Table#c0fc126", + "snapshotId": "100d984", + "table": "liquibase.structure.core.Table#100d981", "unique": true } }, { "index": { "columns": [ - "liquibase.structure.core.Column#c0fc159" + "liquibase.structure.core.Column#100d960" ], "name": "IX_pk_trusted_party", - "snapshotId": "c0fc158", - "table": "liquibase.structure.core.Table#c0fc155", + "snapshotId": "100d964", + "table": "liquibase.structure.core.Table#100d961", "unique": true } }, { "index": { "columns": [ - "liquibase.structure.core.Column#c0fc142" + "liquibase.structure.core.Column#100d997" ], "name": "IX_pk_validation_rule", - "snapshotId": "c0fc141", - "table": "liquibase.structure.core.Table#c0fc137", + "snapshotId": "100d996", + "table": "liquibase.structure.core.Table#100d992", "unique": true } }, { "index": { "columns": [ - "liquibase.structure.core.Column#c0fc152" + "liquibase.structure.core.Column#100d1007" ], "name": "IX_pk_valueset", - "snapshotId": "c0fc151", - "table": "liquibase.structure.core.Table#c0fc150", + "snapshotId": "100d1006", + "table": "liquibase.structure.core.Table#100d1005", "unique": true } }, { "index": { "columns": [ - "liquibase.structure.core.Column#c0fc104" + "liquibase.structure.core.Column#100d944" ], "name": "idx_6bd7e9b8e4d29f7ed8d5d4bb2", - "snapshotId": "c0fc107", - "table": "liquibase.structure.core.Table#c0fc102", + "snapshotId": "100d947", + "table": "liquibase.structure.core.Table#100d942", "unique": false } } @@ -646,77 +686,77 @@ "liquibase.structure.core.PrimaryKey": [ { "primaryKey": { - "backingIndex": "liquibase.structure.core.Index#c0fc117", + "backingIndex": "liquibase.structure.core.Index#100d972", "columns": [ - "liquibase.structure.core.Column#c0fc118" + "liquibase.structure.core.Column#100d973" ], "name": "pk_audit_event", - "snapshotId": "c0fc125", - "table": "liquibase.structure.core.Table#c0fc116" + "snapshotId": "100d980", + "table": "liquibase.structure.core.Table#100d971" } }, { "primaryKey": { - "backingIndex": "liquibase.structure.core.Index#c0fc105", + "backingIndex": "liquibase.structure.core.Index#100d945", "columns": [ - "liquibase.structure.core.Column#c0fc106" + "liquibase.structure.core.Column#100d946" ], "name": "pk_revocation_batch", - "snapshotId": "c0fc115", - "table": "liquibase.structure.core.Table#c0fc102" + "snapshotId": "100d955", + "table": "liquibase.structure.core.Table#100d942" } }, { "primaryKey": { - "backingIndex": "liquibase.structure.core.Index#c0fc129", + "backingIndex": "liquibase.structure.core.Index#100d984", "columns": [ - "liquibase.structure.core.Column#c0fc130" + "liquibase.structure.core.Column#100d985" ], "name": "pk_signer_information", - "snapshotId": "c0fc136", - "table": "liquibase.structure.core.Table#c0fc126" + "snapshotId": "100d991", + "table": "liquibase.structure.core.Table#100d981" } }, { "primaryKey": { - "backingIndex": "liquibase.structure.core.Index#c0fc158", + "backingIndex": "liquibase.structure.core.Index#100d964", "columns": [ - "liquibase.structure.core.Column#c0fc159" + "liquibase.structure.core.Column#100d960" ], "name": "pk_trusted_party", - "snapshotId": "c0fc165", - "table": "liquibase.structure.core.Table#c0fc155" + "snapshotId": "100d970", + "table": "liquibase.structure.core.Table#100d961" } }, { "primaryKey": { - "backingIndex": "liquibase.structure.core.Index#c0fc141", + "backingIndex": "liquibase.structure.core.Index#100d996", "columns": [ - "liquibase.structure.core.Column#c0fc142" + "liquibase.structure.core.Column#100d997" ], "name": "pk_validation_rule", - "snapshotId": "c0fc149", - "table": "liquibase.structure.core.Table#c0fc137" + "snapshotId": "100d1004", + "table": "liquibase.structure.core.Table#100d992" } }, { "primaryKey": { - "backingIndex": "liquibase.structure.core.Index#c0fc151", + "backingIndex": "liquibase.structure.core.Index#100d1006", "columns": [ - "liquibase.structure.core.Column#c0fc152" + "liquibase.structure.core.Column#100d1007" ], "name": "pk_valueset", - "snapshotId": "c0fc154", - "table": "liquibase.structure.core.Table#c0fc150" + "snapshotId": "100d1009", + "table": "liquibase.structure.core.Table#100d1005" } } ], "liquibase.structure.core.Schema": [ { "schema": { - "catalog": "liquibase.structure.core.Catalog#c0fc100", + "catalog": "liquibase.structure.core.Catalog#100d940", "default": true, - "snapshotId": "c0fc101" + "snapshotId": "100d941" } } ], @@ -724,133 +764,147 @@ { "table": { "columns": [ - "liquibase.structure.core.Column#c0fc118", - "liquibase.structure.core.Column#c0fc119", - "liquibase.structure.core.Column#c0fc120", - "liquibase.structure.core.Column#c0fc121", - "liquibase.structure.core.Column#c0fc122", - "liquibase.structure.core.Column#c0fc123", - "liquibase.structure.core.Column#c0fc124" + "liquibase.structure.core.Column#100d973", + "liquibase.structure.core.Column#100d974", + "liquibase.structure.core.Column#100d975", + "liquibase.structure.core.Column#100d976", + "liquibase.structure.core.Column#100d977", + "liquibase.structure.core.Column#100d978", + "liquibase.structure.core.Column#100d979" ], "indexes": [ - "liquibase.structure.core.Index#c0fc117" + "liquibase.structure.core.Index#100d972" ], "name": "audit_event", - "primaryKey": "liquibase.structure.core.PrimaryKey#c0fc125", - "schema": "liquibase.structure.core.Schema#c0fc101", - "snapshotId": "c0fc116" + "primaryKey": "liquibase.structure.core.PrimaryKey#100d980", + "schema": "liquibase.structure.core.Schema#100d941", + "snapshotId": "100d971" } }, { "table": { "columns": [ - "liquibase.structure.core.Column#c0fc106", - "liquibase.structure.core.Column#c0fc104", - "liquibase.structure.core.Column#c0fc108", - "liquibase.structure.core.Column#c0fc109", - "liquibase.structure.core.Column#c0fc110", - "liquibase.structure.core.Column#c0fc111", - "liquibase.structure.core.Column#c0fc112", - "liquibase.structure.core.Column#c0fc113", - "liquibase.structure.core.Column#c0fc114" + "liquibase.structure.core.Column#100d946", + "liquibase.structure.core.Column#100d944", + "liquibase.structure.core.Column#100d948", + "liquibase.structure.core.Column#100d949", + "liquibase.structure.core.Column#100d950", + "liquibase.structure.core.Column#100d951", + "liquibase.structure.core.Column#100d952", + "liquibase.structure.core.Column#100d953", + "liquibase.structure.core.Column#100d954" ], "indexes": [ - "liquibase.structure.core.Index#c0fc105", - "liquibase.structure.core.Index#c0fc107" + "liquibase.structure.core.Index#100d945", + "liquibase.structure.core.Index#100d947" ], "name": "revocation_batch", - "primaryKey": "liquibase.structure.core.PrimaryKey#c0fc115", - "schema": "liquibase.structure.core.Schema#c0fc101", - "snapshotId": "c0fc102", + "primaryKey": "liquibase.structure.core.PrimaryKey#100d955", + "schema": "liquibase.structure.core.Schema#100d941", + "snapshotId": "100d942", "uniqueConstraints": [ - "liquibase.structure.core.UniqueConstraint#c0fc103" + "liquibase.structure.core.UniqueConstraint#100d943" ] } }, { "table": { "columns": [ - "liquibase.structure.core.Column#c0fc130", - "liquibase.structure.core.Column#c0fc131", - "liquibase.structure.core.Column#c0fc132", - "liquibase.structure.core.Column#c0fc128", - "liquibase.structure.core.Column#c0fc133", - "liquibase.structure.core.Column#c0fc134", - "liquibase.structure.core.Column#c0fc135" + "liquibase.structure.core.Column#100d985", + "liquibase.structure.core.Column#100d986", + "liquibase.structure.core.Column#100d987", + "liquibase.structure.core.Column#100d983", + "liquibase.structure.core.Column#100d988", + "liquibase.structure.core.Column#100d989", + "liquibase.structure.core.Column#100d990" ], "indexes": [ - "liquibase.structure.core.Index#c0fc129" + "liquibase.structure.core.Index#100d984" ], "name": "signer_information", - "primaryKey": "liquibase.structure.core.PrimaryKey#c0fc136", - "schema": "liquibase.structure.core.Schema#c0fc101", - "snapshotId": "c0fc126", + "primaryKey": "liquibase.structure.core.PrimaryKey#100d991", + "schema": "liquibase.structure.core.Schema#100d941", + "snapshotId": "100d981", "uniqueConstraints": [ - "liquibase.structure.core.UniqueConstraint#c0fc127" + "liquibase.structure.core.UniqueConstraint#100d982" ] } }, { "table": { "columns": [ - "liquibase.structure.core.Column#c0fc159", - "liquibase.structure.core.Column#c0fc160", - "liquibase.structure.core.Column#c0fc161", - "liquibase.structure.core.Column#c0fc157", - "liquibase.structure.core.Column#c0fc162", - "liquibase.structure.core.Column#c0fc163", - "liquibase.structure.core.Column#c0fc164" + "liquibase.structure.core.Column#100d960", + "liquibase.structure.core.Column#100d965", + "liquibase.structure.core.Column#100d966", + "liquibase.structure.core.Column#100d963", + "liquibase.structure.core.Column#100d967", + "liquibase.structure.core.Column#100d968", + "liquibase.structure.core.Column#100d969" ], "indexes": [ - "liquibase.structure.core.Index#c0fc158" + "liquibase.structure.core.Index#100d964" ], "name": "trusted_party", - "primaryKey": "liquibase.structure.core.PrimaryKey#c0fc165", - "schema": "liquibase.structure.core.Schema#c0fc101", - "snapshotId": "c0fc155", + "primaryKey": "liquibase.structure.core.PrimaryKey#100d970", + "schema": "liquibase.structure.core.Schema#100d941", + "snapshotId": "100d961", "uniqueConstraints": [ - "liquibase.structure.core.UniqueConstraint#c0fc156" + "liquibase.structure.core.UniqueConstraint#100d962" ] } }, { "table": { "columns": [ - "liquibase.structure.core.Column#c0fc142", - "liquibase.structure.core.Column#c0fc143", - "liquibase.structure.core.Column#c0fc139", - "liquibase.structure.core.Column#c0fc144", - "liquibase.structure.core.Column#c0fc145", - "liquibase.structure.core.Column#c0fc146", - "liquibase.structure.core.Column#c0fc140", - "liquibase.structure.core.Column#c0fc147", - "liquibase.structure.core.Column#c0fc148" + "liquibase.structure.core.Column#100d957", + "liquibase.structure.core.Column#100d958" + ], + "name": "trusted_party_roles", + "outgoingForeignKeys": [ + "liquibase.structure.core.ForeignKey#100d959" + ], + "schema": "liquibase.structure.core.Schema#100d941", + "snapshotId": "100d956" + } + }, + { + "table": { + "columns": [ + "liquibase.structure.core.Column#100d997", + "liquibase.structure.core.Column#100d998", + "liquibase.structure.core.Column#100d994", + "liquibase.structure.core.Column#100d999", + "liquibase.structure.core.Column#100d1000", + "liquibase.structure.core.Column#100d1001", + "liquibase.structure.core.Column#100d995", + "liquibase.structure.core.Column#100d1002", + "liquibase.structure.core.Column#100d1003" ], "indexes": [ - "liquibase.structure.core.Index#c0fc141" + "liquibase.structure.core.Index#100d996" ], "name": "validation_rule", - "primaryKey": "liquibase.structure.core.PrimaryKey#c0fc149", - "schema": "liquibase.structure.core.Schema#c0fc101", - "snapshotId": "c0fc137", + "primaryKey": "liquibase.structure.core.PrimaryKey#100d1004", + "schema": "liquibase.structure.core.Schema#100d941", + "snapshotId": "100d992", "uniqueConstraints": [ - "liquibase.structure.core.UniqueConstraint#c0fc138" + "liquibase.structure.core.UniqueConstraint#100d993" ] } }, { "table": { "columns": [ - "liquibase.structure.core.Column#c0fc152", - "liquibase.structure.core.Column#c0fc153" + "liquibase.structure.core.Column#100d1007", + "liquibase.structure.core.Column#100d1008" ], "indexes": [ - "liquibase.structure.core.Index#c0fc151" + "liquibase.structure.core.Index#100d1006" ], "name": "valueset", - "primaryKey": "liquibase.structure.core.PrimaryKey#c0fc154", - "schema": "liquibase.structure.core.Schema#c0fc101", - "snapshotId": "c0fc150" + "primaryKey": "liquibase.structure.core.PrimaryKey#100d1009", + "schema": "liquibase.structure.core.Schema#100d941", + "snapshotId": "100d1005" } } ], @@ -859,15 +913,15 @@ "uniqueConstraint": { "clustered": false, "columns": [ - "liquibase.structure.core.Column#c0fc139", - "liquibase.structure.core.Column#c0fc140" + "liquibase.structure.core.Column#100d994", + "liquibase.structure.core.Column#100d995" ], "deferrable": false, "disabled": false, "initiallyDeferred": false, "name": "uc_16f88905e309ddbd1fb7b128d", - "snapshotId": "c0fc138", - "table": "liquibase.structure.core.Table#c0fc137", + "snapshotId": "100d993", + "table": "liquibase.structure.core.Table#100d992", "validate": true } }, @@ -875,14 +929,14 @@ "uniqueConstraint": { "clustered": false, "columns": [ - "liquibase.structure.core.Column#c0fc104" + "liquibase.structure.core.Column#100d944" ], "deferrable": false, "disabled": false, "initiallyDeferred": false, "name": "uc_revocation_batch_batchid", - "snapshotId": "c0fc103", - "table": "liquibase.structure.core.Table#c0fc102", + "snapshotId": "100d943", + "table": "liquibase.structure.core.Table#100d942", "validate": true } }, @@ -890,14 +944,14 @@ "uniqueConstraint": { "clustered": false, "columns": [ - "liquibase.structure.core.Column#c0fc128" + "liquibase.structure.core.Column#100d983" ], "deferrable": false, "disabled": false, "initiallyDeferred": false, "name": "uc_signer_information_thumbprint", - "snapshotId": "c0fc127", - "table": "liquibase.structure.core.Table#c0fc126", + "snapshotId": "100d982", + "table": "liquibase.structure.core.Table#100d981", "validate": true } }, @@ -905,14 +959,14 @@ "uniqueConstraint": { "clustered": false, "columns": [ - "liquibase.structure.core.Column#c0fc157" + "liquibase.structure.core.Column#100d963" ], "deferrable": false, "disabled": false, "initiallyDeferred": false, "name": "uc_trusted_party_thumbprint", - "snapshotId": "c0fc156", - "table": "liquibase.structure.core.Table#c0fc155", + "snapshotId": "100d962", + "table": "liquibase.structure.core.Table#100d961", "validate": true } } diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java index 1b2de182..c1172fa5 100644 --- a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java @@ -57,6 +57,7 @@ import java.util.Optional; import java.util.UUID; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -114,6 +115,11 @@ public void setup() { } + @AfterEach + public void teardown() throws Exception { + trustedPartyTestHelper.setRoles(countryCode); + } + @Test void testSuccessfulUpload() throws Exception { long revocationBatchesInDb = revocationBatchRepository.count(); @@ -141,6 +147,7 @@ void testSuccessfulUpload() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); MvcResult mvcResult = mockMvc.perform(post("/revocation-list") .content(payload) @@ -188,6 +195,7 @@ void testUploadFailedInvalidJson() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); mockMvc.perform(post("/revocation-list") .content(payload) @@ -229,6 +237,7 @@ void testUploadFailedInvalidJsonValues() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); mockMvc.perform(post("/revocation-list") .content(payload) @@ -270,6 +279,7 @@ void testUploadFailedInvalidJsonValuesInHashEntries() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); mockMvc.perform(post("/revocation-list") .content(payload) @@ -311,6 +321,7 @@ void testUploadFailedInvalidCountry() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); mockMvc.perform(post("/revocation-list") .content(payload) @@ -353,6 +364,7 @@ void testDeleteRevocationBatch() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -401,6 +413,7 @@ void testDeleteRevocationBatchAlternativeEndpoint() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(post("/revocation-list/delete") .content(payload) @@ -444,6 +457,7 @@ void testDeleteRevocationBatchFailedInvalidJson() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -483,6 +497,7 @@ void testDeleteRevocationBatchFailedInvalidJsonValue() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -522,6 +537,7 @@ void testDeleteRevocationBatchFailedBatchNotFound() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -563,6 +579,7 @@ void testDeleteRevocationBatchFailedInvalidCountry() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, "XX"); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -604,6 +621,7 @@ void testDeleteRevocationBatchFailedUploadDoesNotMatchAuth() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -645,6 +663,7 @@ void testDeleteRevocationBatchFailedInvalidCmsSignature() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -686,6 +705,7 @@ void testDeleteRevocationBatchFailedGone() throws Exception { .buildAsString(); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); mockMvc.perform(delete("/revocation-list") .content(payload) @@ -720,6 +740,7 @@ void testDownloadBatchList() throws Exception { } String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); mockMvc.perform(get("/revocation-list") .accept("application/json") @@ -800,6 +821,7 @@ void testDownloadBatchList() throws Exception { void testDownloadBatchListFailedNoIfModifiedSince() throws Exception { String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); mockMvc.perform(get("/revocation-list") .accept("application/json") @@ -813,6 +835,7 @@ void testDownloadBatchListFailedNoIfModifiedSince() throws Exception { void testDownloadBatchListFailedIfModifiedSinceInFuture() throws Exception { String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); mockMvc.perform(get("/revocation-list") .accept("application/json") @@ -852,6 +875,7 @@ void testDownloadRevocationBatch() throws Exception { revocationBatchRepository.save(entity); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); mockMvc.perform(get("/revocation-list/" + entity.getBatchId()) .accept("application/cms") @@ -875,6 +899,7 @@ void testDownloadRevocationBatch() throws Exception { @Test void testDownloadRevocationBatchInvalidBatchId() throws Exception { String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); mockMvc.perform(get("/revocation-list/thisIsNotAnUUID") .accept("application/cms") @@ -899,6 +924,7 @@ void testDownloadRevocationBatchGone() throws Exception { revocationBatchRepository.save(entity); String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); mockMvc.perform(get("/revocation-list/" + entity.getBatchId()) .accept("application/cms") @@ -912,6 +938,7 @@ void testDownloadRevocationBatchGone() throws Exception { void testDownloadRevocationBatchNotFound() throws Exception { String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); mockMvc.perform(get("/revocation-list/" + UUID.randomUUID()) .accept("application/cms") @@ -959,4 +986,94 @@ public static void assertEquals(RevocationBatchEntity expected, RevocationBatchE Assertions.assertEquals(expected.getId(), expected.getId()); Assertions.assertEquals(expected.getSignedBatch(), expected.getSignedBatch()); } + + @Test + void testDownloadBatchListRequiresCorrectRole() throws Exception { + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); + + mockMvc.perform(get("/revocation-list") + .accept("application/json") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + .header(HttpHeaders.IF_MODIFIED_SINCE, OffsetDateTime.now().toString()) + ) + .andExpect(status().isForbidden()); + } + + @Test + void testDeleteRevocationBatchRequiresCorrectRole() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + RevocationBatchDeleteRequestDto deleteRequestDto = new RevocationBatchDeleteRequestDto(UUID.randomUUID().toString()); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(deleteRequestDto)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER); + + mockMvc.perform(delete("/revocation-list") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isForbidden()); + + mockMvc.perform(post("/revocation-list/delete") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isForbidden()); + } + + @Test + void testUploadRequiresCorrectRole() throws Exception { + X509Certificate signerCertificate = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + RevocationBatchDto revocationBatchDto = new RevocationBatchDto(); + revocationBatchDto.setCountry(countryCode); + revocationBatchDto.setExpires(ZonedDateTime.now().plusDays(7)); + revocationBatchDto.setHashType(RevocationHashTypeDto.SIGNATURE); + revocationBatchDto.setKid("UNKNOWN_KID"); + revocationBatchDto.setEntries(List.of(new RevocationBatchDto.BatchEntryDto("aaaaaaaaaaaaaaaaaaaaaaaa"))); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(revocationBatchDto)) + .buildAsString(); + + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_LIST_READER, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER); + + mockMvc.perform(post("/revocation-list") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isForbidden()) + .andExpect(header().doesNotExist(HttpHeaders.ETAG)); + } + + @Test + void testDownloadRevocationBatchRequiresCorrectRole() throws Exception { + String authCertHash = trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_DELETER, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); + + mockMvc.perform(get("/revocation-list/" + UUID.randomUUID()) + .accept("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isForbidden()); + } } 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 index 8b54cffd..add0eca4 100644 --- a/src/test/java/eu/europa/ec/dgc/gateway/testdata/TrustedPartyTestHelper.java +++ b/src/test/java/eu/europa/ec/dgc/gateway/testdata/TrustedPartyTestHelper.java @@ -28,6 +28,7 @@ import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -78,6 +79,17 @@ public PrivateKey getPrivateKey(TrustedPartyEntity.CertificateType type, String return privateKeyMap.get(type).get(countryCode); } + public void setRoles(String countryCode, TrustedPartyEntity.CertificateRoles... roles) throws Exception { + String hash = getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + + TrustedPartyEntity entity = trustedPartyRepository.getFirstByThumbprintAndCertificateType( + hash, TrustedPartyEntity.CertificateType.AUTHENTICATION).orElseThrow(); + + entity.setCertificateRoles(Arrays.asList(roles)); + + trustedPartyRepository.save(entity); + } + private void prepareTestCert(TrustedPartyEntity.CertificateType type, String countryCode) throws Exception { // Check if a test certificate already exists if (!hashMap.get(type).containsKey(countryCode)) {