Skip to content

Commit

Permalink
Feat: Publication (#179)
Browse files Browse the repository at this point in the history
* Implementation of Publication Archive

* Finetuning for Publication
Unit Test for Publication Archive

* Fix Typo

* Add ability to switch off publication

* Update URLs in Readme and License File

* Add ability to switch off synchronize call
  • Loading branch information
f11h authored Mar 30, 2022
1 parent 300ebc1 commit 9997e89
Show file tree
Hide file tree
Showing 19 changed files with 1,143 additions and 41 deletions.
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ services:
- DGC_TRUSTANCHOR_KEYSTOREPATH=/ec/prod/app/san/dgc/ta.jks
- DGC_TRUSTANCHOR_KEYSTOREPASS=dgcg-p4ssw0rd
- DGC_TRUSTANCHOR_CERTIFICATEALIAS=dgcg_trust_anchor
- DGC_PUBLICATION_KEYSTORE_KEYSTOREPATH=/ec/prod/app/san/dgc/publication-signer-tst.jks
- DGC_PUBLICATION_KEYSTORE_KEYSTOREPASS=dgc-p4ssw0rd
- DGC_PUBLICATION_KEYSTORE_CERTIFICATEALIAS=dgc_tst_publication
depends_on:
- mysql
networks:
Expand Down
1 change: 1 addition & 0 deletions lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*-
* ---license-start
* EU Digital Green Certificate Gateway Service / dgc-gateway
* ---
* Copyright (C) 2021 - 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.client;

import eu.europa.ec.dgc.gateway.model.AssetManagerSynchronizeResponseDto;
import lombok.AllArgsConstructor;
import lombok.Getter;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;

@FeignClient(
name = "assetManagerClient",
url = "${dgc.publication.url}",
configuration = AssetManagerClientConfig.class)
@ConditionalOnProperty("dgc.publication.enabled")
public interface AssetManagerClient {

@PutMapping(
value = "/remote.php/dav/files/{uid}/{path}/{filename}",
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = MediaType.ALL_VALUE)
ResponseEntity<Void> uploadFile(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHeader,
@PathVariable("uid") String uid,
@PathVariable("path") String path,
@PathVariable("filename") String filename,
@RequestBody byte[] file);

@PostMapping(
value = "/ocs/v2.php/apps/files/api/v2/synchronize",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
ResponseEntity<AssetManagerSynchronizeResponseDto> synchronize(
@RequestHeader(HttpHeaders.AUTHORIZATION) String authHeader,
@RequestHeader("OCS-APIRequest") String ocsApiRequest,
@RequestBody SynchronizeFormData formData);

@Getter
@AllArgsConstructor
class SynchronizeFormData {
private String path;
private String nodeList;
private String notifyEmails;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*-
* ---license-start
* EU Digital Green Certificate Gateway Service / dgc-gateway
* ---
* Copyright (C) 2021 - 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.client;

import eu.europa.ec.dgc.gateway.config.DgcConfigProperties;
import feign.Client;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import feign.httpclient.ApacheHttpClient;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
@Slf4j
@ConditionalOnProperty("dgc.publication.enabled")
public class AssetManagerClientConfig {

private final ObjectFactory<HttpMessageConverters> messageConverters;

private final DgcConfigProperties config;

/**
* Form Encoder for Multipart File Upload.
*/
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}

/**
* Configure the client depending on the ssl properties.
*
* @return an Apache Http Client with or without SSL features
*/
@Bean
public Client assetManagerClient() throws NoSuchAlgorithmException {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

httpClientBuilder.setSSLContext(SSLContext.getDefault());
httpClientBuilder.setSSLHostnameVerifier(new DefaultHostnameVerifier());

return new ApacheHttpClient(httpClientBuilder.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

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

import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
Expand All @@ -31,7 +32,8 @@
public class DgcConfigProperties {

private final CertAuth certAuth = new CertAuth();
private final TrustAnchor trustAnchor = new TrustAnchor();
private final KeyStoreWithAlias trustAnchor = new KeyStoreWithAlias();
private final Publication publication = new Publication();

private String validationRuleSchema;

Expand All @@ -41,6 +43,22 @@ public class DgcConfigProperties {

private SignerInformation signerInformation = new SignerInformation();

@Getter
@Setter
public static class Publication {
private KeyStoreWithAlias keystore = new KeyStoreWithAlias();
private Boolean enabled;
private Boolean synchronizeEnabled;
private String url;
private String amngrUid;
private String path;
private String user;
private String password;
private String archiveFilename;
private String signatureFilename;
private List<String> notifyEmails = new ArrayList<>();
}

@Getter
@Setter
public static class JrcConfig {
Expand All @@ -61,7 +79,7 @@ public static class ProxyConfig {

@Getter
@Setter
public static class TrustAnchor {
public static class KeyStoreWithAlias {
private String keyStorePath;
private String keyStorePass;
private String certificateAlias;
Expand Down
67 changes: 35 additions & 32 deletions src/main/java/eu/europa/ec/dgc/gateway/config/DgcKeyStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,21 @@

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@RequiredArgsConstructor
@Slf4j
@Profile("!test")
public class DgcKeyStore {

private final DgcConfigProperties dgcConfigProperties;
Expand All @@ -44,14 +45,10 @@ public class DgcKeyStore {
* Creates a KeyStore instance with keys for DGC TrustAnchor.
*
* @return KeyStore Instance
* @throws KeyStoreException if no implementation for the specified type found
* @throws IOException if there is an I/O or format problem with the keystore data
* @throws CertificateException if any of the certificates in the keystore could not be loaded
* @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found
*/
@Bean
public KeyStore trustAnchorKeyStore() throws KeyStoreException, IOException,
CertificateException, NoSuchAlgorithmException {
@Qualifier("trustAnchor")
public KeyStore trustAnchorKeyStore() throws Exception {
KeyStore keyStore = KeyStore.getInstance("JKS");

loadKeyStore(
Expand All @@ -62,35 +59,41 @@ public KeyStore trustAnchorKeyStore() throws KeyStoreException, IOException,
return keyStore;
}

private void loadKeyStore(KeyStore keyStore, String path, char[] password)
throws CertificateException, NoSuchAlgorithmException, IOException {
/**
* Creates a KeyStore instance with keys for DGC Publication Feature.
*
* @return KeyStore Instance
*/
@Bean
@Qualifier("publication")
@ConditionalOnProperty("dgc.publication.enabled")
public KeyStore publicationKeyStore() throws Exception {
KeyStore keyStore = KeyStore.getInstance("JKS");

InputStream fileStream;
loadKeyStore(
keyStore,
dgcConfigProperties.getPublication().getKeystore().getKeyStorePath(),
dgcConfigProperties.getPublication().getKeystore().getKeyStorePass().toCharArray());

if (path.startsWith("classpath:")) {
String resourcePath = path.substring(10);
fileStream = getClass().getClassLoader().getResourceAsStream(resourcePath);
} else {
File file = new File(path);
fileStream = file.exists() ? getStream(path) : null;
}
return keyStore;
}

if (fileStream != null && fileStream.available() > 0) {
private void loadKeyStore(KeyStore keyStore, String path, char[] password) throws Exception {
try (InputStream fileStream = getStream(path)) {
keyStore.load(fileStream, password);
fileStream.close();
} else {
keyStore.load(null);
log.info("Could not find Keystore {}", path);
} catch (Exception e) {
log.error("Could not load Keystore {}", path);
throw e;
}

}

private InputStream getStream(String path) {
try {
return new FileInputStream(path);
} catch (IOException e) {
log.info("Could not find Keystore {}", path);
private InputStream getStream(String path) throws FileNotFoundException {
if (path.startsWith("classpath:")) {
String resourcePath = path.substring(10);
return getClass().getClassLoader().getResourceAsStream(resourcePath);
} else {
File file = new File(path);
return file.exists() ? new FileInputStream(path) : null;
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*-
* ---license-start
* EU Digital Green Certificate Gateway Service / dgc-gateway
* ---
* Copyright (C) 2021 - 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.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class AssetManagerSynchronizeResponseDto {

/**
* Initialize Dto with values for embedded sub-classed.
*/
public AssetManagerSynchronizeResponseDto(
String status, int statusCode, String message, String path, String token) {
Ocs.Data data = new Ocs.Data(statusCode, message, token, path);
Ocs.Meta meta = new Ocs.Meta(status, statusCode, message);

ocs = new Ocs(meta, data);
}

private Ocs ocs;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Ocs {

private Meta meta;
private Data data;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Meta {
private String status;
private Integer statuscode;
private String message;
}

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Data {
private Integer statusCode;
private String statusMessage;
private String token;
private String path;
}
}
}
Loading

0 comments on commit 9997e89

Please sign in to comment.