Skip to content

Commit

Permalink
Feat/list signature (#22)
Browse files Browse the repository at this point in the history
* feat/list-signature jks signing optional
  • Loading branch information
a-trzewik authored Jul 7, 2021
1 parent a4b2a58 commit d56a59d
Show file tree
Hide file tree
Showing 26 changed files with 473 additions and 25 deletions.
Binary file added certs/signing.jks
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package eu.europa.ec.dgc.businessrule;

import eu.europa.ec.dgc.businessrule.config.DgcConfigProperties;
import eu.europa.ec.dgc.businessrule.config.JksSigningConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -30,7 +31,7 @@
* The Application class.
*/
@SpringBootApplication
@EnableConfigurationProperties(DgcConfigProperties.class)
@EnableConfigurationProperties({DgcConfigProperties.class, JksSigningConfig.class})
public class DgcBusinessRuleServiceApplication extends SpringBootServletInitializer {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package eu.europa.ec.dgc.businessrule.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Getter
@Setter
@ConfigurationProperties("jks-signing")
public class JksSigningConfig {
private String keyStoreFile;
private String keyStorePassword;
private String certAlias;
private String privateKeyPassword;
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ public class CountryListEntity {
@Column(name = "raw_data", nullable = false)
String rawData;

@Column(name = "hash", length = 64)
private String hash;

@Column(name = "signature", length = 256)
private String signature;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package eu.europa.ec.dgc.businessrule.entity;

public enum ListType {
Rules, ValueSets
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package eu.europa.ec.dgc.businessrule.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
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 = "signed_list")
@AllArgsConstructor
@NoArgsConstructor
public class SignedListEntity {
@Id
@Column(name = "list_type", nullable = false)
@Enumerated(EnumType.STRING)
private ListType listType;

@Column(name = "hash", nullable = false, length = 64)
private String hash;

@Column(name = "signature", nullable = false, length = 256)
private String signature;

@Lob
@Column(name = "raw_data", nullable = false)
String rawData;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package eu.europa.ec.dgc.businessrule.repository;

import eu.europa.ec.dgc.businessrule.entity.ListType;
import eu.europa.ec.dgc.businessrule.entity.SignedListEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface SignedListRepository extends JpaRepository<SignedListEntity, ListType> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package eu.europa.ec.dgc.businessrule.restapi.controller;

import eu.europa.ec.dgc.businessrule.entity.BusinessRuleEntity;
import eu.europa.ec.dgc.businessrule.entity.SignedListEntity;
import eu.europa.ec.dgc.businessrule.exception.DgcaBusinessRulesResponseException;
import eu.europa.ec.dgc.businessrule.restapi.dto.BusinessRuleListItemDto;
import eu.europa.ec.dgc.businessrule.restapi.dto.ProblemReportDto;
Expand All @@ -35,9 +36,11 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import javax.validation.Valid;
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;
Expand All @@ -56,7 +59,7 @@ public class BusinessRuleController {

private static final String API_VERSION_HEADER = "X-VERSION";

private static final String X_SIGNATURE_HEADER = "X-SIGNATURE";
public static final String X_SIGNATURE_HEADER = "X-SIGNATURE";

private final BusinessRuleService businessRuleService;

Expand Down Expand Up @@ -90,8 +93,21 @@ public class BusinessRuleController {
public ResponseEntity<List<BusinessRuleListItemDto>> getRules(
@RequestHeader(value = API_VERSION_HEADER, required = false) String apiVersion
) {

return ResponseEntity.ok(businessRuleService.getBusinessRulesList());
Optional<SignedListEntity> rulesList = businessRuleService.getBusinessRulesSignedList();
ResponseEntity responseEntity;
if (rulesList.isPresent()) {
ResponseEntity.BodyBuilder respBuilder = ResponseEntity.ok();
String signature = rulesList.get().getSignature();
if (signature != null & signature.length() > 0) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set(X_SIGNATURE_HEADER, signature);
respBuilder.headers(responseHeaders);
}
responseEntity = respBuilder.body(rulesList.get().getRawData());
} else {
responseEntity = ResponseEntity.ok(businessRuleService.getBusinessRulesList());
}
return responseEntity;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

package eu.europa.ec.dgc.businessrule.restapi.controller;

import eu.europa.ec.dgc.businessrule.entity.CountryListEntity;
import eu.europa.ec.dgc.businessrule.service.CountryListService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -31,6 +32,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -83,7 +85,16 @@ public class CountryListController {
public ResponseEntity<String> getCountryList(
@RequestHeader(value = API_VERSION_HEADER, required = false) String apiVersion
) {
return ResponseEntity.ok(countryListService.getCountryList());
CountryListEntity countryList = countryListService.getCountryList();
ResponseEntity<String> responseEntity;
if (countryList.getSignature() != null) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set(BusinessRuleController.X_SIGNATURE_HEADER, countryList.getSignature());
responseEntity = ResponseEntity.ok().headers(responseHeaders).body(countryList.getRawData());
} else {
responseEntity = ResponseEntity.ok(countryList.getRawData());
}
return responseEntity;
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package eu.europa.ec.dgc.businessrule.restapi.controller;

import eu.europa.ec.dgc.businessrule.service.SigningService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/publickey")
@Slf4j
@RequiredArgsConstructor
public class SigningController {
private final Optional<SigningService> signingService;

/**
* Http Method for getting the business rules list.
*/
@GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(
summary = "Gets the signing public key (der base64 encoded)",
description = "Gets the signing public key (der base64 encoded)",
tags = {"Business Rules"},
responses = {
@ApiResponse(
responseCode = "200",
description = "public key"),
@ApiResponse(
responseCode = "404",
description = "signing not supported"),
}
)
public ResponseEntity<String> getPublicKey() {
if (signingService.isPresent()) {
return ResponseEntity.ok(signingService.get().getPublicKey());
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package eu.europa.ec.dgc.businessrule.restapi.controller;


import eu.europa.ec.dgc.businessrule.entity.SignedListEntity;
import eu.europa.ec.dgc.businessrule.entity.ValueSetEntity;
import eu.europa.ec.dgc.businessrule.exception.DgcaBusinessRulesResponseException;
import eu.europa.ec.dgc.businessrule.restapi.dto.ProblemReportDto;
Expand All @@ -36,9 +37,11 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.util.List;
import java.util.Optional;
import javax.validation.Valid;
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;
Expand Down Expand Up @@ -127,7 +130,21 @@ public class ValueSetController {
public ResponseEntity<List<ValueSetListItemDto>> getValueSetList(
@RequestHeader(value = API_VERSION_HEADER, required = false) String apiVersion
) {
return ResponseEntity.ok(valueSetService.getValueSetsList());
Optional<SignedListEntity> rulesList = valueSetService.getValueSetsSignedList();
ResponseEntity responseEntity;
if (rulesList.isPresent()) {
ResponseEntity.BodyBuilder respBuilder = ResponseEntity.ok();
String signature = rulesList.get().getSignature();
if (signature != null & signature.length() > 0) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set(BusinessRuleController.X_SIGNATURE_HEADER, signature);
respBuilder.headers(responseHeaders);
}
responseEntity = respBuilder.body(rulesList.get().getRawData());
} else {
responseEntity = ResponseEntity.ok(valueSetService.getValueSetsList());
}
return responseEntity;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,28 @@

package eu.europa.ec.dgc.businessrule.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import eu.europa.ec.dgc.businessrule.entity.BusinessRuleEntity;
import eu.europa.ec.dgc.businessrule.entity.ListType;
import eu.europa.ec.dgc.businessrule.entity.SignedListEntity;
import eu.europa.ec.dgc.businessrule.exception.DgcaBusinessRulesResponseException;
import eu.europa.ec.dgc.businessrule.model.BusinessRuleItem;
import eu.europa.ec.dgc.businessrule.repository.BusinessRuleRepository;
import eu.europa.ec.dgc.businessrule.repository.SignedListRepository;
import eu.europa.ec.dgc.businessrule.restapi.dto.BusinessRuleListItemDto;
import eu.europa.ec.dgc.businessrule.utils.BusinessRulesUtils;
import eu.europa.ec.dgc.gateway.connector.model.ValidationRule;
import eu.europa.ec.dgc.utils.CertificateUtils;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -43,6 +52,8 @@
public class BusinessRuleService {

private final BusinessRuleRepository businessRuleRepository;
private final ListSigningService listSigningService;
private final SignedListRepository signedListRepository;

private final BusinessRulesUtils businessRulesUtils;

Expand All @@ -56,6 +67,10 @@ public List<BusinessRuleListItemDto> getBusinessRulesList() {
return rulesItems;
}

public Optional<SignedListEntity> getBusinessRulesSignedList() {
return signedListRepository.findById(ListType.Rules);
}

/**
* Gets list of all business rules ids and hashes for a country.
*/
Expand All @@ -80,7 +95,7 @@ public BusinessRuleEntity getBusinessRuleByCountryAndHash(String country, String
* @param businessRules list of actual value sets
*/
@Transactional
public void updateBusinesRules(List<BusinessRuleItem> businessRules) {
public void updateBusinessRules(List<BusinessRuleItem> businessRules) {
List<String> ruleHashes =
businessRules.stream().map(BusinessRuleItem::getHash).collect(Collectors.toList());
List<String> alreadyStoredRules = getBusinessRulesHashList();
Expand All @@ -96,7 +111,7 @@ public void updateBusinesRules(List<BusinessRuleItem> businessRules) {
saveBusinessRule(rule);
}
}

listSigningService.updateSignedList(getBusinessRulesList(),ListType.Rules);
}

/**
Expand Down
Loading

0 comments on commit d56a59d

Please sign in to comment.