aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/gv/egiz/moazs/backend
diff options
context:
space:
mode:
authorChristof Rabensteiner <christof.rabensteiner@iaik.tugraz.at>2019-07-09 14:11:47 +0200
committerChristof Rabensteiner <christof.rabensteiner@iaik.tugraz.at>2019-07-09 14:11:47 +0200
commit8aba1b4f18f5fbfebdf239b4b4945b628e439905 (patch)
tree0004115e44b8d6ddf48542b7413d4f54fa76dc27 /src/main/java/at/gv/egiz/moazs/backend
parent65163646205b6e05139485fe957bceabe531f447 (diff)
downloadmoa-zs-8aba1b4f18f5fbfebdf239b4b4945b628e439905.tar.gz
moa-zs-8aba1b4f18f5fbfebdf239b4b4945b628e439905.tar.bz2
moa-zs-8aba1b4f18f5fbfebdf239b4b4945b628e439905.zip
Refactor Needles Interfaces and Rename "process" to "backend"
Reason: - Interfaces with a single method can be replaced with interfaces from java.util.function. - Less interfaces = less code = less maintenance! - Spring can inject beans by name so we name dependencies correctly to prevent ambiguity. Others: - Rename process to backend since backend gives a better description of its components.
Diffstat (limited to 'src/main/java/at/gv/egiz/moazs/backend')
-rw-r--r--src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java112
-rw-r--r--src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java75
-rw-r--r--src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java89
-rw-r--r--src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java116
4 files changed, 392 insertions, 0 deletions
diff --git a/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java b/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java
new file mode 100644
index 0000000..06eba80
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java
@@ -0,0 +1,112 @@
+package at.gv.egiz.moazs.backend;
+
+
+import at.gv.egiz.moazs.MoaZSException;
+import at.gv.egiz.moazs.client.ClientFactory;
+import at.gv.egiz.moazs.client.TnvzHelper;
+import at.gv.egiz.moazs.repository.DeliveryRepository;
+import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;
+import at.gv.egiz.moazs.scheme.RequestStatusResponse;
+import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
+import at.gv.zustellung.msg.xsd.App2ZusePort;
+import at.gv.zustellung.tnvz.xsd.TNVZServicePort;
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import java.util.function.Consumer;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
+import static at.gv.egiz.moazs.scheme.RequestStatusResponse.generateErrorFromException;
+import static java.lang.String.format;
+
+@Component
+@Profile("!cluster")
+public class DeliveryRequestBackend implements Consumer<String> {
+
+ private static final Logger log = Logger.getLogger(DeliveryRequestBackend.class);
+ private static final String MZS_PIPELINE_ERROR_MSG =
+ "An error occured while processing the DeliveryRequest with AppDeliveryID=%s. ";
+
+ private final DeliveryRepository repository;
+ private final TnvzHelper tnvzHelper;
+ private final Mzs2MsgConverter converter;
+ private final ClientFactory clientFactory;
+ private final Consumer<byte[]> signatureVerifier;
+
+ @Autowired
+ public DeliveryRequestBackend(DeliveryRepository repository,
+ TnvzHelper tnvzHelper,
+ Mzs2MsgConverter converter,
+ ClientFactory clientFactory,
+ Consumer<byte[]> signatureVerifier) {
+ this.repository = repository;
+ this.tnvzHelper = tnvzHelper;
+ this.converter = converter;
+ this.clientFactory = clientFactory;
+ this.signatureVerifier = signatureVerifier;
+ }
+
+ /**
+ * Performs all {@code DeliveryRequest}'s Back-End Tasks.
+ *
+ * Fetches {@code DeliveryRequest} referred by appDeliveryID from
+ * {@code DeliveryRepository}, makes sure that all necessary
+ * tasks (query tnvz, query msg, accept status) are executed and
+ * stores the response back to {@code DeliveryRepository}.
+ * @param appDeliveryID
+ */
+ @Override
+ public void accept(String appDeliveryID) {
+
+ var exceptionBuilder = moaZSExceptionBuilder();
+
+ try {
+ var mzsRequest = repository.retrieveDeliveryRequest(appDeliveryID).orElseThrow();
+ exceptionBuilder.withDeliverySystem(mzsRequest);
+
+ at.gv.zustellung.msg.xsd.DeliveryRequestType msgRequest = buildMsgRequest(mzsRequest, exceptionBuilder);
+
+ var msgClientParams = mzsRequest.getConfig().getMSGClient();
+ App2ZusePort client = clientFactory.create(msgClientParams, App2ZusePort.class);
+ var status = client.delivery(msgRequest);
+
+ var response = new RequestStatusResponse(status);
+ exceptionBuilder.withAllParametersInAnswer(response.getAnswer());
+
+ verifySignedStatus(response.getResponseID(), appDeliveryID, exceptionBuilder);
+ repository.store(response);
+
+ } catch (MoaZSException exception) {
+ log.error(format(MZS_PIPELINE_ERROR_MSG, appDeliveryID), exception);
+ var errorResponse = generateErrorFromException(exception);
+ repository.store(errorResponse);
+ }
+ }
+
+ private void verifySignedStatus(String responseID, String appDeliveryID, MoaZSException.Builder exceptionBuilder) throws MoaZSException {
+ try {
+ var signedStatus = repository.retrieveBinaryResponse(responseID).get();
+ signatureVerifier.accept(signedStatus);
+ } catch (MoaZSException ex) {
+ throw exceptionBuilder.withMessage(format(MsgResponseBackend.MOASP_SIGNATURE_INVALID_ERROR_MSG, appDeliveryID))
+ .withErrorCode(MoaZSException.ERROR_MOASP_SIGNATURE_INVALID)
+ .withCause(ex)
+ .build();
+ }
+ }
+
+ private at.gv.zustellung.msg.xsd.DeliveryRequestType buildMsgRequest(DeliveryRequestType mzsRequest,
+ MoaZSException.Builder exceptionBuilder) throws MoaZSException {
+ if (mzsRequest.getConfig().isPerformQueryPersonRequest()) {
+ var tnvzClientParams = mzsRequest.getConfig().getTNVZClient();
+ TNVZServicePort tvnzPort = clientFactory.create(tnvzClientParams, TNVZServicePort.class);
+ var identification = tnvzHelper.performQueryPersonRequest(mzsRequest, tvnzPort, exceptionBuilder);
+ return converter.convert(mzsRequest, identification);
+ } else {
+ return converter.convert(mzsRequest);
+ }
+ }
+
+}
diff --git a/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java b/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java
new file mode 100644
index 0000000..414c2dc
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/backend/MsgResponseBackend.java
@@ -0,0 +1,75 @@
+package at.gv.egiz.moazs.backend;
+
+import at.gv.egiz.moazs.MoaZSException;
+import at.gv.egiz.moazs.repository.DeliveryRepository;
+import at.gv.egiz.moazs.scheme.MsgResponse;
+import at.gv.egiz.moazs.service.MsgService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
+import static java.lang.String.format;
+import static java.util.concurrent.CompletableFuture.supplyAsync;
+
+@Component
+public class MsgResponseBackend implements Consumer<String> {
+
+ private static final Logger log = LoggerFactory.getLogger(MsgService.class);
+
+ public static final String MOASP_SIGNATURE_INVALID_ERROR_MSG = "Signature of Msg Response " +
+ "with AppDeliveryID=%s is not valid.";
+
+ private final DeliveryRepository repository;
+ private final Consumer<byte[]> signatureVerifier;
+ private final Function<MsgResponse, CompletableFuture<Void>> sink;
+
+ @Autowired
+ public MsgResponseBackend(DeliveryRepository repository,
+ Consumer<byte[]> signatureVerifier,
+ Function<MsgResponse, CompletableFuture<Void>> sink) {
+ this.repository = repository;
+ this.signatureVerifier = signatureVerifier;
+ this.sink = sink;
+ }
+
+ /**
+ * Performs all {@code MsgResponse}'s Back-End Tasks, such as verifying
+ * its signature and archiving the response.
+ *
+ * @param responseID refers to MsgResponse Object.
+ */
+ @Override
+ public void accept(String responseID) {
+ supplyAsync(() -> verify(responseID)).thenAcceptAsync(sink::apply);
+ }
+
+ public MsgResponse verify(String responseID) {
+
+ var response = repository.retrieveResponse(responseID).get();
+ var builder = moaZSExceptionBuilder().withAllParametersInAnswer(response.getAnswer());
+
+ var binaryResponse = repository.retrieveBinaryResponse(responseID).get();
+
+ try {
+ signatureVerifier.accept(binaryResponse);
+ return response;
+ } catch (MoaZSException ex) {
+ log.error(ex.getMessage(), ex);
+ var wrappingEx = builder
+ .withMessage(format(MOASP_SIGNATURE_INVALID_ERROR_MSG, response.getAppDeliveryID()))
+ .withErrorCode(MoaZSException.ERROR_MOASP_SIGNATURE_INVALID)
+ .withCause(ex)
+ .build();
+
+ return response.generateError(wrappingEx);
+ }
+
+ }
+
+}
diff --git a/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java b/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java
new file mode 100644
index 0000000..02771a9
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java
@@ -0,0 +1,89 @@
+package at.gv.egiz.moazs.backend;
+
+import at.gv.egiz.moazs.repository.DeliveryRepository;
+import at.gv.egiz.moazs.scheme.Marshaller;
+import at.gv.egiz.moazs.scheme.MsgResponse;
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
+import static java.lang.String.format;
+import static java.util.concurrent.CompletableFuture.allOf;
+import static java.util.concurrent.CompletableFuture.supplyAsync;
+
+@Component
+public class SaveResponseToFileSink implements Function<MsgResponse, CompletableFuture<Void>> {
+
+ private static final Logger log = LoggerFactory.getLogger(SaveResponseToFileSink.class);
+ private static final String SAVING_FAILED_MSG = "Could not save response with AppDeliveryId=%s.";
+
+ private final SimpleDateFormat isoFormatter;
+ private final Marshaller msgMarshaller;
+ private final DeliveryRepository repository;
+ private final String root;
+
+ @Autowired
+ public SaveResponseToFileSink(Marshaller msgMarshaller, DeliveryRepository repository, String root) {
+ this.msgMarshaller = msgMarshaller;
+ this.repository = repository;
+ this.root = root;
+ this.isoFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ }
+
+ @Override
+ public CompletableFuture<Void> apply(MsgResponse response) {
+
+ var responseID = response.getResponseID();
+
+ var responsePath = generatePath(responseID, "xml");
+ var storeResponseToFileSystemFuture = supplyAsync(() -> msgMarshaller.marshallXml(response.getResponse()))
+ .thenApply(responseString -> responseString.getBytes(StandardCharsets.UTF_8))
+ .thenAccept(responseByteArray -> storeToFile(responsePath, responseByteArray))
+ .exceptionally(ex -> logException(ex, responseID));
+
+ var binaryResponsePath = generatePath(responseID, "binary.xml");
+ var storeBinaryResponseToFileSystemFuture = supplyAsync(() -> repository.retrieveBinaryResponse(responseID).get())
+ .thenAccept(binaryResponseByteArray -> storeToFile(binaryResponsePath, binaryResponseByteArray))
+ .exceptionally(ex -> logException(ex, responseID));
+
+ return allOf(storeResponseToFileSystemFuture, storeBinaryResponseToFileSystemFuture);
+
+ }
+
+ private String generatePath(String id, String suffix) {
+ var folder = sanitizeFileString(id);
+ var nowInIso8601 = isoFormatter.format(new Date());
+ return format("%s/%s/%s.%s", root, folder, nowInIso8601, suffix);
+ }
+
+ private String sanitizeFileString(String fileString) {
+ return fileString.replaceAll("[^a-zA-Z0-9\\._\\-]", "");
+ }
+
+ private Void logException(Throwable ex, String appDeliveryID) {
+ if(log.isErrorEnabled()) {
+ log.error(format(SAVING_FAILED_MSG, appDeliveryID), ex);
+ }
+ return null;
+ }
+
+ private void storeToFile(String path, byte[] content) {
+ try {
+ FileUtils.writeByteArrayToFile(new File(path), content);
+ } catch (IOException e) {
+ throw moaZSException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java b/src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java
new file mode 100644
index 0000000..e9c5387
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/backend/SignatureVerifier.java
@@ -0,0 +1,116 @@
+package at.gv.egiz.moazs.backend;
+
+import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.ISignatureVerificationService;
+import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.data.IXMLSignatureVerificationResponse;
+import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.exceptions.MOASigServiceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.function.Consumer;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
+import static at.gv.egiz.moazs.MoaZSException.moaZSExceptionBuilder;
+import static java.lang.String.format;
+
+public class SignatureVerifier implements Consumer<byte[]> {
+
+ private static final Logger log = LoggerFactory.getLogger(SignatureVerifier.class);
+ private static final int OK_CODE = 0;
+
+ private final ISignatureVerificationService service;
+ private final String trustProfile;
+ private final boolean isManifestCheckActive;
+
+ public SignatureVerifier(ISignatureVerificationService service,
+ String trustProfile, boolean isManifestCheckActive) {
+ this.service = service;
+ this.trustProfile = trustProfile;
+ this.isManifestCheckActive = isManifestCheckActive;
+ }
+
+ /**
+ * Verifies the signature of a signed XML document. Throws a at.gv.egiz.moazs.MoaZSException exception
+ * if the validation fails.
+ * @param signedXMLdocument
+ * @throws at.gv.egiz.moazs.MoaZSException
+ */
+ @Override
+ public void accept(byte[] signedXMLdocument) {
+
+ try {
+ var response = service.verifyXMLSignature(signedXMLdocument, trustProfile);
+
+ if (log.isDebugEnabled()) {
+ print(response);
+ }
+
+ if (response == null) {
+ throw moaZSException("MOA SPSS could not find the signature. ");
+ }
+
+ var builder = new StringBuilder();
+
+ if (response.getSignatureCheckCode() != OK_CODE) {
+ builder.append(format("Signature is not valid; SignatureCheckCode was %d. ",
+ response.getSignatureCheckCode()));
+ }
+
+ if (response.getCertificateCheckCode() != OK_CODE) {
+ builder.append(format("Certificate chain is not valid; CertificateCheckCode was %d. ",
+ response.getCertificateCheckCode()));
+ }
+
+ if (response.getSignatureManifestCheckCode() != OK_CODE) {
+ var signatureManifestErrorMsg = format("Signature Manifest is not valid; " +
+ "SignatureManifestCheckCode was %d. ", response.getSignatureManifestCheckCode());
+ if (isManifestCheckActive) {
+ builder.append(signatureManifestErrorMsg);
+ } else {
+ log.warn(signatureManifestErrorMsg);
+ }
+ }
+
+ if (response.isXmlDSIGManigest() && response.getXmlDSIGManifestCheckCode() != OK_CODE) {
+ var xmlDSIGManifestErrorMsg = format("XmlDSIGManifest Manifest is not valid; " +
+ "XmlDSIGManifest was %d. ", response.getXmlDSIGManifestCheckCode());
+ if (isManifestCheckActive) {
+ builder.append(xmlDSIGManifestErrorMsg);
+ } else {
+ log.warn(xmlDSIGManifestErrorMsg);
+ }
+ }
+
+ var msg = builder.toString();
+
+ if(msg.length() > 0) {
+ throw moaZSException(msg);
+ }
+
+ } catch (MOASigServiceException e) {
+ throw moaZSExceptionBuilder("Could not accept the XML signature.")
+ .withCause(e)
+ .build();
+ }
+
+ }
+
+ private void print(IXMLSignatureVerificationResponse response) {
+ log.debug("Response:");
+
+ if (response == null) {
+ log.debug("null");
+ return;
+ }
+
+ log.debug(" XmlDsigSubjectName: {}", response.getXmlDsigSubjectName());
+ log.debug(" SignatureManifestCheckCode: {}", response.getSignatureManifestCheckCode());
+ log.debug(" XmlDSIGManifestCheckCode: {}", response.getXmlDSIGManifestCheckCode());
+ log.debug(" CertificateCheckCode: {}", response.getCertificateCheckCode());
+ log.debug(" SignatureCheckCode: {}", response.getSignatureCheckCode());
+ log.debug(" SigningDateTime: {}", response.getSigningDateTime());
+ log.debug(" isXmlDSIGManigest: {}", response.isXmlDSIGManigest());
+ log.debug(" isPublicAuthority: {}", response.isPublicAuthority());
+ log.debug(" isQualifiedCertificate: {}", response.isQualifiedCertificate());
+ log.debug(" getPublicAuthorityCode: {}", response.getPublicAuthorityCode());
+ }
+}