Skip to content

Commit

Permalink
Merge pull request #83 from eu-digital-green-certificates/feat/revoca…
Browse files Browse the repository at this point in the history
…tion_list_download

Feat/revocation list download
  • Loading branch information
f11h authored Jan 31, 2022
2 parents 174c63d + 073da3a commit eb9a7de
Show file tree
Hide file tree
Showing 10 changed files with 819 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package eu.europa.ec.dgc.gateway.connector;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.europa.ec.dgc.gateway.connector.client.DgcGatewayConnectorRestClient;
import eu.europa.ec.dgc.gateway.connector.dto.RevocationBatchDto;
import eu.europa.ec.dgc.gateway.connector.exception.RevocationBatchDownloadException;
import eu.europa.ec.dgc.gateway.connector.exception.RevocationBatchGoneException;
import eu.europa.ec.dgc.gateway.connector.exception.RevocationBatchParseException;
import eu.europa.ec.dgc.gateway.connector.iterator.DgcGatewayRevocationListDownloadIterator;
import eu.europa.ec.dgc.signing.SignedMessageParser;
import eu.europa.ec.dgc.signing.SignedStringMessageParser;
import feign.FeignException;
import java.time.ZonedDateTime;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

@ConditionalOnProperty("dgc.gateway.connector.enabled")
@Service
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@RequiredArgsConstructor
@Slf4j
public class DgcGatewayRevocationListDownloadConnector {

private final DgcGatewayConnectorRestClient dgcGatewayConnectorRestClient;
private final ObjectMapper objectMapper;

/**
* Gets a revocation list iterator, for partly downloading the revocation list.
* The if-modified-since header is set to the default value to start at the beginning of the list.
* @return revocation list iterator
*/
public DgcGatewayRevocationListDownloadIterator getRevocationListDownloadIterator() {
return new DgcGatewayRevocationListDownloadIterator(dgcGatewayConnectorRestClient);
}

/**
* Gets a revocation list iterator, for partly downloading the revocation list.
* The if-modified-since header is set to the value of the parameter. Only newer part of the list are downloaded.
* @param ifModifiedSinceDate The value for the if-modified-since header
* @return revocation list iterator
*/
public DgcGatewayRevocationListDownloadIterator getRevocationListDownloadIterator(
ZonedDateTime ifModifiedSinceDate) {

return new DgcGatewayRevocationListDownloadIterator(dgcGatewayConnectorRestClient, ifModifiedSinceDate);
}

/**
* Gets the revocation list batch data for a given batchId.
* @param batchId the id of the batch to download.
* @return the batch data.
*/
public RevocationBatchDto getRevocationListBatchById(String batchId) throws RevocationBatchDownloadException,
RevocationBatchGoneException, RevocationBatchParseException {

ResponseEntity<String> responseEntity;

try {
responseEntity = dgcGatewayConnectorRestClient.downloadBatch(batchId);
} catch (FeignException e) {
log.error("Download of revocation list batch failed. DGCG responded with status code: {}", e.status());

if (e.status() == HttpStatus.GONE.value()) {
throw new RevocationBatchGoneException(String.format("Batch already gone: %s", batchId),batchId);
}

throw new RevocationBatchDownloadException("Batch download failed with exception.", e);
}

if (responseEntity.getStatusCode() != HttpStatus.OK) {
int statusCode = responseEntity.getStatusCode().value();
log.error("Download of revocation list batch failed. DGCG responded with status code: {}", statusCode);

throw new RevocationBatchDownloadException(
String.format("Batch download failed with unexpected response. Response status code: %d", statusCode),
statusCode);
}

String cms = responseEntity.getBody();

if (!checkCmsSignature(cms)) {
log.error("CMS check failed for revocation batch: {}", batchId);
throw new RevocationBatchParseException(
String.format("CMS check failed for revocation batch: %s", batchId), batchId);
}

return map(cms, batchId);
}

private boolean checkCmsSignature(String cms) {
SignedStringMessageParser parser =
new SignedStringMessageParser(cms);

if (parser.getParserState() != SignedMessageParser.ParserState.SUCCESS) {
log.error("Invalid CMS for Revocation List Batch.");
return false;
}

if (!parser.isSignatureVerified()) {
log.error("Invalid CMS Signature for Revocation List Batch");
return false;
}

return true;
}

private RevocationBatchDto map(String cms, String batchId) {
SignedStringMessageParser parser =
new SignedStringMessageParser(cms);

try {
objectMapper.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true);
return objectMapper.readValue(parser.getPayload(), RevocationBatchDto.class);
} catch (JsonProcessingException e) {
log.error("Failed to parse revocation batch JSON: {}", e.getMessage());

throw new RevocationBatchParseException(
String.format("Failed to parse revocation batch JSON: %s", e.getMessage()), batchId);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@
package eu.europa.ec.dgc.gateway.connector.client;

import eu.europa.ec.dgc.gateway.connector.dto.CertificateTypeDto;
import eu.europa.ec.dgc.gateway.connector.dto.RevocationBatchListDto;
import eu.europa.ec.dgc.gateway.connector.dto.TrustListItemDto;
import eu.europa.ec.dgc.gateway.connector.dto.ValidationRuleDto;
import java.util.List;
import java.util.Map;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.HttpHeaders;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;

@ConditionalOnProperty("dgc.gateway.connector.enabled")
@FeignClient(
Expand Down Expand Up @@ -119,4 +122,22 @@ public interface DgcGatewayConnectorRestClient {
@GetMapping(value = "/rules/{cc}", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Map<String, List<ValidationRuleDto>>> downloadValidationRule(@PathVariable("cc") String countryCode);


/**
* Downloads a batch list from the revocation list.
*
*/
@GetMapping(value = "/revocation-list", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<RevocationBatchListDto> downloadRevocationList(
@RequestHeader(HttpHeaders.IF_MODIFIED_SINCE) String lastUpdate);

/**
* Downloads a batch of the revocation list.
*
* @param batchId ID of the batch to download
* @return batch as cms massage
*/
@GetMapping(value = "/revocation-list/{batchId}", produces = {"application/cms-text"})
ResponseEntity<String> downloadBatch(@PathVariable("batchId") String batchId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*-
* ---license-start
* eu-digital-green-certificates / dgc-lib
* ---
* Copyright (C) 2022 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.connector.dto;

import java.time.ZonedDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class RevocationBatchDto {

private String country;

private ZonedDateTime expires;

private String kid;

private RevocationHashTypeDto hashType;

private List<BatchEntryDto> entries;

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class BatchEntryDto {

private String hash;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*-
* ---license-start
* eu-digital-green-certificates / dgc-lib
* ---
* Copyright (C) 2022 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.connector.dto;

import java.time.ZonedDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
public class RevocationBatchListDto {


private Boolean more;

private List<RevocationBatchListItemDto> batches;

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class RevocationBatchListItemDto {

private String batchId;

private String country;

private ZonedDateTime date;

private Boolean deleted;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*-
* ---license-start
* eu-digital-green-certificates / dgc-lib
* ---
* Copyright (C) 2022 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.connector.dto;

// Type of hash for revocation lists
public enum RevocationHashTypeDto {

// The hash is calculated over the UCI string encoded in UTF-8 and converted to a byte array.
UCI,

// The hash is calculated over the bytes of the COSE_SIGN1 signature from the CWT
SIGNATURE,

// The CountryCode encoded as a UTF-8 string concatenated with the UCI encoded with a
// UTF-8 string. This is then converted to a byte array and used as input to the hash function.")
COUNTRYCODEUCI

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package eu.europa.ec.dgc.gateway.connector.exception;

import lombok.Getter;

@Getter
public class RevocationBatchDownloadException extends RuntimeException {


private final int status;

public RevocationBatchDownloadException(String message, Throwable inner) {
super(message, inner);
this.status = 500;
}

public RevocationBatchDownloadException(String message) {
super(message);
this.status = 500;
}

public RevocationBatchDownloadException(String message, Throwable inner, int status) {
super(message, inner);
this.status = status;
}

public RevocationBatchDownloadException(String message, int status) {
super(message);
this.status = status;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package eu.europa.ec.dgc.gateway.connector.exception;

import lombok.Getter;

@Getter
public class RevocationBatchGoneException extends RuntimeException {

private final String batchId;

public RevocationBatchGoneException(String message, String batchId) {
super(message);
this.batchId = batchId;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package eu.europa.ec.dgc.gateway.connector.exception;

import lombok.Getter;

@Getter
public class RevocationBatchParseException extends RuntimeException {

private final String batchId;

public RevocationBatchParseException(String message, String batchId) {
super(message);
this.batchId = batchId;
}

}

Loading

0 comments on commit eb9a7de

Please sign in to comment.