Skip to content

Commit

Permalink
feat/context, cbor service, expiration time (#47)
Browse files Browse the repository at this point in the history
* feat/context, cbor service, expiration time

* sonar

* feat/did document with jwk
  • Loading branch information
a-trzewik committed May 10, 2021
1 parent d7a63f7 commit c5d4723
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 61 deletions.
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,16 @@
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.4.31</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-cbor</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.9.2</version>
</dependency>
<dependency>
<groupId>io.pivotal.cfenv</groupId>
<artifactId>java-cfenv-boot</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package eu.europa.ec.dgc.issuance.config;

import ehn.techiop.hcert.kotlin.chain.Base45Service;
import ehn.techiop.hcert.kotlin.chain.CborService;
import ehn.techiop.hcert.kotlin.chain.CompressorService;
import ehn.techiop.hcert.kotlin.chain.ContextIdentifierService;
import ehn.techiop.hcert.kotlin.chain.CoseService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultBase45Service;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultCborService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultCompressorService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultContextIdentifierService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultCoseService;
Expand Down Expand Up @@ -41,9 +39,4 @@ Base45Service base45Service() {
return new DefaultBase45Service();
}

@Bean
CborService cborService() {
return new DefaultCborService();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,51 @@

package eu.europa.ec.dgc.issuance.config;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;

@Getter
@Setter
@ConfigurationProperties("issuance")
public class IssuanceConfigProperties {

@NotBlank
@Size(max = 20)
private String dgciPrefix;
private String keyStoreFile;
private String keyStorePassword;
private String certAlias;
private String privateKeyPassword;
@NotBlank
@Size(max = 2)
private String countryCode;
private int tanExpirationHours = 24;
@DurationUnit(ChronoUnit.HOURS)
private Duration tanExpirationHours = Duration.ofHours(24);
/**
* JSON file that is provided to /context endpoint.
*/
private String contextFile;
@NotNull
private Expiration expiration;

@Getter
@Setter
public static class Expiration {
@DurationUnit(ChronoUnit.DAYS)
@NotNull
private Duration vaccination;
@DurationUnit(ChronoUnit.DAYS)
@NotNull
private Duration recovery;
@DurationUnit(ChronoUnit.DAYS)
@NotNull
private Duration test;
}

}
12 changes: 10 additions & 2 deletions src/main/java/eu/europa/ec/dgc/issuance/config/OpenApiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import java.util.Optional;
import lombok.Generated;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.info.BuildProperties;
Expand All @@ -14,7 +15,7 @@
@RequiredArgsConstructor
public class OpenApiConfig {

private final BuildProperties buildProperties;
private final Optional<BuildProperties> buildProperties;

/**
* Configure the OpenApi bean with title and version.
Expand All @@ -23,11 +24,18 @@ public class OpenApiConfig {
*/
@Bean
public OpenAPI openApi() {
String version;
if (buildProperties.isPresent()) {
version = buildProperties.get().getVersion();
} else {
// build properties is not available if starting from IDE without running mvn before (so fake this)
version = "dev";
}
return new OpenAPI()
.info(new Info()
.title("Digital Green Certificate Issuance")
.description("The API defines Issuance Service for digital green certificates.")
.version(buildProperties.getVersion())
.version(version)
.license(new License()
.name("Apache 2.0")
.url("https://www.apache.org/licenses/LICENSE-2.0")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public class CertController {
* Controller for creating Vaccination Certificate.
*/
@Operation(
summary = "create edgc with process step informations, developing tool"
summary = "create edgc with process step information, developing tool"
)
@PostMapping(value = "create", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ChainResult> createVaccinationCertificate(@RequestBody Eudgc eudgc) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package eu.europa.ec.dgc.issuance.restapi.controller;

import com.fasterxml.jackson.databind.JsonNode;
import eu.europa.ec.dgc.issuance.service.ContextService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.AllArgsConstructor;
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("/context")
@AllArgsConstructor
public class ContextController {
private final ContextService contextService;

@Operation(
summary = "provide configuration information for wallet app",
description = "list of claim endpoints for wallet app"
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "server list")}
)
@GetMapping(value = "")
public ResponseEntity<JsonNode> context() {
return ResponseEntity.ok(contextService.getContextDefintion());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@

package eu.europa.ec.dgc.issuance.restapi.dto;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;

@Data
public class DidAuthentication {
private String type;
private String controller;
// TODO use data type and ISO Date formater
private String expires;
private String publicKeyBase58;
private JsonNode publicKeyJsw;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package eu.europa.ec.dgc.issuance.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
import com.upokecenter.cbor.CBORObject;
import ehn.techiop.hcert.data.Eudgc;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultCborService;
import eu.europa.ec.dgc.issuance.config.IssuanceConfigProperties;
import eu.europa.ec.dgc.issuance.entity.GreenCertificateType;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* own cbor service.
* The default one inject fixed country code and expiration period
*
*/
@Service
@RequiredArgsConstructor
public class ConfigurableCborService extends DefaultCborService {
public static final int ISSUER = 1;
public static final int ISSUED_AT = 6;
public static final int EXPIRATION = 4;
public static final int HCERT = -260;
public static final int HCERT_VERSION = 1;
// Need autowired because there is circular reference
@Autowired
private DgciService dgciService;

private final IssuanceConfigProperties issuanceConfigProperties;

@Override
public byte[] encode(@NotNull Eudgc input) {
byte[] cbor;
try {
cbor = new CBORMapper().writeValueAsBytes(input);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
GreenCertificateType greenCertificateType;
if (input.getT() != null && !input.getT().isEmpty()) {
greenCertificateType = GreenCertificateType.Test;
} else if (input.getR() != null && !input.getR().isEmpty()) {
greenCertificateType = GreenCertificateType.Recovery;
} else {
greenCertificateType = GreenCertificateType.Vaccination;
}
long issueTime = Instant.now().getEpochSecond();
long expirationTime = issueTime + dgciService.expirationForType(greenCertificateType).get(ChronoUnit.SECONDS);
CBORObject coseContainer = CBORObject.NewMap();
coseContainer.set(CBORObject.FromObject(ISSUER),
CBORObject.FromObject(issuanceConfigProperties.getCountryCode()));
coseContainer.set(CBORObject.FromObject(ISSUED_AT),CBORObject.FromObject(issueTime));
coseContainer.set(CBORObject.FromObject(EXPIRATION),CBORObject.FromObject(expirationTime));
CBORObject hcert = CBORObject.NewMap();
hcert.set(CBORObject.FromObject(HCERT_VERSION),CBORObject.DecodeFromBytes(cbor));
coseContainer.set(CBORObject.FromObject(HCERT),hcert);
return coseContainer.EncodeToBytes();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package eu.europa.ec.dgc.issuance.service;


import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import eu.europa.ec.dgc.issuance.config.IssuanceConfigProperties;
import java.io.File;
import java.io.IOException;
import javax.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@RequiredArgsConstructor
public class ContextService {
private final IssuanceConfigProperties issuanceConfigProperties;
private JsonNode contextDefinition;

/**
* load json context file.
*/
@PostConstruct
public void loadContextFile() {
if (issuanceConfigProperties.getContextFile() != null
&& issuanceConfigProperties.getContextFile().length() > 0) {
File contextFile = new File(issuanceConfigProperties.getContextFile());
if (!contextFile.isFile()) {
throw new IllegalArgumentException("configured context file can not be found: " + contextFile);
}
ObjectMapper mapper = new ObjectMapper();
try {
contextDefinition = mapper.readTree(contextFile);
log.info("context file loaded from: " + contextFile);
} catch (IOException e) {
throw new IllegalArgumentException("can not read json context file: " + contextFile, e);
}
} else {
log.warn("the context json file not configured (property: issuance.contextFile)."
+ " The empty context file is generated instead");
JsonNodeFactory jsonNodeFactory = JsonNodeFactory.instance;
ObjectNode contextObj = jsonNodeFactory.objectNode();
contextObj.set("Origin", jsonNodeFactory.textNode(issuanceConfigProperties.getCountryCode()));
contextObj.set("claimEndpoints", jsonNodeFactory.arrayNode());
contextDefinition = contextObj;
}
}

public JsonNode getContextDefintion() {
return contextDefinition;
}
}
Loading

0 comments on commit c5d4723

Please sign in to comment.