/******************************************************************************* * 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.preprocess; import at.gv.egiz.moazs.util.StringUtils; import at.gv.zustellung.app2mzs.xsd.*; import org.springframework.stereotype.Component; import java.math.BigInteger; import java.util.List; import java.util.Map; import java.util.stream.Stream; import static at.gv.zustellung.app2mzs.xsd.ClientType.clientTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.CustomHTTPHeaderType.*; import static at.gv.zustellung.app2mzs.xsd.ForwardResponseToServiceType.forwardResponseToServiceTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.MsgResponseSinksType.msgResponseSinksTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.SSLType.SSLTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.SaveResponseToFileType.saveResponseToFileTypeBuilder; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; /** * @author Christof Rabensteiner * */ @Component public class ConfigUtil { public static final String TNVZ_REQUEST_KEY = "perform-query-person-request"; public static final String MSG_CLIENT_KEY = "msg-client"; public static final String TNVZ_CLIENT_KEY = "tnvz-client"; public static final String URL_KEY = "url"; public static final String SSL_KEY = "ssl"; public static final String TRUST_ALL_KEY = "trust-all"; public static final String LAX_HOSTNAME_VERIFICATION_KEY = "lax-hostname-verification"; public static final String KEYSTORE_KEY = "keystore"; public static final String TRUSTSTORE_KEY = "truststore"; public static final String FILENAME_KEY = "filename"; public static final String FILETYPE_KEY = "filetype"; public static final String PASSWORD_KEY = "password"; public static final String RECEIVE_TIMEOUT_KEY = "receive-timeout"; public static final String CONNECTION_TIMEOUT_KEY = "connection-timeout"; public static final String MSG_RESPONSE_SINKS_KEY = "msg-response-sinks"; public static final String LOG_RESPONSE_KEY = "log-response"; public static final String SAVE_RESPONSE_TO_FILE_KEY = "save-response-to-file"; public static final String ACTIVE_KEY = "active"; public static final String SAVE_RESPONSE_TO_FILE_PATH_KEY = "path"; public static final String FORWARD_RESPONSE_TO_SERVICE_KEY = "forward-response-to-service"; public static final String APP_CLIENT_KEY = "app-client"; public static final String SERVICE_TIMEOUT_KEY = "service-timeout"; public static final String CUSTOM_HTTP_HEADERS_KEY = "custom-http-headers"; /** * Convert a map into a Config object. * * @param values: Map with well-defined indexes and values * @return Config */ public ConfigType convert(Map values) { Boolean performQueryPersonRequest = booleanOrNull(values.get(TNVZ_REQUEST_KEY)); var msgClientParams = filterMapByPrefix(values, MSG_CLIENT_KEY); ClientType msgClient = msgClientParams.isEmpty() ? null : buildClient(msgClientParams); var tnvzClientParams = filterMapByPrefix(values, TNVZ_CLIENT_KEY); ClientType tnvzClient = tnvzClientParams.isEmpty() ? null : buildClient(tnvzClientParams); var msgResponseSinksParams = filterMapByPrefix(values, MSG_RESPONSE_SINKS_KEY); MsgResponseSinksType sinks = msgResponseSinksParams.isEmpty() ? null : buildMsgResponseSinks(msgResponseSinksParams); var serviceTimeout = bigIntOrNull(values.get(SERVICE_TIMEOUT_KEY)); return ConfigType.configTypeBuilder() .withPerformQueryPersonRequest(performQueryPersonRequest) .withMSGClient(msgClient) .withTNVZClient(tnvzClient) .withMsgResponseSinks(sinks) .withServiceTimeout(serviceTimeout) .build(); } private Map filterMapByPrefix(Map values, String prefix) { return values.entrySet().stream() .filter(entry -> entry.getKey().startsWith(prefix)) .collect(toMap(e -> StringUtils.removePrefix(e.getKey()), Map.Entry::getValue)); } private ClientType buildClient(Map clientParams) { var url = clientParams.get(URL_KEY); var connectionTimeout = bigIntOrNull(clientParams.get(CONNECTION_TIMEOUT_KEY)); var receiveTimeout = bigIntOrNull(clientParams.get(RECEIVE_TIMEOUT_KEY)); var sslParams = filterMapByPrefix(clientParams, SSL_KEY); SSLType ssl = sslParams.isEmpty() ? null : buildSSL(sslParams); var customHeaderParams = filterMapByPrefix(clientParams, CUSTOM_HTTP_HEADERS_KEY); var customHeaders = customHeaderParams.isEmpty() ? null : convertMapToHeaders(customHeaderParams); return clientTypeBuilder() .withURL(url) .withSSL(ssl) .withConnectionTimeout(connectionTimeout) .withReceiveTimeout(receiveTimeout) .withCustomHTTPHeader(customHeaders) .build(); } private SSLType buildSSL(Map sslParams) { var keyStoreParams = filterMapByPrefix(sslParams, KEYSTORE_KEY); KeyStoreType keyStore = keyStoreParams.isEmpty() ? null : buildKeyStore(keyStoreParams); var trustStoreParams = filterMapByPrefix(sslParams, TRUSTSTORE_KEY); KeyStoreType trustStore = trustStoreParams.isEmpty() ? null : buildKeyStore(trustStoreParams); var trustAll = booleanOrNull(sslParams.get(TRUST_ALL_KEY)); var laxHostNameVerification = booleanOrNull(sslParams.get(LAX_HOSTNAME_VERIFICATION_KEY)); return SSLTypeBuilder() .withKeyStore(keyStore) .withTrustStore(trustStore) .withTrustAll(trustAll) .withLaxHostNameVerification(laxHostNameVerification) .build(); } private KeyStoreType buildKeyStore(Map params) { return keyStoreTypeBuilder() .withFileName(params.get(FILENAME_KEY)) .withFileType(params.get(FILETYPE_KEY)) .withPassword(params.get(PASSWORD_KEY)) .build(); } private MsgResponseSinksType buildMsgResponseSinks(Map params) { var logResponse = booleanOrNull(params.get(LOG_RESPONSE_KEY)); var saveResponse = buildSaveResponse(filterMapByPrefix(params, SAVE_RESPONSE_TO_FILE_KEY)); var forwardResponse = buildForwardResponse(filterMapByPrefix(params, FORWARD_RESPONSE_TO_SERVICE_KEY)); return msgResponseSinksTypeBuilder() .withLogResponse(logResponse) .withSaveResponseToFile(saveResponse) .withForwardResponseToService(forwardResponse) .build(); } private SaveResponseToFileType buildSaveResponse(Map params) { var isActive = booleanOrNull(params.get(ACTIVE_KEY)); var path = params.get(SAVE_RESPONSE_TO_FILE_PATH_KEY); return saveResponseToFileTypeBuilder() .withActive(isActive) .withPath(path) .build(); } private ForwardResponseToServiceType buildForwardResponse(Map params) { var isActive = booleanOrNull(params.get(ACTIVE_KEY)); var appClientParams = filterMapByPrefix(params, APP_CLIENT_KEY); ClientType appClient = appClientParams.isEmpty() ? null : buildClient(appClientParams); return forwardResponseToServiceTypeBuilder() .withActive(isActive) .withAppClient(appClient) .build(); } private Boolean booleanOrNull(String value) { return value == null ? null : Boolean.valueOf(value); } private BigInteger bigIntOrNull(String value) { return value == null ? null : new BigInteger(value); } /** * Combine properties of two Configs; {@code primary} overrides {@code fallback}. * * @param primary * @param fallback * @return Merged Config */ public ConfigType merge(ConfigType primary, ConfigType fallback) { var builder = configTypeBuilder(fallback); if (primary.isPerformQueryPersonRequest() != null) { builder.withPerformQueryPersonRequest(primary.isPerformQueryPersonRequest()); } if (primary.getMSGClient() != null) { builder.withMSGClient(merge(primary.getMSGClient(), fallback.getMSGClient())); } if (primary.getTNVZClient() != null) { builder.withTNVZClient(merge(primary.getTNVZClient(), fallback.getTNVZClient())); } if (primary.getMsgResponseSinks() != null) { builder.withMsgResponseSinks(merge(primary.getMsgResponseSinks(), fallback.getMsgResponseSinks())); } if (primary.getServiceTimeout() != null) { builder.withServiceTimeout(primary.getServiceTimeout()); } return builder.build(); } private ClientType merge(ClientType primary, ClientType fallback) { if (fallback == null) { return primary; } var builder = clientTypeBuilder(fallback); if (primary.getURL() != null) { builder.withURL(primary.getURL()); } if (primary.getSSL() != null) { builder.withSSL(merge(primary.getSSL(), fallback.getSSL())); } if (primary.getConnectionTimeout() != null) { builder.withConnectionTimeout(primary.getConnectionTimeout()); } if (primary.getReceiveTimeout() != null) { builder.withReceiveTimeout(primary.getReceiveTimeout()); } if (primary.getCustomHTTPHeader() != null) { builder.withCustomHTTPHeader(merge(primary.getCustomHTTPHeader(), fallback.getCustomHTTPHeader())); } return builder.build(); } private SSLType merge(SSLType primary, SSLType fallback) { if (fallback == null) { return primary; } var builder = SSLTypeBuilder(fallback); if (primary.getKeyStore() != null) { builder.withKeyStore(merge(primary.getKeyStore(), fallback.getKeyStore())); } if (primary.getTrustStore() != null) { builder.withTrustStore(merge(primary.getTrustStore(), fallback.getTrustStore())); } if (primary.isLaxHostNameVerification() != null) { builder.withLaxHostNameVerification(primary.isLaxHostNameVerification()); } if (primary.isTrustAll() != null) { builder.withTrustAll(primary.isTrustAll()); } return builder.build(); } private KeyStoreType merge(KeyStoreType primary, KeyStoreType fallback) { if (primary.getFileName() != null && primary.getFileType() != null && primary.getPassword() != null) return primary; return fallback; } private List merge(List primary, List fallback) { if (fallback == null) return primary; var primaryMap = convertHeadersToMap(primary); var fallbackMap = convertHeadersToMap(fallback); Map mergedMap = Stream.of(fallbackMap, primaryMap) .flatMap(map -> map.entrySet().stream()) .collect(toMap( Map.Entry::getKey, entry -> entry.getValue().get(0), (v1, v2) -> v2 )); return convertMapToHeaders(mergedMap); } private MsgResponseSinksType merge(MsgResponseSinksType primary, MsgResponseSinksType fallback) { if (fallback == null) { return primary; } var builder = msgResponseSinksTypeBuilder(fallback); if (primary.isLogResponse() != null) { builder.withLogResponse(primary.isLogResponse()); } if (primary.getSaveResponseToFile() != null) { builder.withSaveResponseToFile(merge(primary.getSaveResponseToFile(), fallback.getSaveResponseToFile())); } if (primary.getForwardResponseToService() != null) { builder.withForwardResponseToService(merge(primary.getForwardResponseToService(), fallback.getForwardResponseToService())); } return builder.build(); } private SaveResponseToFileType merge(SaveResponseToFileType primary, SaveResponseToFileType fallback) { if (fallback == null) return primary; var builder = saveResponseToFileTypeBuilder(fallback); if (primary.isActive() != null) { builder.withActive(primary.isActive()); } if (primary.getPath() != null) { builder.withPath(primary.getPath()); } return builder.build(); } private ForwardResponseToServiceType merge(ForwardResponseToServiceType primary, ForwardResponseToServiceType fallback) { if (fallback == null) return primary; var builder = forwardResponseToServiceTypeBuilder(fallback); if (primary.isActive() != null) { builder.withActive(primary.isActive()); } if (primary.getAppClient() != null) { builder.withAppClient(merge(primary.getAppClient(), fallback.getAppClient())); } return builder.build(); } /** * Convert a Map that is indexed by header names into a List of CustomHTTPHeaders. * @param customHeaderMap map of headers, indexed by header names. * @return List of CustomHTTPHeaders */ public List convertMapToHeaders(Map customHeaderMap) { return customHeaderMap.entrySet().stream() .map(e -> customHTTPHeaderTypeBuilder() .withName(e.getKey()) .withValue(e.getValue()) .build()) .collect(toList()); } /** * Convert a List of CustomHTTPHeaders into a Map that is indexed by the header names. Values are wrapped in Lists. * @param customHeaders List of CustomHTTPHeaders * @return Map of headers, indexed by header names. */ public Map> convertHeadersToMap(List customHeaders) { return customHeaders .stream() .collect(toMap( CustomHTTPHeaderType::getName, h -> List.of(h.getValue()))); } }