/******************************************************************************* * Copyright 2019 Graz University of Technology * MOA ZS has been developed in a cooperation between EGIZ * and Graz University of Technology. * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ package at.gv.egiz.moazs.backend; import at.gv.egiz.moazs.MoaZSException; import at.gv.egiz.moazs.preprocess.MzsDeliveryRequestValidator; import at.gv.egiz.moazs.repository.DeliveryRepository; import at.gv.egiz.moazs.scheme.MsgResponse; import at.gv.egiz.moazs.service.MsgService; import at.gv.zustellung.app2mzs.xsd.ConfigType; import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Map; import java.util.function.Consumer; import java.util.function.Supplier; import static at.gv.egiz.moazs.MoaZSException.*; import static at.gv.egiz.moazs.preprocess.ConfigProfileGenerator.DEFAULT_CONFIG_KEY; import static at.gv.zustellung.app2mzs.xsd.DeliveryRequestType.deliveryRequestTypeBuilder; import static java.lang.String.format; import static java.util.concurrent.CompletableFuture.supplyAsync; /** * @author Christof Rabensteiner * */ @Component public class MsgResponseBackend implements Consumer { 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."; public static final String BINARY_RESPONSE_MISSING_ERROR_MSG = "Binary Response is not in repository."; public static final String RESPONSE_MISSING_ERROR_MSG = "Response with ResponseID=%s is not in repository."; public static final String REQUEST_MISSING_ERROR_MSG = "Request with AppDeliveryID=%s is not in repository. " + "Falling Back to Default Configuration."; private final DeliveryRepository repository; private final Consumer signatureVerifier; private final MsgResponseSinkHub hub; private final Map deliveryRequestConfigs; private final MzsDeliveryRequestValidator validator; @Autowired public MsgResponseBackend(DeliveryRepository repository, Consumer signatureVerifier, MsgResponseSinkHub hub, Map deliveryRequestConfigs, MzsDeliveryRequestValidator validator) { this.repository = repository; this.signatureVerifier = signatureVerifier; this.hub = hub; this.deliveryRequestConfigs = deliveryRequestConfigs; this.validator = validator; } /** * Performs all {@code MsgResponse}'s Back-End Tasks, such as verifying * its signature and archiving the response. * * Note: When the signature verification fails, this method will not archive * the original response (the one that was received from msg) to the sink, * but an error message. * * @param responseID refers to MsgResponse Object. */ @Override public void accept(String responseID) { log.info("Backend accepts MsgResponse with responseID={}.", responseID); supplyAsync(() -> verifySignature(responseID)) .thenAcceptAsync(msgResponse -> applySinks(msgResponse)) .exceptionally(ex -> { log.error(ex.getMessage(), ex); return null; }); } private MsgResponse verifySignature(String responseID) { var response = repository.retrieveResponse(responseID).orElseThrow( ()-> moaZSException(format(RESPONSE_MISSING_ERROR_MSG, responseID))); try { var binaryResponse = repository.retrieveBinaryResponse(responseID).orElseThrow( () -> moaZSException(BINARY_RESPONSE_MISSING_ERROR_MSG, MoaZSException.ERROR_MZS_BINARY_RESPONSE_MISSING)); log.info("Verify Signature of msg:{} with AppDeliveryID={}.", response.getRootElementLocalPart(), response.getAppDeliveryID()); signatureVerifier.accept(binaryResponse); return response; } catch (MoaZSException ex) { log.error(ex.getMessage(), ex); var text = format(MOASP_SIGNATURE_INVALID_ERROR_MSG, response.getAppDeliveryID()); var code = ERROR_MOASP_SIGNATURE_INVALID; return response.generateError(text, code); } } private void applySinks(MsgResponse msgResponse) { var appDeliveryID = msgResponse.getAppDeliveryID(); var config = repository .retrieveDeliveryRequest(appDeliveryID) .orElseGet(supplyRequestWithDefaultConfig(appDeliveryID)) .getConfig(); var sinkParams = config.getMsgResponseSinks(); hub.applySinks(msgResponse, sinkParams); } private Supplier supplyRequestWithDefaultConfig(String appDeliveryID) { return () -> { log.warn(format(REQUEST_MISSING_ERROR_MSG, appDeliveryID)); var defaultConfig = deliveryRequestConfigs.get(DEFAULT_CONFIG_KEY); validator.areSinksConfigured(defaultConfig.getMsgResponseSinks()); return deliveryRequestTypeBuilder() .withConfig(defaultConfig) .build(); }; } }