RemoteStoreConfigPropertiesUtils.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package org.openspcoop2.pdd.core.keystore;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.utils.certificate.KeystoreType;
import org.openspcoop2.utils.certificate.remote.RemoteKeyIdMode;
import org.openspcoop2.utils.certificate.remote.RemoteStoreConfig;
import org.openspcoop2.utils.properties.PropertiesReader;
import org.openspcoop2.utils.transport.http.HttpUtilities;

/**
 * RemoteStoreConfigPropertiesUtils
 *
 * @author Andrea Poli (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class RemoteStoreConfigPropertiesUtils {
	
	private RemoteStoreConfigPropertiesUtils() {}

	public static final String PROPERTY_STORE_NAME = "name";
	public static final String PROPERTY_STORE_LABEL = "label";
	
	public static final String PROPERTY_STORE_TOKEN_POLICY = "tokenPolicy";
	
	public static final String PROPERTY_STORE_URL = "baseUrl";
	
	public static final String PROPERTY_STORE_ID_MODE = "keyId.mode";
	public static final String PROPERTY_STORE_ID_PARAMETER_NAME = "keyId.parameter";
	
	public static final String PROPERTY_STORE_KEY_ALGORITHM = "keyAlgorithm";
	
	public static final String PROPERTY_READ_TIMEOUT = "readTimeout";
	public static final String PROPERTY_CONNECT_TIMEOUT = "connectTimeout";
	
	public static final String PROPERTY_HTTP_BASIC_USERNAME = "http.username";
	public static final String PROPERTY_HTTP_BASIC_USERNAME_DISABLE_VALUE = "#none#";
	public static final String PROPERTY_HTTP_BASIC_PASSWORD = "http.password";

	public static final String PROPERTY_HTTP_HEADER_PREFIX = "http.header.";
	
	public static final String PROPERTY_HTTP_QUERY_PARAMETER_PREFIX = "http.queryParameter.";
	
	public static final String PROPERTY_HTTPS_HOSTNAME_VERIFIER = "https.hostnameVerifier";
	public static final String PROPERTY_HTTPS_TRUST_ALL_CERTS = "https.trustAllCerts";
	public static final String PROPERTY_HTTPS_TRUST_STORE = "https.trustStore";
	public static final String PROPERTY_HTTPS_TRUST_STORE_PASSWORD = "https.trustStore.password";
	public static final String PROPERTY_HTTPS_TRUST_STORE_TYPE = "https.trustStore.type";
	public static final String PROPERTY_HTTPS_TRUST_STORE_CRL = "https.trustStore.crl";

	public static final String PROPERTY_HTTPS_KEY_STORE = "https.keyStore";
	public static final String PROPERTY_HTTPS_KEY_STORE_PASSWORD = "https.keyStore.password";
	public static final String PROPERTY_HTTPS_KEY_STORE_TYPE = "https.keyStore.type";
	public static final String PROPERTY_HTTPS_KEY_ALIAS = "https.key.alias";
	public static final String PROPERTY_HTTPS_KEY_PASSWORD = "https.key.password";

	public static final String PROPERTY_FORWARD_PROXY_URL = "forwardProxy.url";
	public static final String PROPERTY_FORWARD_PROXY_HEADER = "forwardProxy.header";
	public static final String PROPERTY_FORWARD_PROXY_QUERY_PARAMETER = "forwardProxy.queryParameter";
	public static final String PROPERTY_FORWARD_PROXY_BASE64 = "forwardProxy.base64";
	
	public static final String PROPERTY_MULTITENANT = "multiTenant";
	
	public static final String PROPERTY_MULTITENANT_BASEURL_DEFAULT_STRING = "multiTenant.baseUrl.defaultString";
	public static final String PROPERTY_MULTITENANT_BASEURL_PLACEHOLDER = "multiTenant.baseUrl.placeholder";
	public static final String PROPERTY_MULTITENANT_BASEURL_TENANT_STRING = "multiTenant.baseUrl.tenantString";
	
	public static final String PROPERTY_MULTITENANT_HTTP_BASIC_USERNAME_PREFIX = "multiTenant.http.username.";
	public static final String PROPERTY_MULTITENANT_HTTP_BASIC_PASSWORD_PREFIX = "multiTenant.http.password.";

	public static final String PROPERTY_MULTITENANT_HTTP_HEADER_PREFIX = "multiTenant.http.header.";
	
	public static final String PROPERTY_MULTITENANT_HTTP_QUERY_PARAMETER_PREFIX = "multiTenant.http.queryParameter.";
	
	
	public static RemoteStoreConfig read(Properties p, RequestInfo requestInfo) throws KeystoreException {
		
		String storeName = getProperty(p, PROPERTY_STORE_NAME, true);
		
		RemoteStoreConfig config = new RemoteStoreConfig(storeName);
		
		String storeLabel = getProperty(p, PROPERTY_STORE_LABEL, false);
		if(storeLabel!=null && StringUtils.isNotEmpty(storeLabel)) {
			config.setStoreLabel(storeLabel);
		}
		
		String tokenPolicy = getProperty(p, PROPERTY_STORE_TOKEN_POLICY, false);
		if(tokenPolicy!=null && StringUtils.isNotEmpty(tokenPolicy)) {
			config.setTokenPolicy(tokenPolicy);
		}
		
		String storeUrl = getProperty(p, PROPERTY_STORE_URL, true);
		config.setBaseUrl(storeUrl);
		
		String idMode = getProperty(p, PROPERTY_STORE_ID_MODE, true);
		RemoteKeyIdMode keyIdMode = RemoteKeyIdMode.valueOf(idMode);
		config.setIdMode(keyIdMode);
		
		switch (keyIdMode) {
		case HEADER:
		case URL_PARAMETER:
			config.setParameterName(getProperty(p, PROPERTY_STORE_ID_PARAMETER_NAME, true));
			break;
		default:
			break;
		}
		
		String keyAlgo = getProperty(p, PROPERTY_STORE_KEY_ALGORITHM, false);
		if(keyAlgo!=null && StringUtils.isNotEmpty(keyAlgo)) {
			config.setKeyAlgorithm(keyAlgo);
		}

		config.setReadTimeout(getIntProperty(p, PROPERTY_READ_TIMEOUT, false, 15000));
		config.setConnectTimeout(getIntProperty(p, PROPERTY_CONNECT_TIMEOUT, false, HttpUtilities.HTTP_CONNECTION_TIMEOUT));
		
		String basicUsername = getProperty(p, PROPERTY_HTTP_BASIC_USERNAME, false);
		if(basicUsername!=null && StringUtils.isNotEmpty(basicUsername) && !PROPERTY_HTTP_BASIC_USERNAME_DISABLE_VALUE.equals(basicUsername)) {
			config.setBasicUsername(basicUsername);
			
			String basicPassword = getProperty(p, PROPERTY_HTTP_BASIC_PASSWORD, false);
			if(basicPassword!=null && StringUtils.isNotEmpty(basicPassword)) {
				config.setBasicPassword(basicPassword);
			}
		}
		
		PropertiesReader pReader = new PropertiesReader(p, false);
		
		config.setHeaders(read(pReader, PROPERTY_HTTP_HEADER_PREFIX, config.getHeaders()));
		
		config.setQueryParameters(read(pReader, PROPERTY_HTTP_QUERY_PARAMETER_PREFIX, config.getQueryParameters()));
			
		config.setHostnameVerifier(getBooleanProperty(p, PROPERTY_HTTPS_HOSTNAME_VERIFIER, false, true));	
		
		readTrustStoreConfig(config, p, requestInfo);
		
		readKeyStoreConfig(config, p, requestInfo);
		
		readForwardProxyConfig(config, p);
		
		List<String> multi = readMultitenant(p);
		if(multi!=null && !multi.isEmpty()) {
			setMultitenant(config, multi, p, pReader);
		}
		
		return config;
	}
	
	private static Map<String, String> read(PropertiesReader pReader, String pName, Map<String, String> map) throws KeystoreException {
		Properties properties = null;
		try {
			properties = pReader.readProperties_convertEnvProperties(pName);
		}catch(Exception e) {
			throw new KeystoreException("Property '"+pName+".*' read failed: "+e.getMessage(),e);
		}
		if(properties!=null && !properties.isEmpty()) {
			for (Map.Entry<Object,Object> entry : properties.entrySet()) {
				if(entry.getKey() instanceof String && entry.getValue() instanceof String) {
					String name = (String) entry.getKey();
					String value = (String) entry.getValue();
					if(map==null) {
						map = new HashMap<>();
					}
					map.put(name, value);
				}
			}
		}
		return map;
	}
	
	private static List<String> readMultitenant(Properties p) throws KeystoreException{
		List<String> l = new ArrayList<>();
		String multi = getProperty(p, PROPERTY_MULTITENANT, false);
		if(multi==null || StringUtils.isEmpty(multi.trim())) {
			return l;
		}
		multi = multi.trim();
		if(!multi.contains(",")) {
			l.add(multi);
			return l;
		}
		String [] tmp = multi.split(",");
		if(tmp!=null && tmp.length>0) {
			for (String s : tmp) {
				if(s!=null && StringUtils.isNotEmpty(s.trim())) {
					l.add(s.trim());
				}
			}
		}
		return l;
	}
	
	private static void setMultitenant(RemoteStoreConfig config, List<String> multi, Properties p, PropertiesReader pReader) throws KeystoreException {
		config.setMultitenant(true);
		config.setBaseUrlMultitenantDefaultString(getProperty(p, PROPERTY_MULTITENANT_BASEURL_DEFAULT_STRING, true));
		config.setBaseUrlMultitenantPlaceholder(getProperty(p, PROPERTY_MULTITENANT_BASEURL_PLACEHOLDER, true));
		config.setBaseUrlMultitenantTenantString(getProperty(p, PROPERTY_MULTITENANT_BASEURL_TENANT_STRING, true));
		
		for (String tenant : multi) {
			setMultitenantHttpBasic(config, tenant, p);
			setMultitenantHttp(config, tenant, pReader);
		}
	}
	private static void setMultitenantHttpBasic(RemoteStoreConfig config, String tenant, Properties p) throws KeystoreException {
		String username = getProperty(p, PROPERTY_MULTITENANT_HTTP_BASIC_USERNAME_PREFIX+tenant, false);
		if(username!=null) {
			if(config.getMultiTenantBasicUsername()==null) {
				config.setMultiTenantBasicUsername(new HashMap<>());
			}
			config.getMultiTenantBasicUsername().put(tenant, username);
		}
		
		String password = getProperty(p, PROPERTY_MULTITENANT_HTTP_BASIC_PASSWORD_PREFIX+tenant, false);
		if(password!=null) {
			if(config.getMultiTenantBasicPassword()==null) {
				config.setMultiTenantBasicPassword(new HashMap<>());
			}
			config.getMultiTenantBasicPassword().put(tenant, password);
		}
	}
	private static void setMultitenantHttp(RemoteStoreConfig config, String tenant, PropertiesReader pReader) throws KeystoreException {
		Map<String, String> header = read(pReader, PROPERTY_MULTITENANT_HTTP_HEADER_PREFIX+tenant+".", config.getMultiTenantHeaders()!=null ? config.getMultiTenantHeaders().get(tenant) : null);
		if(header!=null && !header.isEmpty()) {
			if(config.getMultiTenantHeaders()==null) {
				config.setMultiTenantHeaders(new HashMap<>());
			}
			config.getMultiTenantHeaders().put(tenant, header);
		}
		
		Map<String, String> query = read(pReader, PROPERTY_MULTITENANT_HTTP_QUERY_PARAMETER_PREFIX+tenant+".", config.getMultiTenantQueryParameters()!=null ? config.getMultiTenantQueryParameters().get(tenant) : null);
		if(query!=null && !query.isEmpty()) {
			if(config.getMultiTenantQueryParameters()==null) {
				config.setMultiTenantQueryParameters(new HashMap<>());
			}
			config.getMultiTenantQueryParameters().put(tenant, query);
		}
	}
	
	private static void readTrustStoreConfig(RemoteStoreConfig config, Properties p, RequestInfo requestInfo) throws KeystoreException {
		config.setTrustAllCerts(getBooleanProperty(p, PROPERTY_HTTPS_TRUST_ALL_CERTS, false, false));	
		String trustStorePath = getProperty(p, PROPERTY_HTTPS_TRUST_STORE, false);	
		if(trustStorePath!=null && StringUtils.isNotEmpty(trustStorePath)) {
			String trustStorePassword = getProperty(p, PROPERTY_HTTPS_TRUST_STORE_PASSWORD, true);	
			String trustStoreType = getProperty(p, PROPERTY_HTTPS_TRUST_STORE_TYPE, false);
			if(trustStoreType==null || StringUtils.isEmpty(trustStoreType)) {
				trustStoreType = KeystoreType.JKS.getNome();
			}
			try {
				config.setTrustStore(GestoreKeystoreCaching.getMerlinTruststore(requestInfo, trustStorePath, trustStoreType, trustStorePassword).getTrustStore().getKeystore());
			}catch(Exception e) {
				throw new KeystoreException(e.getMessage(),e);
			}
			
			String crl = getProperty(p, PROPERTY_HTTPS_TRUST_STORE_CRL, false);
			if(crl!=null && StringUtils.isNotEmpty(crl)) {
				try {
					config.setCrlStore(GestoreKeystoreCaching.getCRLCertstore(requestInfo, crl).getCertStore());
				}catch(Exception e) {
					throw new KeystoreException(e.getMessage(),e);
				}
			}
		}
	}
	
	private static void readKeyStoreConfig(RemoteStoreConfig config, Properties p, RequestInfo requestInfo) throws KeystoreException {
		String keyStorePath = getProperty(p, PROPERTY_HTTPS_KEY_STORE, false);	
		if(keyStorePath!=null && StringUtils.isNotEmpty(keyStorePath)) {
			String keyStorePassword = getProperty(p, PROPERTY_HTTPS_KEY_STORE_PASSWORD, true);	
			String keyStoreType = getProperty(p, PROPERTY_HTTPS_KEY_STORE_TYPE, false);
			if(keyStoreType==null || StringUtils.isEmpty(keyStoreType)) {
				keyStoreType = KeystoreType.JKS.getNome();
			}
			
			String keyPassword = getProperty(p, PROPERTY_HTTPS_KEY_PASSWORD, true);	
			String keyAlias = getProperty(p, PROPERTY_HTTPS_KEY_ALIAS, false);	
			
			try {
				config.setKeyStore(GestoreKeystoreCaching.getMerlinKeystore(requestInfo, keyStorePath,keyStoreType,keyStorePassword,keyPassword).getKeyStore().getKeystore());
			}catch(Exception e) {
				throw new KeystoreException(e.getMessage(),e);
			}
			config.setKeyAlias(keyAlias);
			config.setKeyPassword(keyPassword);	
		}
	}
	
	private static void readForwardProxyConfig(RemoteStoreConfig config, Properties p) throws KeystoreException {
		String forwardProxyUrl = getProperty(p, PROPERTY_FORWARD_PROXY_URL, false);	
		if(forwardProxyUrl!=null && StringUtils.isNotEmpty(forwardProxyUrl)) {
			config.setForwardProxyUrl(forwardProxyUrl);
			config.setForwardProxyHeader(getProperty(p, PROPERTY_FORWARD_PROXY_HEADER, false));	
			config.setForwardProxyQueryParameter(getProperty(p, PROPERTY_FORWARD_PROXY_QUERY_PARAMETER, false));
			if(config.getForwardProxyHeader()==null && config.getForwardProxyQueryParameter()==null) {
				throw new KeystoreException("ForwardProxy property '"+PROPERTY_FORWARD_PROXY_URL+"' require '"+
						PROPERTY_FORWARD_PROXY_HEADER+"' o '"+
						PROPERTY_FORWARD_PROXY_QUERY_PARAMETER+"'");
			}
			config.setForwardProxyBase64(getBooleanProperty(p, PROPERTY_FORWARD_PROXY_BASE64, false, true));
		}
	}
	
	private static String getProperty(Properties p, String name, boolean required) throws KeystoreException {
		String tmp = p.getProperty(name);
		if(tmp!=null) {
			return tmp.trim();
		}
		else {
			if(required) {
				throw new KeystoreException("Property '"+name+"' notFound");
			}
			return null;
		}
	}
	private static boolean getBooleanProperty(Properties p, String name, boolean required, boolean defaultValue) throws KeystoreException {
		String tmp = getProperty(p, name, required);
		if(tmp!=null && StringUtils.isNotEmpty(tmp)) {
			try {
				return Boolean.valueOf(tmp);
			}catch(Exception t) {
				throw new KeystoreException("Boolean property '"+name+"' invalid (found value:["+tmp+"]): "+t.getMessage(),t);
			}
		}
		return defaultValue;
	}
	private static int getIntProperty(Properties p, String name, boolean required, int defaultValue) throws KeystoreException {
		String tmp = getProperty(p, name, required);
		if(tmp!=null && StringUtils.isNotEmpty(tmp)) {
			try {
				return Integer.valueOf(tmp);
			}catch(Exception t) {
				throw new KeystoreException("Boolean property '"+name+"' invalid (found value:["+tmp+"]): "+t.getMessage(),t);
			}
		}
		return defaultValue;
	}
}