diff options
| author | Christof Rabensteiner <christof.rabensteiner@iaik.tugraz.at> | 2019-07-22 13:02:19 +0200 | 
|---|---|---|
| committer | Christof Rabensteiner <christof.rabensteiner@iaik.tugraz.at> | 2019-07-22 13:02:19 +0200 | 
| commit | cb9a76eaafd37f921006822bcfe043655288bc63 (patch) | |
| tree | 76c2463e181ac293134f4c5bfc7e342607f3f399 /src | |
| parent | d873625c0ced62e712dc1b1a7570b63482fd0a0a (diff) | |
| download | moa-zs-cb9a76eaafd37f921006822bcfe043655288bc63.tar.gz moa-zs-cb9a76eaafd37f921006822bcfe043655288bc63.tar.bz2 moa-zs-cb9a76eaafd37f921006822bcfe043655288bc63.zip | |
Test Flow of DeliveryRequest from "End-To-End" & Fix Bugs
Schema Changes:
- Remove mzs:DeliveryRequest/TnvzMetaData because all metadata fields
  can be collected from DeliveryRequest and redundancy is not needed.
Fixes and Refactoring in preprocess:
- MzsDeliveryRequestValidator: Instead of returning false, throw an
  exception when a condition is not met, and explain which condition
  is not met / why it is not met in the exception's message.
- Integrate interface change in ConfigProfileGenerator and
  DeliveryRequestAugmenter.
- Rewrite and simplify DeliveryRequestAugmenter's augmentation.
- ConfigUtil Fixes: Ensure that we do not override the wrong
  parameters while merging. This error appeared in tnvz / msg client,
  connection / receive timeout, key / trust store, and lax hostname
  verification / trust all.
Fix Bugs in Interceptor / SoapUtils:
- Problem: DOM access and information extraction was implemented
  somewhat sloppy.
- SolutioN: Change DOM access interface to access DOM more
  efficiently. Add boundary checks and handle edge cases while
  extracting information from SOAP Messages.
- Test those changes properly.
Testing:
- Implement Delivery Request Flow in ITEndToEndTest.
- Start application on random port instead of fixed port when running
  integration tests.
- Add assertions to tests in ITMzsServiceTest suite.
Others Bug Fixes:
- ServicesConfig: Ensure that mzs service and msg service run on
  different endpoint addresses (/msg and /mzs).
- DeliveryRequestBackend: Throw exception when binary message is
  missing. Don't wrap the exception.
- SaveResponseToFileSink: Wrap Response in JAXB Element (otherwise,
  marshaller does not recognize it)
Diffstat (limited to 'src')
26 files changed, 961 insertions, 187 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 index 6a1e0fd..72f8ba0 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java +++ b/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java @@ -18,10 +18,10 @@ import org.springframework.stereotype.Component;  import java.util.function.Consumer; -import static at.gv.egiz.moazs.scheme.RequestStatusResponse.*; +import static at.gv.egiz.moazs.MoaZSException.moaZSException;  import static at.gv.egiz.moazs.scheme.RequestStatusResponse.generateError; +import static at.gv.egiz.moazs.scheme.RequestStatusResponse.getAnswer;  import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Error.errorBuilder; -import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder;  import static java.lang.String.format;  @Component @@ -72,7 +72,7 @@ public class DeliveryRequestBackend implements Consumer<String> {          try {              var mzsRequest = repository.retrieveDeliveryRequest(appDeliveryID).orElseThrow( -                    () -> MoaZSException.moaZSException(format(DELIVERY_REQUEST_MISSING_ERROR_MSG, appDeliveryID))); +                    () -> moaZSException(format(DELIVERY_REQUEST_MISSING_ERROR_MSG, appDeliveryID)));              fallbackAnswerBuilder.withDeliverySystem(mzsRequest.getConfig().getMSGClient().getURL());              var msgRequest = buildMsgRequest(mzsRequest); @@ -98,14 +98,15 @@ public class DeliveryRequestBackend implements Consumer<String> {      }      private void verifySignedStatus(String responseID, String appDeliveryID) throws MoaZSException { +        var signedStatus = repository.retrieveBinaryResponse(responseID).orElseThrow(() -> moaZSException( +                format(BINARY_RESPONSE_MISSING_ERROR_MSG, responseID), +                MoaZSException.ERROR_MZS_BINARY_RESPONSE_MISSING));          try { -            var signedStatus = repository.retrieveBinaryResponse(responseID).orElseThrow( -                    () -> MoaZSException.moaZSException(format(BINARY_RESPONSE_MISSING_ERROR_MSG, responseID)));              signatureVerifier.accept(signedStatus);          } catch (MoaZSException ex) {              var message = format(MsgResponseBackend.MOASP_SIGNATURE_INVALID_ERROR_MSG, appDeliveryID);              var code = MoaZSException.ERROR_MOASP_SIGNATURE_INVALID; -            throw MoaZSException.moaZSException(message, code, ex); +            throw moaZSException(message, code, ex);          }      } diff --git a/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java b/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java index 2468ca9..c58e1a5 100644 --- a/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java +++ b/src/main/java/at/gv/egiz/moazs/backend/SaveResponseToFileSink.java @@ -50,7 +50,7 @@ public class SaveResponseToFileSink {          var responseID = response.getResponseID();          var responsePath = generatePath(rootPath, responseID, "xml"); -        var storeResponseToFileSystemFuture = supplyAsync(() -> msgMarshaller.marshallXml(response.getResponse())) +        var storeResponseToFileSystemFuture = supplyAsync(() -> msgMarshaller.marshallXml(response.getResponseAsJAXBElement()))                  .thenApply(responseString -> responseString.getBytes(StandardCharsets.UTF_8))                  .thenAccept(responseByteArray -> storeToFile(responsePath, responseByteArray))                  .exceptionally(ex -> logException(ex, responseID)); diff --git a/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java b/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java index de22805..a304bdf 100644 --- a/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java +++ b/src/main/java/at/gv/egiz/moazs/client/TnvzHelper.java @@ -96,18 +96,16 @@ public class TnvzHelper {          var builder = metaDataBuilder(); -        var meta = request.getTnvzMetaData(); - +        var meta = request.getMetaData();          if (meta.getDeliveryQuality() != null) {              builder.withDeliveryQuality(meta.getDeliveryQuality());          } else {              builder.withPrivateMessageQuality(meta.getPrivateMessageQuality());          } -        return builder -                .withOrigin(meta.getOrigin()) -                .withPreAdviceNote(request.getReceiver().getPreAdviceNote()) +        return builder.withOrigin(meta.getOrigin())                  .withIgnorePostRedirectionOrder(meta.getIgnorePostRedirectionOrder()) +                .withPreAdviceNote(request.getReceiver().getPreAdviceNote())                  .build();      } diff --git a/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java b/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java index c16dfd2..6f71e50 100644 --- a/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java +++ b/src/main/java/at/gv/egiz/moazs/config/ServicesConfig.java @@ -23,14 +23,15 @@ public class ServicesConfig {      @Autowired      public Endpoint msgEndpoint(MsgService msgService,                                  Zuse2AppPortService zuse2app, -                                Interceptor<Message> msgInterceptor) { -        return endpointFactory.create(msgService, zuse2app, msgInterceptor); +                                Interceptor<Message> msgInterceptor +    ) { +        return endpointFactory.create(msgService, zuse2app, "/msg", msgInterceptor);      }      @Bean      @Autowired      public Endpoint mzsEndpoint(MzsService mzsService, App2Mzs app2mzs) { -        return endpointFactory.create(mzsService, app2mzs); +        return endpointFactory.create(mzsService, app2mzs, "/mzs");      }      @Bean diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java index 5e81f0d..0637f98 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java @@ -11,6 +11,8 @@ import java.util.Map;  import java.util.Map.Entry;  import java.util.Set; +import static at.gv.egiz.moazs.MoaZSException.moaZSException; +import static java.lang.String.format;  import static java.util.stream.Collectors.*;  public class ConfigProfileGenerator { @@ -70,9 +72,11 @@ public class ConfigProfileGenerator {          var defaultProfile = profiles.get(defaultConfigKey); -        if (!validator.isConfigProfileComplete(defaultProfile)) { +        try { +            validator.isConfigProfileComplete(defaultProfile); +        } catch (MoaZSException ex) {              if (verifyCompletenessOfDefaultConfiguration) -                throw MoaZSException.moaZSException(PROFILE_NOT_COMPLETE_ERROR_MESSAGE); +                throw moaZSException(format("%s Reason: %s", PROFILE_NOT_COMPLETE_ERROR_MESSAGE, ex.getMessage()));              else {                  LOGGER.warn(PROFILE_NOT_COMPLETE_WARNING_MESSAGE);              } diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java index 056f6dc..f49132f 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java @@ -205,7 +205,7 @@ public class ConfigUtil {          }          if (primary.getTNVZClient() != null) { -            builder.withMSGClient(merge(primary.getTNVZClient(), fallback.getTNVZClient())); +            builder.withTNVZClient(merge(primary.getTNVZClient(), fallback.getTNVZClient()));          }          if (primary.getMsgResponseSinks() != null) { @@ -240,7 +240,7 @@ public class ConfigUtil {          }          if (primary.getReceiveTimeout() != null) { -            builder.withConnectionTimeout(primary.getReceiveTimeout()); +            builder.withReceiveTimeout(primary.getReceiveTimeout());          }          return builder.build(); @@ -259,7 +259,7 @@ public class ConfigUtil {          }          if (primary.getTrustStore() != null) { -            builder.withKeyStore(merge(primary.getTrustStore(), fallback.getTrustStore())); +            builder.withTrustStore(merge(primary.getTrustStore(), fallback.getTrustStore()));          }          if (primary.isLaxHostNameVerification() != null) { @@ -267,8 +267,9 @@ public class ConfigUtil {          }          if (primary.isTrustAll() != null) { -            builder.withLaxHostNameVerification(primary.isTrustAll()); +            builder.withTrustAll(primary.isTrustAll());          } +          return builder.build();      } diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java b/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java index e7ee357..240a677 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/DeliveryRequestAugmenter.java @@ -1,36 +1,41 @@  package at.gv.egiz.moazs.preprocess; +import at.gv.egiz.moazs.scheme.Marshaller;  import at.gv.zustellung.app2mzs.xsd.ConfigType;  import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType; +import at.gv.zustellung.app2mzs.xsd.ObjectFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory;  import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.Nullable;  import org.springframework.stereotype.Component;  import java.util.Map;  import static at.gv.egiz.moazs.MoaZSException.moaZSException; +import static at.gv.egiz.moazs.util.NullCoalesce.coalesce;  import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.deliveryRequestTypeBuilder; -import static java.lang.String.format;  @Component  public class DeliveryRequestAugmenter { +    private static final Logger log = LoggerFactory.getLogger(DeliveryRequestAugmenter.class); +    private static final ObjectFactory FACTORY = new ObjectFactory(); +      private final ConfigUtil util;      private final Map<String, ConfigType> configs;      private final MzsDeliveryRequestValidator validator; +    private final Marshaller mzsMarshaller; -    private static final String INCOMPLETE_TNVZ_ERROR_MESSAGE = "mzs:DeliveryRequest is incomplete because mandatory " + -            "fields for sending a tnvz:QueryPersonRequest are missing."; -    private static final String INCOMPLETE_CONFIG_ERROR_MESSAGE = "Could not find a profile for " + -            "the delivery request configuration, and the configuration attached to mzs:DeliveryRequest is incomplete."; -    private static final String INCOMPLETE_MERGED_CONFIG_ERROR_MESSAGE = "I merged parameters from " + -            "mzs:DeliveryRequest/Config with parameters from config profile with ProfileId='%s', but the " + -            "configuration is incomplete."; +    private static final String CONFIG_MISSING_ERROR_MSG = "Delivery request configuration is missing.";      @Autowired -    public DeliveryRequestAugmenter(Map<String, ConfigType> deliveryRequestConfigs, ConfigUtil util, MzsDeliveryRequestValidator validator) { +    public DeliveryRequestAugmenter(Map<String, ConfigType> deliveryRequestConfigs, ConfigUtil util, +                                    MzsDeliveryRequestValidator validator, Marshaller mzsMarshaller) {          this.configs = deliveryRequestConfigs;          this.util = util;          this.validator = validator; +        this.mzsMarshaller = mzsMarshaller;      }      /** @@ -46,41 +51,29 @@ public class DeliveryRequestAugmenter {          var fallbackProfileId = determineProfileIdFrom(requestConfig);          var fallbackConfig = configs.get(fallbackProfileId); -        if (fallbackConfig == null) { - -            if (!validator.isConfigProfileComplete(request.getConfig())) { -                throw moaZSException(INCOMPLETE_CONFIG_ERROR_MESSAGE); -            } else if (!validator.isTnvzComplete(request)) { -                throw moaZSException(INCOMPLETE_TNVZ_ERROR_MESSAGE); -            } else { -                return request; -            } +        trace("request config", requestConfig); +        trace("fallback config", fallbackConfig); -        } else { +        var augmentedConfig = (requestConfig != null && fallbackConfig != null) +                ? util.merge(requestConfig, fallbackConfig) +                : coalesce(requestConfig, fallbackConfig) +                    .orElseThrow(()-> moaZSException(CONFIG_MISSING_ERROR_MSG)); -            var mergedConfig = (requestConfig == null) -                    ? fallbackConfig -                    : util.merge(requestConfig, fallbackConfig); +        trace("augmented config", augmentedConfig); -            if (!validator.isConfigProfileComplete(mergedConfig)) { -                var message = format(INCOMPLETE_MERGED_CONFIG_ERROR_MESSAGE, fallbackProfileId); -                throw moaZSException(message); -            } +        validator.isConfigProfileComplete(augmentedConfig); -            var mergedRequest = deliveryRequestTypeBuilder(request) -                    .withConfig(mergedConfig) -                    .build(); +        var augmentedRequest = deliveryRequestTypeBuilder(request) +                .withConfig(augmentedConfig) +                .build(); -            if (!validator.isTnvzComplete(mergedRequest)) { -                throw moaZSException(INCOMPLETE_TNVZ_ERROR_MESSAGE); -            } +        validator.isTnvzComplete(augmentedRequest); -            return mergedRequest; +        return augmentedRequest; -        }      } -    private String determineProfileIdFrom(ConfigType requestConfig) { +    private String determineProfileIdFrom(@Nullable ConfigType requestConfig) {          return (requestConfig == null                  || requestConfig.getProfileID() == null                  || isProfileMissing(requestConfig.getProfileID())) @@ -92,4 +85,11 @@ public class DeliveryRequestAugmenter {          return !configs.containsKey(id);      } +    private void trace(String description, ConfigType config) { +        if (log.isTraceEnabled()) { +            log.trace("{} : {}", description, mzsMarshaller.marshallXml(FACTORY.createConfig(config))); +        } +    } + +  } diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java b/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java index 2c2fc36..67086a2 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/MzsDeliveryRequestValidator.java @@ -1,90 +1,168 @@  package at.gv.egiz.moazs.preprocess; +import at.gv.egiz.moazs.MoaZSException;  import at.gv.zustellung.app2mzs.xsd.*;  import org.springframework.lang.Nullable;  import org.springframework.stereotype.Component; +import static at.gv.egiz.moazs.MoaZSException.moaZSException; +import static at.gv.egiz.moazs.scheme.NameSpace.*; +import static java.lang.String.format; +  @Component  public class MzsDeliveryRequestValidator {      /**       * Checks if the mandatory fields that are needed to send a tnvz:QueryPersonRequest are present.       * @param request -     * @return true if mandatory fields are present. +     * @throws MoaZSException if a field is missing.       */ -    public boolean isTnvzComplete(DeliveryRequestType request) { -        return !request.getConfig().isPerformQueryPersonRequest() || -                (request.getTnvzMetaData() != null -                && request.getSender().getCorporateBody() != null); +    public void isTnvzComplete(DeliveryRequestType request) { +        if (request.getConfig().isPerformQueryPersonRequest()) { + +            if (request.getMetaData().getOrigin() == null) +                throw mzse(MZS_DELIVERY_REQUEST, MZS_DELIVERY_REQUEST + "/MetaData/Origin is missing."); + +            if (request.getMetaData().getDeliveryQuality() == null) +                throw mzse(MZS_DELIVERY_REQUEST, MZS_DELIVERY_REQUEST + "/MetaData/DeliveryQuality missing."); + +            if (request.getSender().getCorporateBody() == null) +                throw mzse(MZS_DELIVERY_REQUEST, MZS_DELIVERY_REQUEST + "/Sender/CorporateBody is missing."); +        }      }      /**       * Check if all mandatory fields of configuration are present.       *       * @param profile -     * @return true if all mandatory fields are present. +     * @throws MoaZSException if a field is missing.       */ -    public boolean isConfigProfileComplete(@Nullable ConfigType profile) { -        return profile != null -                && profile.isPerformQueryPersonRequest() != null -                && isTVNZClientConfigured(profile.getTNVZClient(), profile.isPerformQueryPersonRequest()) -                && isClientConfigured(profile.getMSGClient()) -                && areSinksConfigured(profile.getMsgResponseSinks()); +    public void isConfigProfileComplete(@Nullable ConfigType profile) { + +        if (profile == null) +            throw mzse(MZS_DELIVERY_REQUEST + "/Config"); + +        if (profile.isPerformQueryPersonRequest() == null) +            throw mzse(MZS_DELIVERY_REQUEST + "/Config", "PerformQueryPersonRequest is missing."); + +        isTNVZClientConfigured(profile.getTNVZClient(), profile.isPerformQueryPersonRequest()); +        areSinksConfigured(profile.getMsgResponseSinks()); + +        try { +            isClientConfigured(profile.getMSGClient()); +        } catch (MoaZSException ex) { +            throw mzse(MZS_MSGCLIENT, ex.getMessage()); +        } +      } -    private boolean isTVNZClientConfigured(ClientType tnvzClient, Boolean isPerformQueryPersonRequest) { -        return !isPerformQueryPersonRequest || (tnvzClient != null +    private void isTNVZClientConfigured(@Nullable ClientType tnvzClient, Boolean isPerformQueryPersonRequest) { +        if (!isPerformQueryPersonRequest) return; + +        var isConfigured = tnvzClient != null                  && tnvzClient.getURL() != null                  && tnvzClient.getReceiveTimeout() != null -                && tnvzClient.getConnectionTimeout() != null -                && isSSLConfigured(tnvzClient)); +                && tnvzClient.getConnectionTimeout() != null; + +        if (!isConfigured) { +            if(tnvzClient == null) throw mzse(MZS_TNVZCLIENT); + +            var reasons = new StringBuilder("The following elements in " + MZS_TNVZCLIENT + "  are missing: "); +            if(tnvzClient.getURL() == null)               reasons.append("URL;"); +            if(tnvzClient.getReceiveTimeout() == null)    reasons.append("ReceiveTimeout;"); +            if(tnvzClient.getConnectionTimeout() == null) reasons.append("ConnectionTimeout;"); +            throw mzse(MZS_TNVZCLIENT, reasons.toString()); +        } + +        try { +            isSSLConfigured(tnvzClient); +        } catch (MoaZSException ex) { +            throw mzse(MZS_TNVZCLIENT, ex.getMessage()); +        }      } -    private boolean isClientConfigured(ClientType clientParams) { -        return clientParams != null +    private void isClientConfigured(@Nullable ClientType clientParams) { +        var isConfigured = clientParams != null                  && clientParams.getURL() != null -                && isSSLConfigured(clientParams)                  && clientParams.getReceiveTimeout() != null                  && clientParams.getConnectionTimeout() != null; + +        if (!isConfigured) throw mzse("Client"); + +        isSSLConfigured(clientParams); +      } -    private boolean isSSLConfigured(ClientType clientParams) { -        return !clientParams.getURL().startsWith("https") || (clientParams.getSSL() != null -                && clientParams.getSSL().isTrustAll() != null -                && clientParams.getSSL().isLaxHostNameVerification() != null -                && isKeyStoreConfigured(clientParams.getSSL().getKeyStore()) -                && isTrustStoreConfigured(clientParams.getSSL().getTrustStore())); +    private void isSSLConfigured(ClientType clientParams) { +        if (!clientParams.getURL().startsWith("https")) return; + +        var isConfigured = (clientParams.getSSL() != null +                    && clientParams.getSSL().isTrustAll() != null +                    && clientParams.getSSL().isLaxHostNameVerification() != null); +        if (!isConfigured) throw mzse("SSL"); + +        try { +            isKeyStoreConfigured(clientParams.getSSL().getKeyStore()); +            isTrustStoreConfigured(clientParams.getSSL().getTrustStore()); +        } catch (MoaZSException ex) { +            throw mzse("SSL", ex.getMessage()); +        }      } -    private boolean isKeyStoreConfigured(KeyStoreType keyStore) { -        return keyStore == null || (keyStore.getPassword() != null +    private void isKeyStoreConfigured(@Nullable KeyStoreType keyStore) { +        if (keyStore == null) return; + +        var isConfigured = keyStore.getPassword() != null                  && keyStore.getFileType() != null -                && keyStore.getFileName() != null); +                && keyStore.getFileName() != null; +        if (!isConfigured) throw mzse("KeyStore");      } -    private boolean isTrustStoreConfigured(KeyStoreType trustStore) { -        return trustStore == null || (trustStore.getPassword() != null +    private void isTrustStoreConfigured(@Nullable KeyStoreType trustStore) { +        if (trustStore == null) return; + +        var isConfigured = trustStore.getPassword() != null                  && "JKS".equals(trustStore.getFileType()) -                && trustStore.getFileName() != null); +                && trustStore.getFileName() != null; +        if (!isConfigured) throw mzse("TrustStore");      } -    private boolean areSinksConfigured(MsgResponseSinksType sinks) { -        return sinks != null -                && sinks.isLogResponse() != null -                && isSaveResponseToFileConfigured(sinks.getSaveResponseToFile()) -                && isForwardResponseToServiceConfigured(sinks.getForwardResponseToService()); +    private void areSinksConfigured(@Nullable MsgResponseSinksType sinks) { +        var isConfigured = sinks != null && sinks.isLogResponse() != null; +        if (!isConfigured) throw mzse("MsgResponseSinks"); + +        isSaveResponseToFileConfigured(sinks.getSaveResponseToFile()); +        isForwardResponseToServiceConfigured(sinks.getForwardResponseToService());      } -    private boolean isSaveResponseToFileConfigured(SaveResponseToFileType fileSink) { -        return fileSink != null +    private void isSaveResponseToFileConfigured(@Nullable SaveResponseToFileType fileSink) { +        var isConfigured = fileSink != null                  && (!fileSink.isActive() || fileSink.getPath() != null); + +        if (!isConfigured) throw mzse("SaveResponseToFile"); +    } + +    private void isForwardResponseToServiceConfigured(@Nullable ForwardResponseToServiceType forwardSink) { +        if (forwardSink == null) throw mzse("ForwardResponseToService"); + +        if (forwardSink.isActive()) { +            try { +                isClientConfigured(forwardSink.getMzsClient()); +            } catch (MoaZSException e) { +                throw mzse("ForwardResponseToService", e.getMessage()); +            } +        }      } -    private boolean isForwardResponseToServiceConfigured(ForwardResponseToServiceType forwardSink) { -        return forwardSink != null -                && (!forwardSink.isActive() || isClientConfigured(forwardSink.getMzsClient())); +    private MoaZSException mzse(String missing) { +        return moaZSException(format("%s is not configured.", missing));      } +    private MoaZSException mzse(String missing, String reason) { +        return moaZSException(format("%s is not configured. Reason: %s", missing, reason)); +    } + +  } diff --git a/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java b/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java index 41b742b..b8a6d78 100644 --- a/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java +++ b/src/main/java/at/gv/egiz/moazs/repository/InMemoryDeliveryRepository.java @@ -4,6 +4,8 @@ package at.gv.egiz.moazs.repository;  import at.gv.egiz.moazs.scheme.MsgResponse;  import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;  import com.google.common.cache.Cache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory;  import org.springframework.context.annotation.Profile;  import org.springframework.stereotype.Repository; @@ -17,6 +19,8 @@ import static java.util.Optional.ofNullable;  @Profile("!cluster")  public class InMemoryDeliveryRepository implements DeliveryRepository { +    private static final Logger log = LoggerFactory.getLogger(InMemoryDeliveryRepository.class); +      private final Cache<String, DeliveryRequestType> requestRepository;      private final Cache<String, MsgResponse> responseRepository;      private final Cache<String, byte[]> binaryResponseRepository; @@ -31,33 +35,38 @@ public class InMemoryDeliveryRepository implements DeliveryRepository {      @Override      public void store(DeliveryRequestType request) {          var key = request.getMetaData().getAppDeliveryID(); +        log.trace("Store DeliveryRequest by key={}", key);          requestRepository.put(key, request);      }      @Override      public Optional<DeliveryRequestType> retrieveDeliveryRequest(String appDeliveryID) { +        log.trace("Retrieve DeliveryRequest by key={}", appDeliveryID);          return ofNullable(requestRepository.getIfPresent(appDeliveryID));      }      @Override      public void store(MsgResponse response) {          String key = response.getResponseID(); +        log.trace("Store MsgResponse by key={}", key);          responseRepository.put(key, response);      }      @Override      public Optional<MsgResponse> retrieveResponse(String responseID) { +        log.trace("Retrieve MsgResponse by key={}", responseID);          return ofNullable(responseRepository.getIfPresent(responseID));      }      @Override -    public void store(String id, byte[] data) { -        binaryResponseRepository.put(id, data); - +    public void store(String responseID, byte[] data) { +        log.trace("Store Binary MsgResponse by key={}", responseID); +        binaryResponseRepository.put(responseID, data);      }      @Override      public Optional<byte[]> retrieveBinaryResponse(String responseID) { +        log.trace("Retrieve Binary MsgResponse by key={}", responseID);          return ofNullable(binaryResponseRepository.getIfPresent(responseID));      } diff --git a/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java b/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java index 83ace5c..1a86079 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java @@ -32,6 +32,10 @@ public class Marshaller {      }      public <T> String marshallXml(final T obj) { +        if (obj == null) { +            return "null"; +        } +          StringWriter sw = new StringWriter();          Result result = new StreamResult(sw);          jaxbMarshaller.marshal(obj, result); diff --git a/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java b/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java index 6b6f34a..386a7a6 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/NameSpace.java @@ -5,6 +5,7 @@ public class NameSpace {      private NameSpace() {}      private static final at.gv.zustellung.msg.xsd.ObjectFactory MSG_FACTORY = new at.gv.zustellung.msg.xsd.ObjectFactory(); +    private static final at.gv.zustellung.app2mzs.xsd.ObjectFactory MZS_FACTORY = new at.gv.zustellung.app2mzs.xsd.ObjectFactory();      public static final String MSG_VERSION = "2.2.0";      public static final String MSG  = MSG_FACTORY.createDeliveryRequest(null).getName().getNamespaceURI(); @@ -18,4 +19,8 @@ public class NameSpace {      public static final String MSG_DELIVERY_NOTIFICATION = MSG_FACTORY.createDeliveryNotification(null).getName().getLocalPart();      public static final String MSG_APP_DELIVERY_ID = MSG_FACTORY.createAppDeliveryID("").getName().getLocalPart(); +    public static final String MZS_DELIVERY_REQUEST = MZS_FACTORY.createDeliveryRequest(null).getName().getLocalPart(); +    public static final String MZS_TNVZCLIENT = MZS_FACTORY.createTNVZClient(null).getName().getLocalPart(); +    public static final String MZS_MSGCLIENT = MZS_FACTORY.createTNVZClient(null).getName().getLocalPart(); +  } diff --git a/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java b/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java index 6e96a6b..461d8fe 100644 --- a/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java +++ b/src/main/java/at/gv/egiz/moazs/scheme/SOAPUtils.java @@ -3,8 +3,12 @@ package at.gv.egiz.moazs.scheme;  import at.gv.egiz.eaaf.core.impl.utils.DOMUtils;  import at.gv.egiz.moazs.MoaZSException;  import org.apache.cxf.binding.soap.Soap11; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory;  import org.springframework.stereotype.Component;  import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList;  import org.xml.sax.SAXException;  import javax.xml.parsers.ParserConfigurationException; @@ -12,34 +16,72 @@ import javax.xml.transform.TransformerException;  import java.io.ByteArrayInputStream;  import java.io.IOException;  import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +import static at.gv.egiz.moazs.MoaZSException.moaZSException;  @Component  public class SOAPUtils { +    private static final Logger log = LoggerFactory.getLogger(SOAPUtils.class); +    private static final String SOAP_BODY_MISSING_ERROR_MSG = "<soap11:Body> is missing."; +    private static final String SOAP_BODY_CHILDREN_MISSING_ERROR_MSG = "<soap11:Body> has no child elements."; +    private static final String APP_DELIVERY_ID_MISSING_ERROR_MSG = "<msg:AppDeliveryID> is missing."; +    private static final String APP_DELIVERY_ID_EMPTY_ERROR_MSG = "<msg:AppDeliveryID> is empty."; +    private static final String MULTIPLE_MSG = "Found multiple {} elements. Will choose first element."; +    private static final String APP_DELIVERY_ID_FULL_TAG_NAME = "<msg:" + NameSpace.MSG_APP_DELIVERY_ID + ">"; +      public Element toDOM(byte[] bytes) throws IOException, SAXException, ParserConfigurationException {          var stream = new ByteArrayInputStream(bytes);          return DOMUtils.parseXmlNonValidating(stream);      } -    public byte[] unwrapSoapEnvelope(Element document)  { -        try { -            var body = document.getElementsByTagNameNS(Soap11.SOAP_NAMESPACE, "Body"); -            var item = body.item(0).getFirstChild(); +    public Node getChildElementOfSoapBody(Element document) throws MoaZSException { + +        var bodyList = document.getElementsByTagNameNS(Soap11.SOAP_NAMESPACE, "Body"); +        if (bodyList.getLength() == 0) throw moaZSException(SOAP_BODY_MISSING_ERROR_MSG); +        if (bodyList.getLength() > 1)  log.warn(MULTIPLE_MSG, "<soap11:Body>"); + +        var body = bodyList.item(0); +        var children = body.getChildNodes(); +        var candidates = filterNodeByType(children, Node.ELEMENT_NODE); + +        if (candidates.isEmpty())  throw moaZSException(SOAP_BODY_CHILDREN_MISSING_ERROR_MSG); +        if (candidates.size() > 1) log.warn(MULTIPLE_MSG, "<soap11:Body> child"); -            return DOMUtils.serializeNode(item, true) -                    .getBytes(StandardCharsets.UTF_8); +        return candidates.get(0); +    } -        } catch (IOException | TransformerException e) { -            throw MoaZSException.moaZSException("Error while parsing message. ", e); +    private List<Node> filterNodeByType(NodeList children, short nodeType) { +        var candidates = new LinkedList<Node>(); +        for (int i = 0; i < children.getLength(); i++) { +            var child = children.item(i); +            if (child.getNodeType() == nodeType) { +                candidates.add(child); +            }          } + +        return candidates; +    } + +    public byte[] toBytes(Node node) throws TransformerException, IOException { +        return DOMUtils.serializeNode(node, true).getBytes(StandardCharsets.UTF_8);      }      public String getAppDeliveryIDFrom(Element document) { -        var elements = document.getElementsByTagNameNS(NameSpace.MSG, NameSpace.MSG_APP_DELIVERY_ID); +        var elementList = document.getElementsByTagNameNS(NameSpace.MSG, NameSpace.MSG_APP_DELIVERY_ID); + +        if (elementList.getLength() == 0) throw moaZSException(APP_DELIVERY_ID_MISSING_ERROR_MSG); +        if (elementList.getLength() > 1)  log.warn(MULTIPLE_MSG, APP_DELIVERY_ID_FULL_TAG_NAME); -        var appDeliveryIdElement = elements.item(0).getFirstChild(); +        var children = elementList.item(0).getChildNodes(); +        var candidates = filterNodeByType(children, Node.TEXT_NODE); +        if (candidates.isEmpty() || candidates.get(0).getNodeValue().isBlank()) +            throw moaZSException(APP_DELIVERY_ID_EMPTY_ERROR_MSG); +        if (candidates.size() > 1) log.warn(MULTIPLE_MSG, APP_DELIVERY_ID_FULL_TAG_NAME); -        return appDeliveryIdElement.getNodeValue(); +        return candidates.get(0).getNodeValue();      }  } diff --git a/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java b/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java index 24321e1..9d31596 100644 --- a/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java +++ b/src/main/java/at/gv/egiz/moazs/util/EndpointFactory.java @@ -20,13 +20,13 @@ public class EndpointFactory {          this.bus = bus;      } -    public Endpoint create(Object implementor, Service service) { -        return create(implementor, service, null); +    public Endpoint create(Object implementor, Service service, String route) { +        return create(implementor, service, route, null);      } -    public Endpoint create(Object implementor, Service service, Interceptor<Message> interceptor) { +    public Endpoint create(Object implementor, Service service, String route, Interceptor<Message> interceptor) {          EndpointImpl endpoint = new EndpointImpl(bus, implementor); -        endpoint.setAddress("/"); +        endpoint.setAddress(route);          endpoint.setServiceName(service.getServiceName());          endpoint.setWsdlLocation(service.getWSDLDocumentLocation().toString());          endpoint.publish(); diff --git a/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java b/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java index 88ab7e0..d4aa75a 100644 --- a/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java +++ b/src/main/java/at/gv/egiz/moazs/util/StoreSOAPBodyBinaryInRepositoryInterceptor.java @@ -13,6 +13,7 @@ import org.w3c.dom.Element;  import org.xml.sax.SAXException;  import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException;  import java.io.IOException;  import java.nio.charset.StandardCharsets;  import java.util.Map; @@ -46,25 +47,22 @@ public class StoreSOAPBodyBinaryInRepositoryInterceptor extends AbstractPhaseInt          try {              byte[] content = messageUtils.copyContent(message); -            if(log.isTraceEnabled()) { -                log.trace("Interceptor received this SOAP message: {}. ", new String(content, StandardCharsets.UTF_8)); -            } -              if (content.length <= 0) {                  return;              }              Element document = soapUtils.toDOM(content); -            byte[] response = soapUtils.unwrapSoapEnvelope(document); -            String appDeliveryID = soapUtils.getAppDeliveryIDFrom(document); -            String rootTag = document.getTagName(); +            var rootTagNode = soapUtils.getChildElementOfSoapBody(document); +            var rootTagLocalName = rootTagNode.getLocalName(); -            if (!idGenerators.containsKey(rootTag)) { -                log.trace("Will not store message of type {}. ", rootTag); +            if (!idGenerators.containsKey(rootTagLocalName)) { +                log.trace("Child element {} of <soap:Body> is unknown. Will not store message.", rootTagLocalName);                  return;              } -            var id = idGenerators.get(rootTag).apply(appDeliveryID); +            String appDeliveryID = soapUtils.getAppDeliveryIDFrom(document); +            var id = idGenerators.get(rootTagLocalName).apply(appDeliveryID); +            byte[] response = soapUtils.toBytes(rootTagNode);              repository.store(id, response);              if(log.isTraceEnabled()) { @@ -72,7 +70,7 @@ public class StoreSOAPBodyBinaryInRepositoryInterceptor extends AbstractPhaseInt                          appDeliveryID, new String(response, StandardCharsets.UTF_8));              } -        } catch (ParserConfigurationException | SAXException | IOException | NullPointerException e) { +        } catch (ParserConfigurationException | SAXException | IOException | NullPointerException | TransformerException e) {              throw moaZSException("Could not extract signed data from message.", e);          }      } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a7b7524..ce4d892 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -51,7 +51,9 @@ delivery-request-configuration-profiles:      # addressable. Requires setting up the tvnz-client.      perform-query-person-request: false -    # Optional (Mandatory if perform-query-person-request is true) +    # TODO: Move tnvz-client into perform-query-person-request to make relationship clearer (also +    # TODO: add active) +    # Mandatory (if perform-query-person-request is true)      # Parameters for the connection to tvnz. Specify url,      # connection-timeout, receive-timeout and ssl here. See msg-client      # for an exhaustive description of all parameters. @@ -166,12 +168,12 @@ moa.spss:      # Select, which trust-profile moa spss uses to verify a signature.      default-trustprofile: test-trustprofile -# Optional -# Redis Setup (Cluster Mode)  spring: +  # Redis Setup (Cluster Mode)    redis:      host: 172.17.0.2      port: 6379 +  main.allow-bean-definition-overriding: true  # Mandatory  repository: diff --git a/src/main/resources/mzs/app2mzs.wsdl b/src/main/resources/mzs/app2mzs.wsdl index b3f0f4b..8c0547b 100644 --- a/src/main/resources/mzs/app2mzs.wsdl +++ b/src/main/resources/mzs/app2mzs.wsdl @@ -45,7 +45,7 @@      </binding>      <service name="app2mzs">          <port binding="tns:app2mzsBinding" name="app2mzsPort"> -            <soap:address location="http://localhost:8080/moazs/services/DeliveryRequest"/> +            <soap:address location="http://localhost:8080/moazs/services/mzs/DeliveryRequest"/>          </port>      </service>  </definitions> diff --git a/src/main/resources/mzs/app2mzs.xsd b/src/main/resources/mzs/app2mzs.xsd index 7e70092..d19ff3b 100644 --- a/src/main/resources/mzs/app2mzs.xsd +++ b/src/main/resources/mzs/app2mzs.xsd @@ -56,7 +56,6 @@  					</xs:sequence>  				</xs:complexType>  			</xs:element> -			<xs:element ref="TnvzMetaData" minOccurs="0"/>  			<xs:element ref="msg:MetaData" />  			<xs:element name="Payload" maxOccurs="unbounded">  				<xs:complexType> @@ -80,21 +79,10 @@  			<xs:element ref="Config" minOccurs="0"/>  		</xs:sequence>  	</xs:complexType> -	<xs:element name="TnvzMetaData" type="TnvzMetaDataType" /> -	<xs:complexType name="TnvzMetaDataType"> -		<xs:sequence> -			<xs:element ref="msg:Origin" /> -			<xs:choice> -				<xs:element ref="msg:DeliveryQuality"/> -				<xs:element ref="msg:PrivateMessageQuality"/> -			</xs:choice> -			<xs:element ref="msg:IgnorePostRedirectionOrder" minOccurs="0"/> -		</xs:sequence> -	</xs:complexType>  	<xs:element name="Config" type="ConfigType"/>  	<xs:complexType name="ConfigType">  		<xs:sequence> -			<xs:element name="ProfileID" type="xs:token" minOccurs="0"></xs:element> +			<xs:element name="ProfileID" type="xs:token" minOccurs="0" />  			<xs:element name="ServiceTimeout" type="xs:nonNegativeInteger" minOccurs="0"/>  			<xs:element name="PerformQueryPersonRequest" type="xs:boolean" minOccurs="0" />  			<xs:element ref="MSGClient" minOccurs="0" /> diff --git a/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java b/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java index f408c3b..c056b2c 100644 --- a/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java +++ b/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java @@ -5,10 +5,12 @@ import at.gv.egiz.moazs.preprocess.*;  import org.junit.Test;  import org.junit.runner.RunWith;  import org.mockito.Mock; +import org.mockito.Mockito;  import org.mockito.junit.MockitoJUnitRunner;  import java.util.Map; +import static at.gv.egiz.moazs.MoaZSException.moaZSException;  import static at.gv.egiz.moazs.preprocess.ConfigProfileGenerator.configProfileGeneratorBuilder;  import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder;  import static org.mockito.ArgumentMatchers.any; @@ -91,7 +93,7 @@ public class ConfigProfileGeneratorTest {      public void cancelAtIncompleteDefaultProfile() {          var propMap = Map.of(PREFIX + "." + DEFAULT + ".property-a", "value-a");          var generator = setupMocksAndBuilder(propMap).build(); -        when(validator.isConfigProfileComplete(any())).thenReturn(false); +        doThrow(moaZSException("Not Complete.")).when(validator).isConfigProfileComplete(any());          generator.generate();      } @@ -100,7 +102,7 @@ public class ConfigProfileGeneratorTest {      public void continueAtIncompleteDefaultWhenVerificationDisabled() {          var propMap = Map.of(PREFIX + "." + DEFAULT + ".property-a", "value-a"); -        when(validator.isConfigProfileComplete(any())).thenReturn(false); +        doThrow(moaZSException("Not Complete.")).when(validator).isConfigProfileComplete(any());          var generator = setupMocksAndBuilder(propMap)                  .withVerifyCompletenessOfDefaultConfiguration(false) @@ -117,7 +119,6 @@ public class ConfigProfileGeneratorTest {          when(properties.getProperty(any())).thenAnswer(i -> propMap.get(i.getArgument(0)));          when(util.merge(any(), any())).thenAnswer(i -> i.getArgument(0));          when(util.convert(any())).thenReturn(configTypeBuilder().build()); -        when(validator.isConfigProfileComplete(any())).thenReturn(true);          return configProfileGeneratorBuilder()                  .withProperties(properties) diff --git a/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java b/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java index 12f1dea..2e9dd80 100644 --- a/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java +++ b/src/test/java/at/gv/egiz/moazs/DeliveryRequestAugmenterTest.java @@ -3,6 +3,7 @@ package at.gv.egiz.moazs;  import at.gv.egiz.moazs.preprocess.MzsDeliveryRequestValidator;  import at.gv.egiz.moazs.preprocess.ConfigUtil;  import at.gv.egiz.moazs.preprocess.DeliveryRequestAugmenter; +import at.gv.egiz.moazs.scheme.Marshaller;  import at.gv.zustellung.app2mzs.xsd.ConfigType;  import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;  import org.junit.Before; @@ -31,10 +32,10 @@ public class DeliveryRequestAugmenterTest {      @Mock      private MzsDeliveryRequestValidator validator; +    private Marshaller mzsMarshaller = new Marshaller(true); +      @Before      public void setupMock() { -        when(validator.isConfigProfileComplete(any())).thenReturn(true); -        when(validator.isTnvzComplete(any())).thenReturn(true);          when(configUtil.merge(any(), any())).thenCallRealMethod();      } @@ -109,11 +110,11 @@ public class DeliveryRequestAugmenterTest {      }      private DeliveryRequestAugmenter createAugmenter(ConfigType fallback) { -        return new DeliveryRequestAugmenter(Map.of("default", fallback), configUtil, validator); +        return new DeliveryRequestAugmenter(Map.of("default", fallback), configUtil, validator, mzsMarshaller);      }      private DeliveryRequestAugmenter createAugmenter(Map<String, ConfigType> profiles) { -        return new DeliveryRequestAugmenter(profiles, configUtil, validator); +        return new DeliveryRequestAugmenter(profiles, configUtil, validator, mzsMarshaller);      }      private ConfigType createConfig(String url, Boolean performTnvz) { diff --git a/src/test/java/at/gv/egiz/moazs/ITEndToEndTest.java b/src/test/java/at/gv/egiz/moazs/ITEndToEndTest.java new file mode 100644 index 0000000..fd2e629 --- /dev/null +++ b/src/test/java/at/gv/egiz/moazs/ITEndToEndTest.java @@ -0,0 +1,259 @@ +package at.gv.egiz.moazs; + +import at.gv.egiz.moazs.client.ClientFactory; +import at.gv.egiz.moazs.repository.DeliveryRepository; +import at.gv.egiz.moazs.scheme.NotificationResponse; +import at.gv.egiz.moazs.scheme.RequestStatusResponse; +import at.gv.zustellung.app2mzs.xsd.DeliveryNotificationACKType; +import at.gv.zustellung.app2mzs.xsd.DeliveryResponseType; +import at.gv.zustellung.app2mzs.xsd.Mzs2AppPortType; +import at.gv.zustellung.msg.xsd.App2ZusePort; +import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType; +import at.gv.zustellung.msg.xsd.DeliveryRequestType; +import at.gv.zustellung.tnvz.xsd.TNVZServicePort; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + +import static at.gv.zustellung.app2mzs.xsd.DeliveryNotificationACKType.deliveryNotificationACKTypeBuilder; +import static at.gv.zustellung.app2mzs.xsd.persondata.IdentificationType.Value.valueBuilder; +import static at.gv.zustellung.app2mzs.xsd.persondata.IdentificationType.identificationTypeBuilder; +import static at.gv.zustellung.msg.xsd.DeliveryAnswerType.deliveryAnswerTypeBuilder; +import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder; +import static java.net.http.HttpResponse.BodyHandlers.ofString; +import static org.apache.commons.io.FileUtils.readFileToString; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class ITEndToEndTest { + +    private final static Logger log = LoggerFactory.getLogger(ITEndToEndTest.class); +    public static final String GZ_WATERMARK = "my-secret-watermark-string"; +    public static final String DELIVERY_SYSTEM = "http://localhost/url/to/msg/service"; + +    @LocalServerPort +    public int port; +    private String mzsFrontendURL; + +    private final String basePath = "src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/"; +    private static Mzs2AppPortType APP; + +    @Before +    public void setupFrontendURL() { +        this.mzsFrontendURL = "http://localhost:" + port + "/services"; +    } + +    @TestConfiguration +    public static class MockClientsAndSignatureVerificationConfig { + + +        @Autowired +        private DeliveryRepository repository; + +        @Bean +        @Primary +        public Consumer<byte[]> signatureVerifier() { +            return bytes -> {}; +        } + +        @Bean +        @Primary +        public ClientFactory mockClientFactory() { +            var factory = mock(ClientFactory.class); +            var tnvz = mockTnvz(); +            var msg = mockMsg(); +            APP = mockApp(); +            when(factory.create(any(), same(TNVZServicePort.class))).thenReturn(tnvz); +            when(factory.create(any(), same(App2ZusePort.class))).thenReturn(msg); +            when(factory.create(any(), same(Mzs2AppPortType.class))).thenReturn(APP); +            return factory; +        } + +        private Mzs2AppPortType mockApp() { +            var app = mock(Mzs2AppPortType.class); +            when(app.forwardStatus(any())).thenAnswer(i -> ack(i.getArgument(0))); +            when(app.forwardNotification(any())).thenAnswer(i -> ack(i.getArgument(0))); +            return app; +        } + +        private DeliveryNotificationACKType ack(DeliveryResponseType response) { +            return deliveryNotificationACKTypeBuilder() +                    .withAppDeliveryID(response.getSuccess().getAppDeliveryID()) +                    .build(); +        } + +        private TNVZServicePort mockTnvz() { +            var tnvz = mock(TNVZServicePort.class); +            var value = valueBuilder().withId("id").withValue("value").build(); +            var receiverId = identificationTypeBuilder() +                    .withValue(value).withId("id").withType("type").build(); +            var tnvzHelperTest = new TnvzHelperTest(); +            tnvzHelperTest.setup(); +            var tnvzSuccess = tnvzHelperTest.tnvzSuccess(List.of("*/*"), receiverId); + +            when(tnvz.queryPerson(any())).thenReturn(tnvzSuccess); +            return tnvz; +        } + +        private App2ZusePort mockMsg() { +            var msg = mock(App2ZusePort.class); +            when(msg.delivery(any())).thenAnswer(i-> partialSuccess(i.getArgument(0))); +            return msg; +        } + +        private DeliveryRequestStatusType partialSuccess(DeliveryRequestType request) { + +            var appDeliveryID = request.getMetaData().getAppDeliveryID(); +            var zsDeliveryID = "ZSDID-" + appDeliveryID; +            var responseID = RequestStatusResponse.getResponseID(appDeliveryID); +            var answer = deliveryAnswerTypeBuilder() +                    .withDeliverySystem(DELIVERY_SYSTEM) +                    .withAppDeliveryID(appDeliveryID) +                    .withZSDeliveryID(zsDeliveryID) +                    .withGZ(GZ_WATERMARK) +                    .build(); +            repository.store(responseID, new byte[]{}); +            return deliveryRequestStatusTypeBuilder() +                    .withPartialSuccess(answer) +                    .build(); +        } + +    } + +    @Test +    public void testHappyPath() throws IOException, InterruptedException { + +        //prepare +        var appDeliveryID = "delivery-request-id"; +        var zsDeliveryID = "ZSDID-" + appDeliveryID; +        var timestamp = ITMzsServiceTest.genTimeStamp().toXMLFormat(); +        var saveSinkFolder = "target/tmp/ITEndToEndTestOut"; +        delete(saveSinkFolder); + +        //app sends delivery request to moazs and receives partial success +        var partialSuccess = sendMzsDeliveryRequest("mzs-delivery-request.xml"); +        assertThat(partialSuccess.statusCode()).isEqualTo(200); +        assertThat(partialSuccess.body()).contains(List.of(GZ_WATERMARK, "PartialSuccess")); + +        // zusemsg sends async success +        var statusResponseID = RequestStatusResponse.getResponseID(appDeliveryID); +        var msgStatus = formatFile("msg-delivery-request-status.xml", new String[]{ +                DELIVERY_SYSTEM, zsDeliveryID, appDeliveryID, GZ_WATERMARK, timestamp +        }); +        sendMsgResponse(msgStatus); +        Thread.sleep(100); +        verify(APP).forwardStatus(any()); +        assertStatusWrittenToFileSystem(saveSinkFolder, statusResponseID); +        assertStatusWasLogged(); + +        // zusemsg sends async notification +        var notificationResponseID = NotificationResponse.getResponseID(appDeliveryID); +        var notification = formatFile("msg-delivery-notification.xml", new String[]{ +                DELIVERY_SYSTEM, zsDeliveryID, appDeliveryID, GZ_WATERMARK, timestamp, timestamp +        }); +        sendMsgResponse(notification); +        Thread.sleep(100); +        verify(APP).forwardNotification(any()); +        assertStatusWrittenToFileSystem(saveSinkFolder, notificationResponseID); +        assertStatusWasLogged(); + +    } + +    private void assertStatusWrittenToFileSystem(String folder, String pathSubString) { +        var rootFolder = new File(folder); +        Collection<File> files = FileUtils.listFiles(rootFolder, null, true); + +        assertThat(rootFolder.exists()).isTrue(); +        assertThat(rootFolder.isDirectory()).isTrue(); +        assertThat(files).isNotEmpty(); + +        long count = files.stream() +                .map(File::getAbsolutePath) +                .filter(path -> path.contains(pathSubString)) +                .count(); + +        assertThat(count).isEqualTo(2); +    } + +    private String readFile(File file) { +        try { +            return readFileToString(file, StandardCharsets.UTF_8); +        } catch (IOException e) { +            throw new RuntimeException(e); +        } +    } + +    private void delete(String file) { +        try { +            FileUtils.deleteDirectory(new File(file)); +        } catch (IOException e) { +            log.warn("Could not delete {}", file); +        } +    } + +    private void assertStatusWasLogged() { +        //todo +    } + + +    private String formatFile(String templateFile, String... values) throws IOException { +        var path = basePath + templateFile; +        var templateString = FileUtils.readFileToString(new File(path), StandardCharsets.UTF_8); +        return String.format(templateString, values); +    } + +    private HttpResponse<String> sendMsgResponse(String bodyString) throws IOException, InterruptedException { + +        var body = HttpRequest.BodyPublishers.ofString(bodyString); +        var client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); +        var request = HttpRequest.newBuilder() +                .uri(URI.create(mzsFrontendURL + "/msg/")) +                .header("Content-Type", "text/xml;charset=UTF-8") +                .header("SOAPAction", "\"\"") +                .POST(body) +                .build(); + +        return client.send(request, ofString()); + +    } + +    private HttpResponse<String> sendMzsDeliveryRequest(String fileName) throws IOException, InterruptedException { + +        var path = basePath + fileName; +        var client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); +        var request = HttpRequest.newBuilder() +                .uri(URI.create(mzsFrontendURL + "/mzs/")) +                .header("Content-Type", "text/xml;charset=UTF-8") +                .header("SOAPAction", "\"\"") +                .POST(HttpRequest.BodyPublishers.ofFile(Paths.get(path))) +                .build(); + +        return client.send(request, ofString()); + +    } + +} diff --git a/src/test/java/at/gv/egiz/moazs/ITMzsServiceTest.java b/src/test/java/at/gv/egiz/moazs/ITMzsServiceTest.java index e9b4f70..3cf0362 100644 --- a/src/test/java/at/gv/egiz/moazs/ITMzsServiceTest.java +++ b/src/test/java/at/gv/egiz/moazs/ITMzsServiceTest.java @@ -2,12 +2,14 @@ package at.gv.egiz.moazs;  import at.gv.egiz.moazs.repository.DeliveryRepository;  import at.gv.egiz.moazs.scheme.RequestStatusResponse; +import org.junit.Before;  import org.junit.Test;  import org.junit.runner.RunWith;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.boot.test.context.SpringBootTest;  import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;  import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.web.server.LocalServerPort;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Primary;  import org.springframework.test.context.junit4.SpringRunner; @@ -27,17 +29,24 @@ import java.util.function.Consumer;  import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.Success.successBuilder;  import static at.gv.zustellung.msg.xsd.DeliveryRequestStatusType.deliveryRequestStatusTypeBuilder;  import static java.net.http.HttpClient.Version; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat;  @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)  public class ITMzsServiceTest { -    private final String serviceUri = "http://localhost:8080/services/DeliveryRequest"; -      private final String basePath = "src/test/resources/at/gv/egiz/moazs/ITMzsServiceTest/"; +    @LocalServerPort +    public int port; +    private String serviceUri; + +    @Before +    public void setupServiceURI() { +        this.serviceUri = "http://localhost:" + port + "/services/mzs"; +    } +      @TestConfiguration      public static class TestConfig { @@ -64,50 +73,44 @@ public class ITMzsServiceTest {              };          } -        private XMLGregorianCalendar genTimeStamp() { -            try { -                return DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); -            } catch (DatatypeConfigurationException e) { -                throw new RuntimeException("ups"); -            } -        }      }      @Test      public void acceptValidDeliveryRequest() throws IOException, InterruptedException {          var response = sendDeliveryRequestFile("validDeliveryRequest.soap"); -        assertEquals(200, response.statusCode()); +        assertThat(response.statusCode()).isEqualTo(200); +        assertThat(response.body()).contains("valid-delivery-request-id");      }      @Test      public void rejectRequestWithoutSender() throws IOException, InterruptedException {          var response = sendDeliveryRequestFile("missingSender.soap"); -        assertEquals(500, response.statusCode()); +        assertThat(response.statusCode()).isEqualTo(500);      }      @Test      public void rejectBothProfileAndCorporateBody() throws IOException, InterruptedException {          var response = sendDeliveryRequestFile("profileAndCorporateBody.soap"); -        assertEquals(500, response.statusCode()); +        assertThat(response.statusCode()).isEqualTo(500);      }      @Test      public void rejectFormallyIncorrectDeliveryRequest() throws IOException, InterruptedException {          var response = sendDeliveryRequestFile("formallyIncorrectDeliveryRequest.soap"); -        assertEquals(500, response.statusCode()); +        assertThat(response.statusCode()).isEqualTo(500);      }      @Test      public void rejectRequestWithoutAppDeliveryID() throws IOException, InterruptedException {          var response = sendDeliveryRequestFile("missingAppDeliveryId.soap"); -        assertEquals(500, response.statusCode()); +        assertThat(response.statusCode()).isEqualTo(500);      }      @Test      public void rejectRequestWithoutMetaData() throws IOException, InterruptedException {          var response = sendDeliveryRequestFile("missingMetaData.soap"); -        assertEquals(500, response.statusCode()); +        assertThat(response.statusCode()).isEqualTo(500);      }      private HttpResponse<String> sendDeliveryRequestFile(String fileName) throws IOException, InterruptedException { @@ -125,4 +128,13 @@ public class ITMzsServiceTest {      } +    public static XMLGregorianCalendar genTimeStamp() { +        try { +            return DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); +        } catch (DatatypeConfigurationException e) { +            throw new RuntimeException("ups"); +        } + +    } +  } diff --git a/src/test/java/at/gv/egiz/moazs/SOAPUtilsTest.java b/src/test/java/at/gv/egiz/moazs/SOAPUtilsTest.java index 31aa197..ff0060e 100644 --- a/src/test/java/at/gv/egiz/moazs/SOAPUtilsTest.java +++ b/src/test/java/at/gv/egiz/moazs/SOAPUtilsTest.java @@ -4,10 +4,8 @@ import at.gv.egiz.moazs.scheme.SOAPUtils;  import org.junit.Before;  import org.junit.Test;  import org.w3c.dom.Element; -import org.xml.sax.SAXException; +import org.w3c.dom.Node; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException;  import java.nio.charset.StandardCharsets;  import static org.assertj.core.api.Assertions.assertThat; @@ -25,39 +23,152 @@ public class SOAPUtilsTest {              "est-id</ZSDeliveryID><AppDeliveryID>valid-delivery-request-id</AppDeliveryID><GZ>12345</GZ>" +              "</PartialSuccess></DeliveryResponse></soap:Body></soap:Envelope>"; + +    public static final String FORMATTED_MESSAGE = +            "<soapenv:Envelope\n" + +            "        xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" + +            "        xmlns:msg=\"http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#\">\n" + +            "    <soapenv:Body>\n" + +            "        <msg:DeliveryNotification>\n" + +            "            <msg:AppDeliveryID>formatted-adid</msg:AppDeliveryID>\n" + +            "        </msg:DeliveryNotification>\n" + +            "    </soapenv:Body>\n" + +            "</soapenv:Envelope>\n"; + +    public static final String CLUTTERED_MESSAGE = +            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + +            "<soapenv:Envelope " + +            "  xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n " + +            "  xmlns:msg=\"http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#\">\n" + +            "    <soapenv:Body unexpected-attribute=\"unexpectedvalue\">  \n" + +            "        <msg:DeliveryNotification>\n" + +            "            <msg:AppDeliveryID attribute=\"some-value\"><element-that-got-lost />cluttered-adid</msg:AppDeliveryID>\n" + +            "        </msg:DeliveryNotification>\n" + +            "    </soapenv:Body>\n" + +            "</soapenv:Envelope>\n"; + +    private static final String MISSING_BODY_MESSAGE = +            "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><NotABodyTag></NotABodyTag></soap:Envelope>"; + +    private static final String EMPTY_BODY_MESSAGE = +            "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body>       </soap:Body></soap:Envelope>"; + +    public static final String EMPTY_APP_DELIVERY_ID_MESSAGE = +            "<soapenv:Envelope\n" + +                    "        xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" + +                    "        xmlns:msg=\"http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#\">\n" + +                    "    <soapenv:Body>\n" + +                    "        <msg:DeliveryNotification>\n" + +                    "            <msg:AppDeliveryID />\n" + +                    "        </msg:DeliveryNotification>\n" + +                    "    </soapenv:Body>\n" + +                    "</soapenv:Envelope>\n"; + +    public static final String BLANK_APP_DELIVERY_ID_MESSAGE = +            "<soapenv:Envelope\n" + +                    "        xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" + +                    "        xmlns:msg=\"http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#\">\n" + +                    "    <soapenv:Body>\n" + +                    "        <msg:DeliveryNotification>\n" + +                    "            <msg:AppDeliveryID>   </msg:AppDeliveryID>\n" + +                    "        </msg:DeliveryNotification>\n" + +                    "    </soapenv:Body>\n" + +                    "</soapenv:Envelope>\n"; +      @Before      public void setup() {          utils = new SOAPUtils();      }      @Test -    public void toDom() throws ParserConfigurationException, SAXException, IOException { -        byte[] bytes = SOAP_MESSAGE.getBytes(StandardCharsets.UTF_8); +    public void canParseSoapMessage() throws Exception { +        parseAndCheckLocalName(SOAP_MESSAGE); +    } + +    @Test +    public void canParseFormattedSoapMessage() throws Exception { +        parseAndCheckLocalName(FORMATTED_MESSAGE); +    } + +    @Test +    public void canParseClutteredSoapMessage() throws Exception  { +        parseAndCheckLocalName(CLUTTERED_MESSAGE); +    } +    private void parseAndCheckLocalName(String text) throws Exception { +        byte[] bytes = text.getBytes(StandardCharsets.UTF_8);          Element root = utils.toDOM(bytes); +        assertThat(root.getLocalName()).isEqualTo("Envelope"); +    } + +    @Test +    public void canUnwrapSoapMessage() throws Exception { +        getFirstChildAndCheckLocalName(SOAP_MESSAGE, "DeliveryResponse"); +    } -        assertThat(root.getTagName()).isEqualTo("soap:Envelope"); +    @Test +    public void canUnwrapFormattedSoapMessage() throws Exception { +        getFirstChildAndCheckLocalName(FORMATTED_MESSAGE, "DeliveryNotification");      }      @Test -    public void unwrapSoapEnvelope() throws ParserConfigurationException, SAXException, IOException { -        byte[] bytes = SOAP_MESSAGE.getBytes(StandardCharsets.UTF_8); +    public void canUnwrapClutteredSoapMessage() throws Exception { +        getFirstChildAndCheckLocalName(CLUTTERED_MESSAGE, "DeliveryNotification"); +    } + +    @Test(expected=MoaZSException.class) +    public void failUnwrapMissingBody() throws Exception { +        getFirstChildAndCheckLocalName(MISSING_BODY_MESSAGE, ""); +    } + +    @Test(expected=MoaZSException.class) +    public void failUnwrapEmptyBody() throws Exception { +        getFirstChildAndCheckLocalName(EMPTY_BODY_MESSAGE, ""); +    } + +    private void getFirstChildAndCheckLocalName(String message, String expectedLocalNameOfChild) throws Exception { +        byte[] bytes = message.getBytes(StandardCharsets.UTF_8);          Element soapRoot = utils.toDOM(bytes); +        Node child = utils.getChildElementOfSoapBody(soapRoot); +        var actualLocalName = child.getLocalName(); +        assertThat(actualLocalName).isEqualTo(expectedLocalNameOfChild); +    } -        byte[] unwrappedMessage = utils.unwrapSoapEnvelope(soapRoot); +    @Test +    public void getAppDeliveryIDFromSoapMessage() throws Exception { +        getAppDeliveryIDAndCheck(SOAP_MESSAGE, "valid-delivery-request-id"); +    } -        Element deliveryResponseRoot = utils.toDOM(unwrappedMessage); -        assertThat(deliveryResponseRoot.getTagName()).isEqualTo("DeliveryResponse"); +    @Test +    public void getAppDeliveryIDFromFormattedMessage() throws Exception { +        getAppDeliveryIDAndCheck(FORMATTED_MESSAGE, "formatted-adid");      }      @Test -    public void getAppDeliveryID() throws ParserConfigurationException, SAXException, IOException { -        byte[] bytes = SOAP_MESSAGE.getBytes(StandardCharsets.UTF_8); -        Element soapRoot = utils.toDOM(bytes); +    public void getAppDeliveryIDFromClutteredMessage() throws Exception { +        getAppDeliveryIDAndCheck(CLUTTERED_MESSAGE, "cluttered-adid"); +    } + +    @Test(expected = MoaZSException.class) +    public void failToRetrieveAppDeliveryIDWhenBodyIsMissing() throws Exception { +        getAppDeliveryIDAndCheck(MISSING_BODY_MESSAGE, ""); +    } -        String appDeliveryID = utils.getAppDeliveryIDFrom(soapRoot); +    @Test(expected = MoaZSException.class) +    public void failToRetrieveEmptyAppDeliveryID() throws Exception { +        getAppDeliveryIDAndCheck(EMPTY_APP_DELIVERY_ID_MESSAGE, ""); +    } + +    @Test(expected = MoaZSException.class) +    public void failToRetrieveBlankAppDeliveryID() throws Exception { +        getAppDeliveryIDAndCheck(BLANK_APP_DELIVERY_ID_MESSAGE, ""); +    } -        assertThat(appDeliveryID).isEqualTo("valid-delivery-request-id"); +    private void getAppDeliveryIDAndCheck(String message, String expectedValue) throws Exception { +        byte[] bytes = message.getBytes(StandardCharsets.UTF_8); +        Element soapRoot = utils.toDOM(bytes); +        String actualAppDeliveryID = utils.getAppDeliveryIDFrom(soapRoot); +        assertThat(actualAppDeliveryID).isEqualTo(expectedValue);      }  } diff --git a/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java b/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java index 020e270..550f089 100644 --- a/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java +++ b/src/test/java/at/gv/egiz/moazs/TnvzHelperTest.java @@ -5,6 +5,8 @@ import at.gv.egiz.moazs.scheme.Mzs2MsgConverter;  import at.gv.egiz.moazs.client.TnvzHelper;  import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;  import at.gv.zustellung.app2mzs.xsd.persondata.IdentificationType; +import at.gv.zustellung.msg.xsd.MetaData; +import at.gv.zustellung.msg.xsd.SystemComponentType;  import at.gv.zustellung.tnvz.xsd.MimeTypeList;  import at.gv.zustellung.tnvz.xsd.QueryPersonResponse;  import at.gv.zustellung.tnvz.xsd.TNVZServicePort; @@ -20,11 +22,12 @@ import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.Payload.payloadBu  import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.Receiver.receiverBuilder;  import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.Sender.senderBuilder;  import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.deliveryRequestTypeBuilder; -import static at.gv.zustellung.app2mzs.xsd.TnvzMetaDataType.tnvzMetaDataTypeBuilder;  import static at.gv.zustellung.app2mzs.xsd.persondata.CorporateBodyType.corporateBodyTypeBuilder;  import static at.gv.zustellung.app2mzs.xsd.persondata.IdentificationType.Value.valueBuilder;  import static at.gv.zustellung.app2mzs.xsd.persondata.IdentificationType.identificationTypeBuilder;  import static at.gv.zustellung.msg.xsd.ErrorInfoType.errorInfoTypeBuilder; +import static at.gv.zustellung.msg.xsd.MetaData.metaDataBuilder; +import static at.gv.zustellung.msg.xsd.SystemComponentType.systemComponentTypeBuilder;  import static at.gv.zustellung.tnvz.xsd.MimeTypeList.mimeTypeListBuilder;  import static at.gv.zustellung.tnvz.xsd.PersonResultSuccessType.personResultSuccessTypeBuilder;  import static at.gv.zustellung.tnvz.xsd.PersonResultType.Error.errorBuilder; @@ -119,14 +122,19 @@ public class TnvzHelperTest {                  .withIdentification(receiverId)                  .build(); -        var metadata = tnvzMetaDataTypeBuilder() +        var origin = systemComponentTypeBuilder() +                .withParticipantID("ID") +                .build(); + +        var metadata = metaDataBuilder() +                .withOrigin(origin)                  .build();          return deliveryRequestTypeBuilder()                  .withSender(sender)                  .withReceiver(receiver) -                .withTnvzMetaData(metadata)                  .withPayload(payload(mzsMimeTypes)) +                .withMetaData(metadata)                  .build();      } @@ -148,7 +156,7 @@ public class TnvzHelperTest {                  .collect(toList());      } -    private QueryPersonResponse tnvzSuccess(List<String> mimeTypes, IdentificationType receiverId) { +    public QueryPersonResponse tnvzSuccess(List<String> mimeTypes, IdentificationType receiverId) {          var success = personResultSuccessTypeBuilder()                  .withMimeTypeList(setupMimeTypeList(mimeTypes)) diff --git a/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/msg-delivery-notification.xml b/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/msg-delivery-notification.xml new file mode 100644 index 0000000..3a90d36 --- /dev/null +++ b/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/msg-delivery-notification.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<soapenv:Envelope +        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" +        xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" +        xmlns:p="http://reference.e-government.gv.at/namespace/persondata/phase2/20181206#" +        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +        xmlns:msg="http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#"> +    <soapenv:Body> +        <msg:DeliveryNotification> +            <msg:DeliverySystem>%s</msg:DeliverySystem> +            <msg:ZSDeliveryID>%s</msg:ZSDeliveryID> +            <msg:AppDeliveryID>%s</msg:AppDeliveryID> +            <msg:GZ>%s</msg:GZ> +            <msg:SenderDetails> +                <p:Identification> +                    <p:Value>12345</p:Value> +                    <p:Type>678910</p:Type> +                </p:Identification> +                <p:CorporateBody> +                    <p:FullName>Bundesministerium für Testzwecke</p:FullName> +                </p:CorporateBody> +            </msg:SenderDetails> +            <msg:ReceiverDetails> +                <p:Identification> +                    <p:Value>123</p:Value> +                    <p:Type>654654</p:Type> +                </p:Identification> +                <p:PhysicalPerson> +                    <p:Name> +                        <p:GivenName>Maxi</p:GivenName> +                        <p:FamilyName>Mustermann1</p:FamilyName> +                    </p:Name> +                    <p:DateOfBirth>1984-01-24</p:DateOfBirth> +                </p:PhysicalPerson> +            </msg:ReceiverDetails> +            <msg:RelayedViaERV>false</msg:RelayedViaERV> +            <msg:Timestamp>%s</msg:Timestamp> +            <msg:Answer xsi:type="msg:AcceptedType"> +                <msg:NotificationsPerformed> +                    <msg:RecipientNotification> +                        <msg:Timestamp>%s</msg:Timestamp> +                    </msg:RecipientNotification> +                </msg:NotificationsPerformed> +            </msg:Answer> +            <dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Id="signature-1-1"> +                <dsig:SignedInfo> +                    <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> +                    <dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> +                    <dsig:Reference Id="reference-1-1" URI=""> +                        <dsig:Transforms> +                            <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> +                            <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> +                        </dsig:Transforms> +                        <dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> +                        <dsig:DigestValue>ejvUI0yh/IIyauFe8x5ZonD/i5oznl8vFyS3oLNivzA=</dsig:DigestValue> +                    </dsig:Reference> +                </dsig:SignedInfo> +                <dsig:SignatureValue> +                    hmVZrLkMDbXaRLYQKOaV3OtK13TQgMu3csKyw9M4zWqNyva1yxnYkzoX3dKDOdc9 +                    O56yQJsjoA3Cuw7pXlGO7jSfVM77dTXbWSDaF95O9Vdsrmr7R6Uki0jA9SmgQLXg +                    hZAUG8JpsHcBn8M0L2BXADKjSn0LuMDL2L7dmU3EM7eRy+OvFwDrXDw1fhjQO6L2 +                    KoflAWLgUerDhJSpzr0+YfmkrjzitLUA7oIg8ieOnfGyql31ECmDJEqgnL78hyPZ +                    KaNZImDf3EWFs8je6mt+os1TwsyXYwz+GGbjoDR8lGTS9xVqnXdrgP8Jyv6p9FEu +                    0IYgSY2FlbI3skPZC8ZVXg== +                </dsig:SignatureValue> +                <dsig:KeyInfo> +                    <dsig:X509Data> +                        <dsig:X509Certificate> +                            MIIEqzCCBBSgAwIBAgIHANux81oNezANBgkqhkiG9w0BAQUFADBAMSIwIAYDVQQD +                            ExlJQUlLIFRlc3QgSW50ZXJtZWRpYXRlIENBMQ0wCwYDVQQKEwRJQUlLMQswCQYD +                            VQQGEwJBVDAeFw0xMzA5MjcwNTMzMzdaFw0yMzA5MjcwNTMzMzdaMIHkMQswCQYD +                            VQQGEwJBVDENMAsGA1UEBxMER3JhejEmMCQGA1UEChMdR3JheiBVbml2ZXJzaXR5 +                            IG9mIFRlY2hub2xvZ3kxSDBGBgNVBAsTP0luc3RpdHV0ZSBmb3IgQXBwbGllZCBJ +                            bmZvcm1hdGlvbiBQcm9jZXNzaW5nIGFuZCBDb21tdW5pY2F0aW9uczEUMBIGA1UE +                            BBMLTU9BLVNTIFRlc3QxGDAWBgNVBCoTD0VHSVogVGVzdHBvcnRhbDEkMCIGA1UE +                            AxMbRUdJWiBUZXN0cG9ydGFsIE1PQS1TUyBUZXN0MIIBIjANBgkqhkiG9w0BAQEF +                            AAOCAQ8AMIIBCgKCAQEAuDjOyf+mY+oQL2FQzzuaiC8C23vVKbq/n2Zi7BqSibZH +                            mtqMJfmj4pT+hWSNHvVvWsaxFcx4KeNqdCMzwnw1r4P3Sf+2o5uFku5KHEMLMokR +                            yYQG9VqY/KkB94ye7Pv6zT8gvKqxGFg96UamECep4swPaSZrA8AOER5WAtyGDzKI +                            Tz+a5zfFaTXDoba7f98PCWR96yKiFjVOhzp38WVz4VJgz+b8ZSY7Xsv5Kn7DXjOL +                            STX4MevFLki3rFPup3+4vGToaMBW3PEj67HXBdqR855Le6+E6rVxORqsXqlVwhsI +                            6nuS0CO2LWYmBNR1IB0mXteeYH/HfxvuZc+7yDjdPQIDAQABo4IBhDCCAYAwDgYD +                            VR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFEmcH6VY4BG1EAGB +                            TLoNR9vH/g6yMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jYS5pYWlrLnR1Z3Jh +                            ei5hdC9jYXBzby9jcmxzL0lBSUtUZXN0X0ludGVybWVkaWF0ZUNBLmNybDCBqgYI +                            KwYBBQUHAQEEgZ0wgZowSgYIKwYBBQUHMAGGPmh0dHA6Ly9jYS5pYWlrLnR1Z3Jh +                            ei5hdC9jYXBzby9PQ1NQP2NhPUlBSUtUZXN0X0ludGVybWVkaWF0ZUNBMEwGCCsG +                            AQUFBzAChkBodHRwOi8vY2EuaWFpay50dWdyYXouYXQvY2Fwc28vY2VydHMvSUFJ +                            S1Rlc3RfSW50ZXJtZWRpYXRlQ0EuY2VyMCEGA1UdEQQaMBiBFnRob21hcy5sZW56 +                            QGVnaXouZ3YuYXQwHwYDVR0jBBgwFoAUaKJeEdreL4BrRES/jfplNoEkp28wDQYJ +                            KoZIhvcNAQEFBQADgYEAlFGjUxXLs7SAT8NtXSrv2WrjlklaRnHTFHLQwyVo8JWb +                            gvRkHHDUv2o8ofXUY2R2WJ38dxeDoccgbXrJb/Qhi8IY7YhCwv/TuIZDisyAqo8W +                            ORKSip/6HWlGCSR/Vgoet1GtCmF0FoUxFUIGSAuQ2yyt4fIzt5GJrU1X5ujjI1w= +                        </dsig:X509Certificate> +                    </dsig:X509Data> +                </dsig:KeyInfo> +            </dsig:Signature> +        </msg:DeliveryNotification> +    </soapenv:Body> +</soapenv:Envelope> diff --git a/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/msg-delivery-request-status.xml b/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/msg-delivery-request-status.xml new file mode 100644 index 0000000..bbeded8 --- /dev/null +++ b/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/msg-delivery-request-status.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<soapenv:Envelope +		xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" +        xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" +		xmlns:msg="http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#"> +	<soapenv:Body> +		<msg:DeliveryRequestStatus> +            <msg:Success> +                <msg:DeliverySystem>%s</msg:DeliverySystem> +                <msg:ZSDeliveryID>%s</msg:ZSDeliveryID> +                <msg:AppDeliveryID>%s</msg:AppDeliveryID> +                <msg:GZ>%s</msg:GZ> +                <msg:DeliveryTimestamp>%s</msg:DeliveryTimestamp> +                <msg:RelayedViaERV>false</msg:RelayedViaERV> +            </msg:Success> +            <dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Id="signature-1-1"> +                <dsig:SignedInfo> +                    <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> +                    <dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> +                    <dsig:Reference Id="reference-1-1" URI=""> +                        <dsig:Transforms> +                            <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> +                            <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> +                        </dsig:Transforms> +                        <dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> +                        <dsig:DigestValue>ejvUI0yh/IIyauFe8x5ZonD/i5oznl8vFyS3oLNivzA=</dsig:DigestValue> +                    </dsig:Reference> +                </dsig:SignedInfo> +                <dsig:SignatureValue> +                    hmVZrLkMDbXaRLYQKOaV3OtK13TQgMu3csKyw9M4zWqNyva1yxnYkzoX3dKDOdc9 +                    O56yQJsjoA3Cuw7pXlGO7jSfVM77dTXbWSDaF95O9Vdsrmr7R6Uki0jA9SmgQLXg +                    hZAUG8JpsHcBn8M0L2BXADKjSn0LuMDL2L7dmU3EM7eRy+OvFwDrXDw1fhjQO6L2 +                    KoflAWLgUerDhJSpzr0+YfmkrjzitLUA7oIg8ieOnfGyql31ECmDJEqgnL78hyPZ +                    KaNZImDf3EWFs8je6mt+os1TwsyXYwz+GGbjoDR8lGTS9xVqnXdrgP8Jyv6p9FEu +                    0IYgSY2FlbI3skPZC8ZVXg== +                </dsig:SignatureValue> +                <dsig:KeyInfo> +                    <dsig:X509Data> +                        <dsig:X509Certificate> +                            MIIEqzCCBBSgAwIBAgIHANux81oNezANBgkqhkiG9w0BAQUFADBAMSIwIAYDVQQD +                            ExlJQUlLIFRlc3QgSW50ZXJtZWRpYXRlIENBMQ0wCwYDVQQKEwRJQUlLMQswCQYD +                            VQQGEwJBVDAeFw0xMzA5MjcwNTMzMzdaFw0yMzA5MjcwNTMzMzdaMIHkMQswCQYD +                            VQQGEwJBVDENMAsGA1UEBxMER3JhejEmMCQGA1UEChMdR3JheiBVbml2ZXJzaXR5 +                            IG9mIFRlY2hub2xvZ3kxSDBGBgNVBAsTP0luc3RpdHV0ZSBmb3IgQXBwbGllZCBJ +                            bmZvcm1hdGlvbiBQcm9jZXNzaW5nIGFuZCBDb21tdW5pY2F0aW9uczEUMBIGA1UE +                            BBMLTU9BLVNTIFRlc3QxGDAWBgNVBCoTD0VHSVogVGVzdHBvcnRhbDEkMCIGA1UE +                            AxMbRUdJWiBUZXN0cG9ydGFsIE1PQS1TUyBUZXN0MIIBIjANBgkqhkiG9w0BAQEF +                            AAOCAQ8AMIIBCgKCAQEAuDjOyf+mY+oQL2FQzzuaiC8C23vVKbq/n2Zi7BqSibZH +                            mtqMJfmj4pT+hWSNHvVvWsaxFcx4KeNqdCMzwnw1r4P3Sf+2o5uFku5KHEMLMokR +                            yYQG9VqY/KkB94ye7Pv6zT8gvKqxGFg96UamECep4swPaSZrA8AOER5WAtyGDzKI +                            Tz+a5zfFaTXDoba7f98PCWR96yKiFjVOhzp38WVz4VJgz+b8ZSY7Xsv5Kn7DXjOL +                            STX4MevFLki3rFPup3+4vGToaMBW3PEj67HXBdqR855Le6+E6rVxORqsXqlVwhsI +                            6nuS0CO2LWYmBNR1IB0mXteeYH/HfxvuZc+7yDjdPQIDAQABo4IBhDCCAYAwDgYD +                            VR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFEmcH6VY4BG1EAGB +                            TLoNR9vH/g6yMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jYS5pYWlrLnR1Z3Jh +                            ei5hdC9jYXBzby9jcmxzL0lBSUtUZXN0X0ludGVybWVkaWF0ZUNBLmNybDCBqgYI +                            KwYBBQUHAQEEgZ0wgZowSgYIKwYBBQUHMAGGPmh0dHA6Ly9jYS5pYWlrLnR1Z3Jh +                            ei5hdC9jYXBzby9PQ1NQP2NhPUlBSUtUZXN0X0ludGVybWVkaWF0ZUNBMEwGCCsG +                            AQUFBzAChkBodHRwOi8vY2EuaWFpay50dWdyYXouYXQvY2Fwc28vY2VydHMvSUFJ +                            S1Rlc3RfSW50ZXJtZWRpYXRlQ0EuY2VyMCEGA1UdEQQaMBiBFnRob21hcy5sZW56 +                            QGVnaXouZ3YuYXQwHwYDVR0jBBgwFoAUaKJeEdreL4BrRES/jfplNoEkp28wDQYJ +                            KoZIhvcNAQEFBQADgYEAlFGjUxXLs7SAT8NtXSrv2WrjlklaRnHTFHLQwyVo8JWb +                            gvRkHHDUv2o8ofXUY2R2WJ38dxeDoccgbXrJb/Qhi8IY7YhCwv/TuIZDisyAqo8W +                            ORKSip/6HWlGCSR/Vgoet1GtCmF0FoUxFUIGSAuQ2yyt4fIzt5GJrU1X5ujjI1w= +                        </dsig:X509Certificate> +                    </dsig:X509Data> +                </dsig:KeyInfo> +            </dsig:Signature> +        </msg:DeliveryRequestStatus> +	</soapenv:Body> +</soapenv:Envelope> diff --git a/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/mzs-delivery-request.xml b/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/mzs-delivery-request.xml new file mode 100644 index 0000000..ef07686 --- /dev/null +++ b/src/test/resources/at/gv/egiz/moazs/ITEndToEndTest/mzs-delivery-request.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<soapenv:Envelope +		xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" +		xmlns:mzs="http://reference.e-government.gv.at/namespace/zustellung/mzs/app2mzs#" +		xmlns:p="http://reference.e-government.gv.at/namespace/zustellung/mzs/persondata#" +		xmlns:msg="http://reference.e-government.gv.at/namespace/zustellung/msg/phase2/20181206#"> +	<soapenv:Body> +		<mzs:DeliveryRequest> +			<mzs:Sender> +                <p:CorporateBody> +                    <p:Identification> +                        <p:Value>kkvj693+tw99uW8UPuEK9en1LzZItkylPajkUUyJJDWQB78VGPkAuhCEk+TD12yQDD/WRglsf+JfQpjubIs/4l/ptluJ9teF3nwkNlu5Dm7mIjzgW1qxrDyomCmPvVxTWOCBuMUbOWRZBhOq+KvDQAu9Vv9KnqSfjYeDZrpHYu4=</p:Value> +                        <p:Type>urn:publicid:gv:at:cemtoken</p:Type> +                    </p:Identification> +                    <p:FullName>Bundesministerium für Testzwecke</p:FullName> +                </p:CorporateBody> +                <mzs:WebserviceURL>https://authority.gv.at/delivery_notification</mzs:WebserviceURL> +            </mzs:Sender> +            <mzs:Receiver> +                <p:PhysicalPerson> +                    <p:Name> +                        <p:GivenName>Maxi</p:GivenName> +                        <p:FamilyName>Mustermann1</p:FamilyName> +                    </p:Name> +                    <p:DateOfBirth>1984-01-24</p:DateOfBirth> +                </p:PhysicalPerson> +                <p:PostalAddress> +                    <p:CountryCode>AT</p:CountryCode> +                    <p:PostalCode>1010</p:PostalCode> +                    <p:Municipality>Wien</p:Municipality> +                    <p:DeliveryAddress> +                        <p:StreetName>Muststrasse</p:StreetName> +                        <p:BuildingNumber>10</p:BuildingNumber> +                    </p:DeliveryAddress> +                </p:PostalAddress> +            </mzs:Receiver> +            <msg:MetaData> +                <msg:AppDeliveryID>delivery-request-id</msg:AppDeliveryID> +                <msg:Origin> +                    <msg:ParticipantID>VKABC</msg:ParticipantID> +                </msg:Origin> +                <msg:Subject>WichtigeMitteilung</msg:Subject> +                <msg:DeliveryQuality>RSa</msg:DeliveryQuality> +            </msg:MetaData> +            <mzs:Payload> +                <mzs:DocumentReference>https://authority.gv.at/files/73bdf969781ba41fa07df1ff8439cf685c0db1c3</mzs:DocumentReference> +                <mzs:FileName>brief.xml</mzs:FileName> +                <mzs:MIMEType>text/xml</mzs:MIMEType> +                <msg:CheckSum> +                    <msg:AlgorithmID>SHA1</msg:AlgorithmID> +                    <msg:Value>9b972c70fdaf5e1b26b3387c87b0ffb72e5940b6</msg:Value> +                </msg:CheckSum> +                <mzs:Size>123401</mzs:Size> +            </mzs:Payload> +            <mzs:Config> +                <mzs:PerformQueryPersonRequest>true</mzs:PerformQueryPersonRequest> +                <mzs:TNVZClient> +                    <mzs:URL>http://localhost/tvnzservice</mzs:URL> +                    <mzs:ConnectionTimeout>0</mzs:ConnectionTimeout> +                    <mzs:ReceiveTimeout>0</mzs:ReceiveTimeout> +                </mzs:TNVZClient> +                <mzs:MsgResponseSinks> +                    <mzs:SaveResponseToFile> +                        <mzs:Active>true</mzs:Active> +                        <mzs:Path>target/tmp/ITEndToEndTestOut</mzs:Path> +                    </mzs:SaveResponseToFile> +                    <mzs:LogResponse>true</mzs:LogResponse> +                    <mzs:ForwardResponseToService> +                        <mzs:Active>true</mzs:Active> +                        <mzs:MzsClient> +                            <mzs:URL>http://localhost/appurl</mzs:URL> +                            <mzs:ConnectionTimeout>0</mzs:ConnectionTimeout> +                            <mzs:ReceiveTimeout>0</mzs:ReceiveTimeout> +                        </mzs:MzsClient> +                    </mzs:ForwardResponseToService> +                </mzs:MsgResponseSinks> +            </mzs:Config> +		</mzs:DeliveryRequest> +	</soapenv:Body> +</soapenv:Envelope> | 
