From 5780d9aa93fcd363584046fb404c63b909faebdb Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Fri, 13 Sep 2019 13:23:21 +0200 Subject: add SimpleStatusMessager.java that only perform String formatting add SSL Client authentication and http basic authentication to HttpClientFactory.java --- .../core/impl/logging/SimpleStatusMessager.java | 44 ++++ .../eaaf/core/impl/utils/HttpClientFactory.java | 240 +++++++++++++++++++-- 2 files changed, 264 insertions(+), 20 deletions(-) create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java (limited to 'eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core') diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java new file mode 100644 index 00000000..d36c79b9 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java @@ -0,0 +1,44 @@ +package at.gv.egiz.eaaf.core.impl.logging; + +import java.text.MessageFormat; + +import at.gv.egiz.eaaf.core.api.IStatusMessenger; + +/** + * Simple {@link IStatusMessenger} implementation that formats messages by using {@link MessageFormat} + * + * @author tlenz + * + */ +public class SimpleStatusMessager implements IStatusMessenger { + + private static final String NOTSUPPORTED = "Error response-codes not supported"; + private static final String NULLMESSAGE = "No error-message provided"; + + @Override + public String getMessage(String messageId, Object[] parameters) { + return getMessageWithoutDefault(messageId, parameters); + + } + + @Override + public String getMessageWithoutDefault(String messageId, Object[] parameters) { + if (messageId != null) { + return MessageFormat.format(messageId, parameters); + + } + + return NULLMESSAGE; + } + + @Override + public String getResponseErrorCode(Throwable throwable) { + return NOTSUPPORTED; + } + + @Override + public String mapInternalErrorToExternalError(String intErrorCode) { + return NOTSUPPORTED; + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java index a141d92d..73f2c518 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java @@ -1,31 +1,46 @@ package at.gv.egiz.eaaf.core.impl.utils; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import javax.annotation.PostConstruct; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.ProtocolException; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; import org.apache.http.client.RedirectStrategy; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContexts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; public class HttpClientFactory implements IHttpClientFactory { private static final Logger log = LoggerFactory.getLogger(HttpClientFactory.class); @@ -39,6 +54,13 @@ public class HttpClientFactory implements IHttpClientFactory { public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "client.http.connection.timeout.request"; public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = "client.http.ssl.hostnameverifier.trustall"; + public static final String PROP_CONFIG_CLIENT_MODE = "client.authmode"; + public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME = "client.auth.http.username"; + public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD = "client.auth.http.password"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH = "client.auth.ssl.keystore.path"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD = "client.auth.ssl.keystore.password"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE = "client.auth.ssl.keystore.type"; + // default configuration values public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15"; public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "15"; @@ -46,6 +68,79 @@ public class HttpClientFactory implements IHttpClientFactory { public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500"; public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100"; + public enum ClientAuthMode { + NONE("none"), + PASSWORD("password"), + SSL("ssl"); + + private final String mode; + + private ClientAuthMode(String mode) { + this.mode = mode; + } + + /** + * Get the PVP mode + * + * @return + */ + public String getMode() { + return this.mode; + } + + public static ClientAuthMode fromString(String s) { + try { + return ClientAuthMode.valueOf(s.toUpperCase()); + + } catch (IllegalArgumentException|NullPointerException e) { + return null; + } + } + + @Override + public String toString() { + return getMode(); + + } + + }; + + public enum KeyStoreType { + PKCS12("pkcs12"), + JKS("jks"); + + private final String type; + + private KeyStoreType (String type) { + this.type = type; + } + + /** + * Get the PVP mode + * + * @return + */ + public String getType() { + return this.type; + } + + public static KeyStoreType fromString(String s) { + try { + return KeyStoreType.valueOf(s.toUpperCase()); + + } catch (IllegalArgumentException|NullPointerException e) { + return null; + } + } + + @Override + public String toString() { + return getType(); + + } + + }; + private HttpClientBuilder httpClientBuilder = null; /* (non-Javadoc) @@ -102,50 +197,155 @@ public class HttpClientFactory implements IHttpClientFactory { .build(); httpClientBuilder.setDefaultRequestConfig(requestConfig); - //set pool connection if requested - if (basicConfig.getBasicConfigurationBoolean( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE, - true)) { - final PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(); - pool.setDefaultMaxPerRoute(Integer.valueOf(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE))); - pool.setMaxTotal(Integer.valueOf(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); + //set pool connection if required + injectConnectionPoolIfRequired(); + + final ClientAuthMode clientAuthMode = ClientAuthMode.fromString( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_MODE, ClientAuthMode.NONE.getMode())); + log.info("Client authentication-mode is set to: {}", clientAuthMode); + + //inject basic http authentication if required + injectBasicAuthenticationIfRequired(clientAuthMode); + + //inject authentication if required + injectSSLContext(clientAuthMode); + + } + + private void injectBasicAuthenticationIfRequired(ClientAuthMode clientAuthMode) { + if (clientAuthMode.equals(ClientAuthMode.PASSWORD)) { + final CredentialsProvider provider = new BasicCredentialsProvider(); + final String username = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME); + final String password = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD); + if (StringUtils.isEmpty(username)) { + log.warn("Http basic authentication was activated but NOT username was set!"); + + } + + log.trace("Injecting basic authentication with username: {} and password: {}", username, password); + final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); + provider.setCredentials(AuthScope.ANY, credentials); + httpClientBuilder.setDefaultCredentialsProvider(provider); + log.info("Basic http authentication was injected with username: {}", username); + + } else { + log.trace("Injection of Http Basic authentication was skipped"); - httpClientBuilder.setConnectionManager(pool); - log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), pool.getDefaultMaxPerRoute()); - } + } + + private SSLContext buildSSLContextWithSSLClientAuthentication() throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, EAAFConfigurationException { + log.trace("Injecting SSL client-authentication into http client ... "); + final KeyStore keystore = getSSLAuthKeyStore(); + final String keyPasswordString = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD); + log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); + final char[] keyPassword = (keyPasswordString == null) ? StringUtils.EMPTY.toCharArray() : keyPasswordString.toCharArray(); + return SSLContexts.custom().loadKeyMaterial(keystore, keyPassword).build(); + + } + + private KeyStore getSSLAuthKeyStore() throws EAAFConfigurationException { + final KeyStoreType keyStoreType = KeyStoreType.fromString( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE, KeyStoreType.PKCS12.getType())); + final String localKeyStorePath = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH, StringUtils.EMPTY); + final String keyStorePassword = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD, StringUtils.EMPTY); + + try { + log.debug("Open keyStore with type: {}", keyStoreType); + KeyStore clientStore; + if (keyStoreType.equals(KeyStoreType.PKCS12)) { + clientStore = KeyStore.getInstance("pkcs12"); + } else { + clientStore = KeyStore.getInstance("JKS"); + } + + + log.debug("Read keyStore path: {} from configuration", localKeyStorePath); + if (StringUtils.isNotEmpty(localKeyStorePath)) { + String absFilePath = FileUtils.makeAbsoluteURL(localKeyStorePath, basicConfig.getConfigurationRootDirectory()); + + if (absFilePath.startsWith("file:")) { + absFilePath = absFilePath.substring("file:".length()); + + } + + final File keyStoreFile = new File(absFilePath); + log.trace("Load keyStore: {} with password: {}", absFilePath, keyStorePassword); + clientStore.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray()); + + return clientStore; + + } else { + log.warn("Path to keyStore for SSL Client-Authentication is empty or null"); + throw new EAAFConfigurationException("Path to keyStore for SSL Client-Authentication is empty or null", new Object[] {}); + + } + + } catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { + log.warn("Can NOT read keyStore: {} from filesystem", localKeyStorePath, null, e); + throw new EAAFConfigurationException("Can NOT read keyStore: {} from filesystem", new Object[] {localKeyStorePath}, e); + + } + + } + + private void injectSSLContext(ClientAuthMode clientAuthMode) { + SSLContext sslContext = null; try { - log.trace("Initializing SSL Context ... "); - final SSLContext sslContext = SSLContext.getDefault(); + if (clientAuthMode.equals(ClientAuthMode.SSL)) { + sslContext = buildSSLContextWithSSLClientAuthentication(); + + } else { + log.trace("Initializing default SSL Context ... "); + sslContext = SSLContext.getDefault(); + + } + + //set hostname verifier HostnameVerifier hostnameVerifier = null; if (basicConfig.getBasicConfigurationBoolean( PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false)) { hostnameVerifier = new NoopHostnameVerifier(); log.warn("HTTP client-builder deactivates SSL Host-name verification!"); + } - final LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext , hostnameVerifier); + final LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext , hostnameVerifier); httpClientBuilder.setSSLSocketFactory(sslSocketFactory ); - } catch (final NoSuchAlgorithmException e) { + } catch (final NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException | KeyStoreException | EAAFConfigurationException e) { log.warn("HTTP client-builder can NOT initialze SSL-Context", e); } - log.info("HTTP client-builder successfuly initialized"); } - + private void injectConnectionPoolIfRequired() { + if (basicConfig.getBasicConfigurationBoolean( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE, + true)) { + final PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(); + pool.setDefaultMaxPerRoute(Integer.valueOf(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE))); + pool.setMaxTotal(Integer.valueOf(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); + + + + httpClientBuilder.setConnectionManager(pool); + log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), pool.getDefaultMaxPerRoute()); + + } + + } -- cgit v1.2.3