summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lenz <thomas.lenz@egiz.gv.at>2020-03-11 12:46:45 +0100
committerThomas Lenz <thomas.lenz@egiz.gv.at>2020-03-11 12:46:45 +0100
commitf95a1fb3982395ccbc7e139cb5bd8a1c106bbb48 (patch)
treee2fd57efe89ded06cfe998d712b68bd2ed4b1de2
parent19bc544de503af5992d045a699a1f2bcc1eaf505 (diff)
downloadEAAF-Components-f95a1fb3982395ccbc7e139cb5bd8a1c106bbb48.tar.gz
EAAF-Components-f95a1fb3982395ccbc7e139cb5bd8a1c106bbb48.tar.bz2
EAAF-Components-f95a1fb3982395ccbc7e139cb5bd8a1c106bbb48.zip
refactor HttpClientFactory.java to build HTTP clients with different authentication mechanisms
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java2
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java2
-rw-r--r--eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java12
-rw-r--r--eaaf_core_utils/pom.xml10
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java50
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java191
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java (renamed from eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java)344
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java (renamed from eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java)78
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java43
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java24
-rw-r--r--eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties5
-rw-r--r--eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml4
-rw-r--r--eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java328
-rw-r--r--eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java2
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java2
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java2
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java2
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java2
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java2
-rw-r--r--eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml2
-rw-r--r--pom.xml8
21 files changed, 898 insertions, 217 deletions
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java
index 817c7aa2..4c82adac 100644
--- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java
@@ -70,8 +70,8 @@ import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException;
import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException;
import at.gv.egiz.eaaf.core.exceptions.ProtocolNotActiveException;
import at.gv.egiz.eaaf.core.impl.gui.AbstractGuiFormBuilderConfiguration;
+import at.gv.egiz.eaaf.core.impl.http.HttpUtils;
import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl;
-import at.gv.egiz.eaaf.core.impl.utils.HttpUtils;
@Service
public class ProtocolAuthenticationService implements IProtocolAuthenticationService {
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java
index adc8774a..f4494106 100644
--- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java
@@ -40,8 +40,8 @@ import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration;
import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException;
import at.gv.egiz.eaaf.core.exceptions.EaafException;
import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import at.gv.egiz.eaaf.core.impl.http.HttpUtils;
import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper;
-import at.gv.egiz.eaaf.core.impl.utils.HttpUtils;
import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils;
import org.apache.commons.lang3.StringUtils;
diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java
index 9a924f83..6aea52ac 100644
--- a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java
+++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java
@@ -1,8 +1,10 @@
package at.gv.egiz.eaaf.core.impl.idp.auth;
-import org.apache.http.impl.client.CloseableHttpClient;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.http.HttpClientConfiguration;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
-import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
public class DummyHttpClientFactory implements IHttpClientFactory {
@@ -18,4 +20,10 @@ public class DummyHttpClientFactory implements IHttpClientFactory {
return null;
}
+ @Override
+ public CloseableHttpClient getHttpClient(HttpClientConfiguration config) throws EaafException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
}
diff --git a/eaaf_core_utils/pom.xml b/eaaf_core_utils/pom.xml
index 1f5467ab..681152f7 100644
--- a/eaaf_core_utils/pom.xml
+++ b/eaaf_core_utils/pom.xml
@@ -100,6 +100,16 @@
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>mockwebserver</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>okhttp-tls</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java
new file mode 100644
index 00000000..1e1e2137
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java
@@ -0,0 +1,50 @@
+package at.gv.egiz.eaaf.core.impl.http;
+
+import java.net.Socket;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.ssl.PrivateKeyDetails;
+import org.apache.http.ssl.PrivateKeyStrategy;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Private Key selection implementation for Apache HTTP clients.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class EaafSslKeySelectionStrategy implements PrivateKeyStrategy {
+
+ private final String keyAlias;
+
+ /**
+ * Private Key selection implementation for Apache HTTP clients.
+ *
+ * @param alias Alias of the Key that should be used for SSL client authentication.
+ */
+ public EaafSslKeySelectionStrategy(String alias) {
+ this.keyAlias = alias;
+
+ }
+
+ @Override
+ public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
+ log.trace("Selection SSL client-auth key for alias: {}", keyAlias);
+ final PrivateKeyDetails selected = aliases.get(keyAlias);
+ if (selected != null) {
+ log.trace("Select SL client-auth key with type:", selected.getType());
+ return keyAlias;
+
+ } else {
+ log.warn("KeyStore contains NO key with alias: {}. Using first key from keystore", keyAlias);
+ log.info("Available aliases: {}", StringUtils.join(aliases.keySet(), ", "));
+ return aliases.keySet().iterator().next();
+
+ }
+
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java
new file mode 100644
index 00000000..582ad545
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java
@@ -0,0 +1,191 @@
+package at.gv.egiz.eaaf.core.impl.http;
+
+import java.text.MessageFormat;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration;
+
+import org.apache.commons.lang3.StringUtils;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Getter
+public class HttpClientConfiguration {
+
+ private static final String MSG_KEYSTORE_NAME = "KeyStore for httpClient: {0}";
+
+ private static final String ERROR_00 = "internal.httpclient.00";
+ private static final String ERROR_01 = "internal.httpclient.01";
+ private static final String ERROR_02 = "internal.httpclient.02";
+
+ @Nonnull
+ private final String friendlyName;
+
+ @Nonnull
+ private final String uuid;
+
+ @Nonnull
+ private ClientAuthMode authMode = ClientAuthMode.NONE;
+
+ @Setter
+ private String username;
+
+ @Setter
+ private String password;
+
+ @Setter
+ boolean disableHostnameValidation = false;
+
+ @Setter
+ boolean disableTlsHostCertificateValidation = false;
+
+
+ private KeyStoreConfiguration keyStoreConfig;
+
+ @Setter
+ private String sslKeyAlias;
+
+ @Setter
+ private String sslKeyPassword;
+
+ @Setter
+ private boolean followHttpRedirects = true;
+
+ /**
+ * Get a new HTTP-client configuration object.
+ *
+ * @param name FriendlyName of this http client for logging purposes.
+ */
+ public HttpClientConfiguration(String name) {
+ this.friendlyName = name;
+ this.uuid = UUID.randomUUID().toString();
+
+ }
+
+ /**
+ * Set Client authentication-mode from configuration property.
+ *
+ * <p>If the mode is unknown than the {@link ClientAuthMode} is set to <code>NONE</code> </p>
+ *
+ * @param authModeString Modes from {@link ClientAuthMode}
+ */
+ public void setAuthMode(String authModeString) {
+ final ClientAuthMode configAuthMode = HttpClientConfiguration.ClientAuthMode.fromString(authModeString);
+ if (configAuthMode != null) {
+ authMode = configAuthMode;
+
+ } else {
+ log.warn("Can Not parse ClientAuthMode for client: {}! Set mode to default value",
+ friendlyName);
+
+ }
+ }
+
+
+ /**
+ * Validate the internal state of this configuration object.
+ *
+ * @throws EaafConfigurationException In case of a configuration error
+ */
+ public void validate() throws EaafConfigurationException {
+ log.trace("Validating http-client: {}", this.friendlyName);
+ if (this.authMode.equals(ClientAuthMode.PASSWORD)) {
+ if (StringUtils.isEmpty(this.username)) {
+ throw new EaafConfigurationException(ERROR_00, new Object[] {this.friendlyName});
+
+ }
+
+ if (StringUtils.isEmpty(this.password)) {
+ log.warn("Http basic authentication was activated but NOT username was set!");
+
+ }
+
+ } else if (this.authMode.equals(ClientAuthMode.SSL)) {
+ if (this.keyStoreConfig == null) {
+ throw new EaafConfigurationException(ERROR_01, new Object[] {this.friendlyName});
+
+ } else {
+ log.trace("Validating KeyStore: {} for http-client: {} ...",
+ this.keyStoreConfig.getFriendlyName(), this.friendlyName);
+ this.keyStoreConfig.validate();
+
+ }
+
+ if (StringUtils.isEmpty(this.sslKeyPassword)) {
+ throw new EaafConfigurationException(ERROR_02, new Object[] {
+ this.friendlyName, this.keyStoreConfig.getFriendlyName()});
+
+ }
+ }
+
+ }
+
+ /**
+ * Build a {@link KeyStoreConfiguration} object from configuration parameters.
+ *
+ * @param keyStoreType String based KeyStore type
+ * @param keyStorePath Path to KeyStore in case of a software based KeyStore
+ * @param keyStorePassword Password in case of a software based KeyStore
+ * @param keyStoreName Name of the KeyStore in case of a named KeyStore like HSM-Facade
+ * @throws EaafConfigurationException In case of a configuration error
+ */
+ public void buildKeyStoreConfig(String keyStoreType, String keyStorePath,
+ String keyStorePassword, String keyStoreName) throws EaafConfigurationException {
+ final KeyStoreConfiguration config = new KeyStoreConfiguration();
+ config.setKeyStoreType(keyStoreType);
+ config.setFriendlyName(MessageFormat.format(MSG_KEYSTORE_NAME, friendlyName));
+ config.setSoftKeyStoreFilePath(keyStorePath);
+ config.setSoftKeyStorePassword(keyStorePassword);
+ config.setKeyStoreName(keyStoreName);
+ this.keyStoreConfig = config;
+
+ }
+
+ public enum ClientAuthMode {
+ NONE("none"), PASSWORD("password"), SSL("ssl");
+
+ private final String mode;
+
+ ClientAuthMode(final String mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Get the PVP mode.
+ *
+ * @return
+ */
+ public String getMode() {
+ return this.mode;
+ }
+
+ /**
+ * Get http-client authentication mode from String representation.
+ *
+ * @param s Config parameter
+ * @return
+ */
+ public static ClientAuthMode fromString(final String s) {
+ try {
+ return ClientAuthMode.valueOf(s.toUpperCase());
+
+ } catch (IllegalArgumentException | NullPointerException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getMode();
+
+ }
+
+ }
+
+}
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/http/HttpClientFactory.java
index e681e705..b6e660da 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/http/HttpClientFactory.java
@@ -1,11 +1,11 @@
-package at.gv.egiz.eaaf.core.impl.utils;
+package at.gv.egiz.eaaf.core.impl.http;
-import java.security.KeyManagementException;
import java.security.KeyStore;
-import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@@ -13,8 +13,8 @@ import javax.net.ssl.SSLContext;
import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException;
import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory;
-import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration;
import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType;
import org.apache.commons.lang3.StringUtils;
@@ -41,22 +41,19 @@ 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.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.ResourceLoader;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class HttpClientFactory implements IHttpClientFactory {
- @Autowired(required = true)
+ @Autowired
private IConfiguration basicConfig;
+ @Autowired
+ private EaafKeyStoreFactory keyStoreFactory;
- @Autowired(required = true)
- ResourceLoader resourceLoader;
-
- @Autowired private EaafKeyStoreFactory keyStoreFactory;
+ private static final String ERROR_03 = "internal.httpclient.03";
public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE =
"client.http.connection.pool.use";
@@ -84,9 +81,10 @@ public class HttpClientFactory implements IHttpClientFactory {
"client.auth.ssl.keystore.name";
public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE =
"client.auth.ssl.keystore.type";
+ public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS =
+ "client.auth.ssl.key.alias";
public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD =
"client.auth.ssl.key.password";
- public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS = "client.auth.ssl.key.alias";
// default configuration values
public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15";
@@ -95,48 +93,8 @@ 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;
-
- ClientAuthMode(final String mode) {
- this.mode = mode;
- }
-
- /**
- * Get the PVP mode.
- *
- * @return
- */
- public String getMode() {
- return this.mode;
- }
-
- /**
- * Get http-client authentication mode from String representation.
- *
- * @param s Config parameter
- * @return
- */
- public static ClientAuthMode fromString(final String s) {
- try {
- return ClientAuthMode.valueOf(s.toUpperCase());
-
- } catch (IllegalArgumentException | NullPointerException e) {
- return null;
- }
- }
-
- @Override
- public String toString() {
- return getMode();
-
- }
-
- }
-
- private HttpClientBuilder httpClientBuilder = null;
+ private String defaultConfigurationId = null;
+ private final Map<String, HttpClientBuilder> availableBuilders = new HashMap<>();
/*
* (non-Javadoc)
@@ -151,151 +109,145 @@ public class HttpClientFactory implements IHttpClientFactory {
@Override
public CloseableHttpClient getHttpClient(final boolean followRedirects) {
- RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
- if (!followRedirects) {
- redirectStrategy = new RedirectStrategy() {
+ return availableBuilders.get(defaultConfigurationId).setRedirectStrategy(
+ buildRedirectStrategy(followRedirects)).build();
- @Override
- public boolean isRedirected(final HttpRequest request, final HttpResponse response,
- final HttpContext context) throws ProtocolException {
- return false;
- }
+ }
- @Override
- public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response,
- final HttpContext context) throws ProtocolException {
- return null;
- }
- };
- }
+ @Override
+ public CloseableHttpClient getHttpClient(@Nonnull HttpClientConfiguration config) throws EaafException {
+ log.trace("Build http client for: {}", config.getFriendlyName());
+ HttpClientBuilder builder = null;
+ if (availableBuilders.containsKey(config.getUuid())) {
+ builder = availableBuilders.get(config.getUuid());
- return httpClientBuilder.setRedirectStrategy(redirectStrategy).build();
+ } else {
+ log.debug("Initialize new http-client builder for: {}", config.getFriendlyName());
- }
+ //validate configuration object
+ config.validate();
- @PostConstruct
- private void initalize() {
- // initialize http client
- log.trace("Initializing HTTP Client-builder ... ");
- httpClientBuilder = HttpClients.custom();
+ builder = HttpClients.custom();
+ builder.setDefaultRequestConfig(buildDefaultRequestConfig());
- // set default request configuration
- final RequestConfig requestConfig =
- RequestConfig.custom()
- .setConnectTimeout(
- Integer.parseInt(basicConfig.getBasicConfiguration(
- PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION,
- DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000)
- .setConnectionRequestTimeout(Integer.parseInt(basicConfig.getBasicConfiguration(
- PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST,
- DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000)
- .setSocketTimeout(Integer.parseInt(
- basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET,
- DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET))
- * 1000)
- .build();
- httpClientBuilder.setDefaultRequestConfig(requestConfig);
+ //inject basic authentication infos
+ injectBasicAuthenticationIfRequired(builder, config);
- ClientAuthMode clientAuthMode = ClientAuthMode.fromString(
- basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_MODE, ClientAuthMode.NONE.getMode()));
- if (clientAuthMode == null) {
- log.warn("Can Not parse ClientAuthMode! Set mode to default value");
- clientAuthMode = ClientAuthMode.NONE;
+ //inject authentication if required
+ final LayeredConnectionSocketFactory sslConnectionFactory = getSslContext(config);
- }
+ // set pool connection if required
+ injectDefaultConnectionPoolIfRequired(builder, sslConnectionFactory);
- // inject basic http authentication if required
- log.info("Client authentication-mode is set to: {}", clientAuthMode);
- injectBasicAuthenticationIfRequired(clientAuthMode);
+ availableBuilders.put(config.getUuid(), builder);
- // inject authentication if required
- final LayeredConnectionSocketFactory sslConnectionFactory = getSslContext(clientAuthMode);
+ }
- // set pool connection if required
- injectConnectionPoolIfRequired(sslConnectionFactory);
+ return builder.setRedirectStrategy(
+ buildRedirectStrategy(config.isFollowHttpRedirects())).build();
}
- private void injectBasicAuthenticationIfRequired(final ClientAuthMode clientAuthMode) {
- if (clientAuthMode.equals(ClientAuthMode.PASSWORD)) {
- final CredentialsProvider provider = new BasicCredentialsProvider();
+ @PostConstruct
+ private void initalize() throws EaafException {
+ final HttpClientConfiguration defaultHttpClientConfig = buildDefaultHttpClientConfiguration();
- final String username =
- basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME);
- final String password =
- basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD);
+ // initialize http client
+ log.trace("Initializing default HTTP-Client builder ... ");
+ final HttpClientBuilder defaultHttpClientBuilder = HttpClients.custom();
- if (StringUtils.isEmpty(username)) {
- log.warn("Http basic authentication was activated but NOT username was set!");
+ // set default request configuration
+ defaultHttpClientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig());
- }
+ //inject http basic authentication
+ injectBasicAuthenticationIfRequired(defaultHttpClientBuilder, defaultHttpClientConfig);
- 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);
+ // inject authentication if required
+ final LayeredConnectionSocketFactory sslConnectionFactory =
+ getSslContext(defaultHttpClientConfig);
- } else {
- log.trace("Injection of Http Basic authentication was skipped");
+ // set pool connection if required
+ injectDefaultConnectionPoolIfRequired(defaultHttpClientBuilder, sslConnectionFactory);
- }
+ //set default http client builder
+ defaultConfigurationId = defaultHttpClientConfig.getUuid();
+ availableBuilders.put(defaultConfigurationId, defaultHttpClientBuilder);
}
- 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_KEY_PASSWORD);
- 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 HttpClientConfiguration buildDefaultHttpClientConfiguration() throws EaafConfigurationException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("Default");
+ // inject basic http authentication if required
+ config.setAuthMode(basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_MODE,
+ HttpClientConfiguration.ClientAuthMode.NONE.getMode()));
+ log.info("Default client authentication-mode is set to: {}", config.getAuthMode());
+
+ // set Username and Password if available
+ config.setUsername(basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME));
+ config.setPassword(basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD));
+
+ // set SSL Client auth. informations if available
+ config.buildKeyStoreConfig(
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE, KeyStoreType.PKCS12.getKeyStoreType()),
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH, StringUtils.EMPTY),
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD, StringUtils.EMPTY),
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME, StringUtils.EMPTY));
+
+ config.setSslKeyAlias(
+ basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS));
+ config.setSslKeyPassword(
+ basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD));
+
+ config.setDisableHostnameValidation(basicConfig.getBasicConfigurationBoolean(
+ PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false));
+
+ // validate configuration object
+ config.validate();
+
+ return config;
}
- private KeyStore getSslAuthKeyStore() throws EaafConfigurationException {
- final String keyStoreType = basicConfig.getBasicConfiguration(
- PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE, KeyStoreType.PKCS12.getKeyStoreType());
- 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);
- final String keyStoreName = basicConfig
- .getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME, StringUtils.EMPTY);
-
- try {
- final KeyStoreConfiguration keyStoreConfig = new KeyStoreConfiguration();
- keyStoreConfig.setKeyStoreType(keyStoreType);
- keyStoreConfig.setFriendlyName("HttpClient Keystore");
- keyStoreConfig.setSoftKeyStoreFilePath(localKeyStorePath);
- keyStoreConfig.setSoftKeyStorePassword(keyStorePassword);
- keyStoreConfig.setKeyStoreName(keyStoreName);
-
- log.debug("Open keyStore with type: {}", keyStoreType);
- final KeyStore keyStore = keyStoreFactory.buildNewKeyStore(keyStoreConfig).getFirst();
+ private void injectBasicAuthenticationIfRequired(HttpClientBuilder builder,
+ final HttpClientConfiguration httpClientConfig) {
+ if (httpClientConfig.getAuthMode().equals(HttpClientConfiguration.ClientAuthMode.PASSWORD)) {
+ final CredentialsProvider provider = new BasicCredentialsProvider();
+ log.trace("Injecting basic authentication with username: {} and password: {}",
+ httpClientConfig.getUsername(), httpClientConfig.getPassword());
+ final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(
+ httpClientConfig.getUsername(), httpClientConfig.getPassword());
- return keyStore;
+ final AuthScope scope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM);
+ provider.setCredentials(scope, credentials);
+ builder.setDefaultCredentialsProvider(provider);
+ log.info("Basic http authentication was injected with username: {}",
+ httpClientConfig.getUsername());
- } catch (final EaafException 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);
+ } else {
+ log.trace("Injection of Http Basic authentication was skipped");
}
}
- private LayeredConnectionSocketFactory getSslContext(final ClientAuthMode clientAuthMode) {
+ @Nonnull
+ private LayeredConnectionSocketFactory getSslContext(final HttpClientConfiguration httpClientConfig)
+ throws EaafException {
SSLContext sslContext = null;
try {
- if (clientAuthMode.equals(ClientAuthMode.SSL)) {
- sslContext = buildSslContextWithSslClientAuthentication();
+ if (httpClientConfig.getAuthMode().equals(HttpClientConfiguration.ClientAuthMode.SSL)) {
+ log.debug("Open keyStore with type: {}", httpClientConfig.getKeyStoreConfig().getKeyStoreType());
+ final KeyStore keyStore = keyStoreFactory.buildNewKeyStore(httpClientConfig.getKeyStoreConfig())
+ .getFirst();
+
+ log.trace("Injecting SSL client-authentication into http client ... ");
+ sslContext = HttpUtils.buildSslContextWithSslClientAuthentication(keyStore,
+ httpClientConfig.getSslKeyAlias(), httpClientConfig.getSslKeyPassword(),
+ httpClientConfig.isDisableTlsHostCertificateValidation(), httpClientConfig.getFriendlyName());
} else {
log.trace("Initializing default SSL Context ... ");
@@ -305,8 +257,7 @@ public class HttpClientFactory implements IHttpClientFactory {
// set hostname verifier
HostnameVerifier hostnameVerifier = null;
- if (basicConfig.getBasicConfigurationBoolean(
- PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false)) {
+ if (httpClientConfig.isDisableHostnameValidation()) {
hostnameVerifier = new NoopHostnameVerifier();
log.warn("HTTP client-builder deactivates SSL Host-name verification!");
@@ -314,22 +265,20 @@ public class HttpClientFactory implements IHttpClientFactory {
final LayeredConnectionSocketFactory sslSocketFactory =
new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
-
+ log.debug("HTTP client-builder successfuly initialized");
return sslSocketFactory;
- } catch (final NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException
- | KeyStoreException | EaafConfigurationException e) {
+ } catch (final NoSuchAlgorithmException e) {
log.warn("HTTP client-builder can NOT initialze SSL-Context", e);
+ throw new EaafFactoryException(ERROR_03, new Object[] {
+ httpClientConfig.getFriendlyName(), e.getMessage()}, e);
}
- log.info("HTTP client-builder successfuly initialized");
- return null;
-
}
- private void injectConnectionPoolIfRequired(
- final LayeredConnectionSocketFactory sslConnectionFactory) {
+ private void injectDefaultConnectionPoolIfRequired(
+ HttpClientBuilder builder, final LayeredConnectionSocketFactory sslConnectionFactory) {
if (basicConfig.getBasicConfigurationBoolean(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE,
true)) {
PoolingHttpClientConnectionManager pool;
@@ -355,15 +304,56 @@ public class HttpClientFactory implements IHttpClientFactory {
basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL,
DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL)));
- httpClientBuilder.setConnectionManager(pool);
+ builder.setConnectionManager(pool);
log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(),
pool.getDefaultMaxPerRoute());
} else if (sslConnectionFactory != null) {
log.trace("Inject SSLSocketFactory without connection pool");
- httpClientBuilder.setSSLSocketFactory(sslConnectionFactory);
+ builder.setSSLSocketFactory(sslConnectionFactory);
+
+ }
+
+ }
+
+ private RequestConfig buildDefaultRequestConfig() {
+ final RequestConfig requestConfig =
+ RequestConfig.custom()
+ .setConnectTimeout(
+ Integer.parseInt(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000)
+ .setConnectionRequestTimeout(Integer.parseInt(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000)
+ .setSocketTimeout(Integer.parseInt(
+ basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET))
+ * 1000)
+ .build();
+ return requestConfig;
+
+ }
+
+ private static RedirectStrategy buildRedirectStrategy(final boolean followRedirects) {
+ RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+ if (!followRedirects) {
+ redirectStrategy = new RedirectStrategy() {
+
+ @Override
+ public boolean isRedirected(final HttpRequest request, final HttpResponse response,
+ final HttpContext context) throws ProtocolException {
+ return false;
+ }
+ @Override
+ public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response,
+ final HttpContext context) throws ProtocolException {
+ return null;
+ }
+ };
}
+ return redirectStrategy;
}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java
index 66356ba0..2d514912 100644
--- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java
@@ -16,14 +16,35 @@
* works that you distribute must include a readable copy of the "NOTICE" text file.
*/
-package at.gv.egiz.eaaf.core.impl.utils;
+package at.gv.egiz.eaaf.core.impl.http;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException;
+
import org.apache.commons.lang3.StringUtils;
+import org.apache.http.conn.ssl.TrustAllStrategy;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.ssl.TrustStrategy;
+
+import lombok.extern.slf4j.Slf4j;
+@Slf4j
public class HttpUtils {
+ private static final String ERROR_03 = "internal.httpclient.03";
+
/**
* Helper method to retrieve server URL including context path.
*
@@ -115,4 +136,59 @@ public class HttpUtils {
}
}
+ /**
+ * Initialize a {@link SSLContext} with a {@link KeyStore} that uses X509 Client
+ * authentication.
+ *
+ * @param keyStore KeyStore with private keys that should be
+ * used
+ * @param keyAlias Alias of the key that should be used. If
+ * the alias is null, than the first key that
+ * is found will be selected.
+ * @param keyPasswordString Password of the Key in this keystore
+ * @param trustAllServerCertificates Deactivate SSL server-certificate
+ * validation
+ * @param friendlyName FriendlyName of the http client for logging
+ * purposes
+ * @return {@link SSLContext} with X509 client authentication
+ * @throws EaafConfigurationException In case of a configuration error
+ * @throws EaafFactoryException In case of a {@link SSLContext}
+ * initialization error
+ */
+ public static SSLContext buildSslContextWithSslClientAuthentication(@Nonnull final KeyStore keyStore,
+ @Nullable String keyAlias, @Nullable String keyPasswordString,
+ boolean trustAllServerCertificates, @Nonnull String friendlyName)
+ throws EaafConfigurationException, EaafFactoryException {
+ try {
+ log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString);
+ final char[] keyPassword = keyPasswordString == null ? StringUtils.EMPTY.toCharArray()
+ : keyPasswordString.toCharArray();
+
+ SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+ if (StringUtils.isNotEmpty(keyAlias)) {
+ sslContextBuilder = sslContextBuilder
+ .loadKeyMaterial(keyStore, keyPassword, new EaafSslKeySelectionStrategy(keyAlias));
+
+ } else {
+ sslContextBuilder = sslContextBuilder
+ .loadKeyMaterial(keyStore, keyPassword);
+
+ }
+
+ if (trustAllServerCertificates) {
+ log.warn("Http-client:{} trusts ALL TLS server-certificates!");
+ final TrustStrategy trustStrategy = new TrustAllStrategy();
+ sslContextBuilder = sslContextBuilder.loadTrustMaterial(trustStrategy);
+
+ }
+
+ return sslContextBuilder.build();
+
+ } catch (NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException
+ | KeyStoreException e) {
+ throw new EaafFactoryException(ERROR_03, new Object[] { friendlyName, e.getMessage() }, e);
+
+ }
+ }
+
}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java
new file mode 100644
index 00000000..7ec58d46
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java
@@ -0,0 +1,43 @@
+package at.gv.egiz.eaaf.core.impl.http;
+
+import javax.annotation.Nonnull;
+
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+
+public interface IHttpClientFactory {
+
+ /**
+ * Return an instance of a Apache HTTP client that uses
+ * default configuration properties from {@link IHttpClientFactory} implementation
+ * and follows http redirects automatically.
+ *
+ * @return http client
+ */
+ @Nonnull
+ CloseableHttpClient getHttpClient();
+
+ /**
+ * Return an instance of a Apache HTTP client that uses
+ * default configuration properties from {@link IHttpClientFactory} implementation.
+ *
+ * @param followRedirects if <code>false</code>, the client does not flow 30x
+ * http redirects
+ * @return http client
+ */
+ @Nonnull
+ CloseableHttpClient getHttpClient(boolean followRedirects);
+
+ /**
+ * Return an instance of a Apache HTTP client based in {@link HttpClientConfiguration}.
+ *
+ * @param config Configuration object for this http client
+ * @return http client
+ * @throws EaafException In case of a http-client initialization problem
+ */
+ @Nonnull
+ CloseableHttpClient getHttpClient(@Nonnull HttpClientConfiguration config)
+ throws EaafException;
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java
deleted file mode 100644
index f922e1ac..00000000
--- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package at.gv.egiz.eaaf.core.impl.utils;
-
-import org.apache.http.impl.client.CloseableHttpClient;
-
-public interface IHttpClientFactory {
-
- /**
- * Return an instance of a Apache HTTP client that follows http redirects
- * automatically.
- *
- * @return
- */
- CloseableHttpClient getHttpClient();
-
- /**
- * Return an instance of a Apache HTTP client.
- *
- * @param followRedirects if <code>false</code>, the client does not flow 30x
- * http redirects
- * @return
- */
- CloseableHttpClient getHttpClient(boolean followRedirects);
-
-}
diff --git a/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties b/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties
index f531e02d..b20c5f63 100644
--- a/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties
+++ b/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties
@@ -12,4 +12,7 @@ internal.keystore.07=Validation of KeyStore: {0} failed. Reason: {1}
internal.keystore.08=Can not access Key: {1} in KeyStore: {0}
internal.keystore.09=Can not access Key: {1} in KeyStore: {0} Reason: {2}
-
+internal.httpclient.00=HttpClient:{0} uses http Basic-Auth, but 'Username' is NOT set
+internal.httpclient.01=HttpClient:{0} uses X509 client-auth, but 'KeyStoreConfig' is NOT set
+internal.httpclient.02=HttpClient:{0} uses KeyStore:{1}, but 'keyPassword' is NOT set
+internal.httpclient.03=Can not initialize SSLContext for HttpClient:{0} Reason:{1} \ No newline at end of file
diff --git a/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml b/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml
index ab631e34..aa5a50de 100644
--- a/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml
+++ b/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml
@@ -11,8 +11,8 @@
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" >
<bean id="eaafHttpClientFactory"
- class="at.gv.egiz.eaaf.core.impl.utils.HttpClientFactory" />
-
+ class="at.gv.egiz.eaaf.core.impl.http.HttpClientFactory" />
+
<bean id="eaafKeyStoreFactory"
class="at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory" />
diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java
new file mode 100644
index 00000000..b2f0f80e
--- /dev/null
+++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java
@@ -0,0 +1,328 @@
+package at.gv.egiz.eaaf.core.test.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.Provider;
+import java.security.cert.X509Certificate;
+
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.http.HttpClientConfiguration;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import okhttp3.HttpUrl;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import okhttp3.tls.HandshakeCertificates;
+import okhttp3.tls.HeldCertificate;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("/spring/test_eaaf_pvp_not_lazy.beans.xml")
+public class HttpClientFactoryTest {
+
+ @Autowired private EaafKeyStoreFactory keyStoreFactory;
+ @Autowired private IHttpClientFactory httpClientFactory;
+
+ private MockWebServer mockWebServer = null;
+ private HttpUrl mockServerUrl;
+
+ /**
+ * JUnit test set-up.
+ *
+ */
+ @Before
+ public void setup() {
+
+ }
+
+ /**
+ * jUnit test shutdown.
+ *
+ * @throws IOException In case of an mockWebServer error
+ */
+ @After
+ public void shutdown() throws IOException {
+ if (mockWebServer != null) {
+ mockWebServer.shutdown();
+ mockWebServer = null;
+
+ }
+
+ }
+
+ @Test
+ public void getDefaultClient() {
+ final CloseableHttpClient client = httpClientFactory.getHttpClient();
+ Assert.assertNotNull("httpClient", client);
+
+ }
+
+ @Test
+ public void getDefaultClientNoRedirect() {
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(false);
+ Assert.assertNotNull("httpClient", client);
+
+ }
+
+ @Test
+ public void getCustomClientsDefault() throws EaafException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ Assert.assertFalse("Wrong default config - Hostnamevalidation",
+ config.isDisableHostnameValidation());
+ Assert.assertFalse("Wrong default config - TLS Server-certs",
+ config.isDisableTlsHostCertificateValidation());
+
+ final CloseableHttpClient client1 = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("first http client", client1);
+
+ final CloseableHttpClient client2 = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("second http client", client2);
+
+ }
+
+ @Test
+ public void getCustomClientUnknownAuthMethod() throws EaafException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode(RandomStringUtils.randomAlphabetic(5));
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ }
+
+ @Test
+ public void getCustomClientBasicAuth() throws EaafException, ClientProtocolException, IOException, InterruptedException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("password");
+ config.setUsername("jUnit");
+ config.setPassword("password");
+
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ //setup test webserver that requestes http Basic authentication
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_UNAUTHORIZED)
+ .addHeader("www-authenticate: Basic realm=\"protected area\"")
+ .setBody("Please authenticate."));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("Successful auth!"));
+
+ //request webservice
+ final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString());
+ final CloseableHttpResponse httpResp2 = client.execute(httpGet2);
+ Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode());
+
+ //check request contains basic authentication after authentication was requested
+ final RecordedRequest httpReq1 = mockWebServer.takeRequest();
+ final RecordedRequest httpReq2 = mockWebServer.takeRequest();
+ Assert.assertNull("wrong BasicAuthHeader", httpReq1.getHeader("Authorization"));
+ Assert.assertNotNull("missing BasicAuthHeader", httpReq2.getHeader("Authorization"));
+
+ }
+
+ @Test
+ public void getCustomClientBasicAuthNoUsername(){
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("password");
+ try {
+ httpClientFactory.getHttpClient(config);
+ Assert.fail("Wrong config not detected");
+
+ } catch (final EaafException e) {
+ Assert.assertEquals("Wrong errorCode", "internal.httpclient.00", e.getErrorId());
+
+ }
+ }
+
+ @Test
+ public void getCustomClientBasicAuthNoPassword() throws EaafException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("password");
+ config.setUsername(RandomStringUtils.randomAlphabetic(5));
+
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ }
+
+ @Test
+ public void getCustomClientX509AuthNoKeyStoreConfig(){
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("ssl");
+ try {
+ httpClientFactory.getHttpClient(config);
+ Assert.fail("Wrong config not detected");
+
+ } catch (final EaafException e) {
+ Assert.assertEquals("Wrong errorCode", "internal.httpclient.01", e.getErrorId());
+
+ }
+ }
+
+ @Test
+ public void getCustomClientX509AuthNoKeyPassword() throws EaafException{
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("ssl");
+ config.buildKeyStoreConfig(
+ "jks",
+ "src/test/resources/data/junit.jks",
+ "password",
+ null);
+
+ try {
+ httpClientFactory.getHttpClient(config);
+ Assert.fail("Wrong config not detected");
+
+ } catch (final EaafException e) {
+ Assert.assertEquals("Wrong errorCode", "internal.httpclient.02", e.getErrorId());
+
+ }
+ }
+
+ @Test
+ public void getCustomClientX509Auth() throws EaafException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("ssl");
+ config.buildKeyStoreConfig(
+ "jks",
+ "src/test/resources/data/junit.jks",
+ "password",
+ null);
+ config.setSslKeyPassword("password");
+
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ }
+
+ @Test
+ public void getCustomClientX509AuthWithAlias() throws EaafException, ClientProtocolException,
+ IOException, KeyStoreException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("ssl");
+ config.buildKeyStoreConfig(
+ "jks",
+ "src/test/resources/data/junit.jks",
+ "password",
+ null);
+ config.setSslKeyPassword("password");
+ config.setSslKeyAlias("sig");
+ config.setDisableTlsHostCertificateValidation(true);
+
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ //set-up mock-up web-server with SSL client authentication
+ final Pair<KeyStore, Provider> sslClientKeyStore =
+ keyStoreFactory.buildNewKeyStore(config.getKeyStoreConfig());
+ final String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
+ final HeldCertificate localhostCertificate = new HeldCertificate.Builder()
+ .addSubjectAlternativeName(localhost)
+ .build();
+ final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()
+ .addTrustedCertificate(
+ (X509Certificate) sslClientKeyStore.getFirst().getCertificate(config.getSslKeyAlias()))
+ .heldCertificate(localhostCertificate)
+ .build();
+ mockWebServer = new MockWebServer();
+ mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false);
+ mockWebServer.requireClientAuth();
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("Successful auth!"));
+ mockServerUrl = mockWebServer.url("/sp/junit");
+
+ //perform test request
+ final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString());
+ final CloseableHttpResponse httpResp2 = client.execute(httpGet2);
+ Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode());
+
+ }
+
+ @Test
+ public void getCustomClientX509AuthWrongKeyPassword() throws EaafException{
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("ssl");
+ config.buildKeyStoreConfig(
+ "jks",
+ "src/test/resources/data/junit.jks",
+ "password",
+ null);
+ config.setSslKeyPassword(RandomStringUtils.randomAlphanumeric(5));
+ config.setSslKeyAlias("sig");
+
+ try {
+ httpClientFactory.getHttpClient(config);
+ Assert.fail("Wrong key password not detected");
+
+ } catch (final EaafException e) {
+ Assert.assertEquals("Wrong errorCode", "internal.httpclient.03", e.getErrorId());
+
+ }
+ }
+
+ @Test
+ public void getCustomClientX509AuthWithWrongAlias() throws EaafException, KeyStoreException, ClientProtocolException, IOException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setAuthMode("ssl");
+ config.buildKeyStoreConfig(
+ "jks",
+ "src/test/resources/data/junit.jks",
+ "password",
+ null);
+ config.setSslKeyPassword("password");
+ config.setSslKeyAlias(RandomStringUtils.randomAlphabetic(5));
+ config.setDisableHostnameValidation(true);
+ config.setFollowHttpRedirects(false);
+ config.setDisableTlsHostCertificateValidation(true);
+
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ //set-up mock-up web-server with SSL client authentication
+ final Pair<KeyStore, Provider> sslClientKeyStore =
+ keyStoreFactory.buildNewKeyStore(config.getKeyStoreConfig());
+ final String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
+ final HeldCertificate localhostCertificate = new HeldCertificate.Builder()
+ .addSubjectAlternativeName(localhost)
+ .build();
+ final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()
+ .addTrustedCertificate(
+ (X509Certificate) sslClientKeyStore.getFirst().getCertificate("meta"))
+ .heldCertificate(localhostCertificate)
+ .build();
+ mockWebServer = new MockWebServer();
+ mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false);
+ mockWebServer.requireClientAuth();
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("Successful auth!"));
+ mockServerUrl = mockWebServer.url("/sp/junit");
+
+ //perform test request
+ final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString());
+ final CloseableHttpResponse httpResp2 = client.execute(httpGet2);
+ Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode());
+
+ }
+}
diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java
index d1887d5c..1d97b167 100644
--- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java
+++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java
@@ -28,8 +28,8 @@ import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration;
import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException;
import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
-import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory;
import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils;
import at.gv.egiz.eaaf.core.impl.utils.Random;
import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils;
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java
index 147199a5..ee601c73 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java
@@ -11,10 +11,10 @@ import javax.xml.parsers.ParserConfigurationException;
import at.gv.egiz.eaaf.core.api.IRequest;
import at.gv.egiz.eaaf.core.api.gui.IVelocityGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
import at.gv.egiz.eaaf.core.impl.idp.module.gui.DummyGuiBuilderConfigurationFactory;
import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl;
import at.gv.egiz.eaaf.core.impl.utils.DomUtils;
-import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory;
import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential;
import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface;
import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider;
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java
index 37e4acd1..cbeca4c3 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java
@@ -6,8 +6,8 @@ import java.net.URLDecoder;
import javax.xml.parsers.ParserConfigurationException;
import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl;
-import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory;
import at.gv.egiz.eaaf.modules.pvp2.PvpConstants;
import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential;
import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface;
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java
index 64ebe00c..3673859a 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java
@@ -6,7 +6,7 @@ import java.util.ArrayList;
import java.util.List;
import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
-import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException;
import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.AbstractChainingMetadataProvider;
import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.PvpMetadataResolverFactory;
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java
index 6abe52dc..27c42c57 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java
@@ -5,8 +5,8 @@ import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
import at.gv.egiz.eaaf.core.impl.utils.FileUtils;
-import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory;
import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider;
import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException;
import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.PvpMetadataResolverFactory;
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java
index accdd8b0..da417ec7 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java
@@ -10,7 +10,7 @@ import java.util.List;
import javax.xml.transform.TransformerException;
-import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider;
import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException;
import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2InternalErrorException;
diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml b/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml
index 99552053..fa7b4eae 100644
--- a/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml
+++ b/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml
@@ -20,7 +20,7 @@
class="at.gv.egiz.eaaf.core.impl.idp.module.gui.DummyGuiBuilderConfigurationFactory" />
<bean id="httpClientFactory"
- class="at.gv.egiz.eaaf.core.impl.utils.HttpClientFactory" />
+ class="at.gv.egiz.eaaf.core.impl.http.HttpClientFactory" />
<bean id="dummyRevisionLogger"
class="at.gv.egiz.eaaf.core.impl.logging.DummyRevisionsLogger" />
diff --git a/pom.xml b/pom.xml
index 0ce897ec..773e0937 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,7 +78,7 @@
<!-- jUnit testing -->
<surefire.version>2.22.1</surefire.version>
<junit.version>4.12</junit.version>
- <com.squareup.okhttp3.version>4.0.0</com.squareup.okhttp3.version>
+ <com.squareup.okhttp3.version>4.4.1</com.squareup.okhttp3.version>
<!-- Code helper plug-ins -->
<org.projectlombok.lombok.version>1.18.10</org.projectlombok.lombok.version>
@@ -484,6 +484,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>okhttp-tls</artifactId>
+ <version>${com.squareup.okhttp3.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>at.gv.egiz.eaaf</groupId>
<artifactId>eaaf_core_utils</artifactId>
<version>${egiz.eaaf.version}</version>