diff --git a/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpClientSteps.java b/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpClientSteps.java index 9561d06c..ee7d3fa1 100644 --- a/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpClientSteps.java +++ b/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpClientSteps.java @@ -21,9 +21,10 @@ import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.HashMap; import java.util.Map; -import javax.net.ssl.SSLContext; import io.cucumber.datatable.DataTable; import io.cucumber.java.Before; @@ -33,12 +34,14 @@ import io.cucumber.java.en.When; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.client5.http.ssl.TrustAllStrategy; +import org.apache.hc.core5.ssl.SSLContextBuilder; import org.apache.hc.core5.ssl.SSLContexts; import org.citrusframework.Citrus; import org.citrusframework.CitrusSettings; @@ -55,12 +58,13 @@ import org.citrusframework.http.client.HttpClientBuilder; import org.citrusframework.http.message.HttpMessage; import org.citrusframework.util.FileUtils; +import org.citrusframework.util.StringUtils; import org.citrusframework.variable.dictionary.DataDictionary; +import org.citrusframework.yaks.YaksSettings; import org.citrusframework.yaks.util.ResourceUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMethod; import static org.citrusframework.TestActionBuilder.logger; @@ -110,6 +114,10 @@ public class HttpClientSteps implements HttpSteps { private String authUser = HttpSettings.getClientAuthUser(); private String authPassword = HttpSettings.getClientAuthPassword(); + private boolean useSslKeyStore = HttpSettings.isUseSslKeyStore(); + private String sslKeyStorePath = HttpSettings.getSslKeyStorePath(); + private String sslKeyStorePassword = HttpSettings.getSslKeyStorePassword(); + @Before public void before(Scenario scenario) { if (httpClient == null) { @@ -156,6 +164,22 @@ public void setUrl(String url) { this.requestUrl = resolvedUrl; } + @Given("^HTTP client (enable|disable) SSL keystore$") + public void setSecureKeyStore(String mode) { + this.useSslKeyStore = "enable".equals(mode); + } + + @Given("^HTTP client SSL keystore path ([^\\s]+)$") + public void setSslKeyStorePath(String sslKeyStorePath) { + this.sslKeyStorePath = sslKeyStorePath; + this.useSslKeyStore = true; + } + + @Given("^HTTP client SSL keystore password ([^\\s]+)$") + public void setSslKeyStorePassword(String sslKeyStorePassword) { + this.sslKeyStorePassword = sslKeyStorePassword; + } + @Given("^HTTP client (enable|disable) basic auth$") public void setBasicAuth(String mode) { if ("enable".equals(mode)) { @@ -438,13 +462,17 @@ private HttpComponentsClientHttpRequestFactory sslRequestFactory() { */ private org.apache.hc.client5.http.classic.HttpClient sslClient() { try { - SSLContext sslcontext = SSLContexts + SSLContextBuilder sslContextBuilder = SSLContexts .custom() - .loadTrustMaterial(TrustAllStrategy.INSTANCE) - .build(); + .loadTrustMaterial(TrustAllStrategy.INSTANCE); + + if (useSslKeyStore && StringUtils.hasText(sslKeyStorePath)) { + sslContextBuilder.loadKeyMaterial(ResourceUtils.resolve(sslKeyStorePath, context).getURL(), + sslKeyStorePassword.toCharArray(), sslKeyStorePassword.toCharArray()); + } SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( - sslcontext, NoopHostnameVerifier.INSTANCE); + sslContextBuilder.build(), NoopHostnameVerifier.INSTANCE); PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory(sslSocketFactory) @@ -453,7 +481,7 @@ private org.apache.hc.client5.http.classic.HttpClient sslClient() { return HttpClients.custom() .setConnectionManager(connectionManager) .build(); - } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { + } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException | UnrecoverableKeyException | CertificateException e) { throw new CitrusRuntimeException("Failed to create http client for ssl connection", e); } } diff --git a/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpServerSteps.java b/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpServerSteps.java index 80a1330b..443dd1b8 100644 --- a/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpServerSteps.java +++ b/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpServerSteps.java @@ -49,6 +49,7 @@ import org.citrusframework.http.server.HttpServer; import org.citrusframework.http.server.HttpServerBuilder; import org.citrusframework.util.FileUtils; +import org.citrusframework.util.StringUtils; import org.citrusframework.variable.dictionary.DataDictionary; import org.citrusframework.yaks.util.ResourceUtils; import org.eclipse.jetty.http.HttpVersion; @@ -83,8 +84,6 @@ public class HttpServerSteps implements HttpSteps { private HttpServer httpServer; - private ServerConnector sslConnector; - private Map requestHeaders = new HashMap<>(); private Map responseHeaders = new HashMap<>(); private Map requestParams = new HashMap<>(); @@ -106,6 +105,9 @@ public class HttpServerSteps implements HttpSteps { private int serverPort = HttpSettings.getServerPort(); private String serverName = HttpSettings.getServerName(); + private boolean useSslConnector = HttpSettings.isUseSslConnector(); + private boolean useSslKeyStore = HttpSettings.isUseSslKeyStore(); + private String sslKeyStorePath = HttpSettings.getSslKeyStorePath(); private String sslKeyStorePassword = HttpSettings.getSslKeyStorePassword(); @@ -142,7 +144,6 @@ public void before(Scenario scenario) { bodyValidationExpressions = new HashMap<>(); outboundDictionary = null; inboundDictionary = null; - sslConnector = null; } @Given("^HTTP server \"([^\"\\s]+)\"$") @@ -227,16 +228,21 @@ public void setAuthPassword(String authPassword) { @Given("^HTTP server (enable|disable) SSL$") public void setSecureConnector(String mode) { - if ("enable".equals(mode)) { - this.sslConnector = sslConnector(); - } else { - this.sslConnector = null; + useSslConnector = "enable".equals(mode); + if (useSslConnector) { + this.useSslKeyStore = true; } } + @Given("^HTTP server (enable|disable) SSL keystore$") + public void setSecureKeyStore(String mode) { + this.useSslKeyStore = "enable".equals(mode); + } + @Given("^HTTP server SSL keystore path ([^\\s]+)$") public void setSslKeyStorePath(String sslKeyStorePath) { this.sslKeyStorePath = sslKeyStorePath; + this.useSslKeyStore = true; } @Given("^HTTP server SSL keystore password ([^\\s]+)$") @@ -454,8 +460,8 @@ public HttpServer getOrCreateHttpServer() { .name(serverName) .build(); - if (sslConnector != null) { - httpServer.setConnector(sslConnector); + if (useSslConnector) { + httpServer.setConnector(sslConnector()); } if ("basic".equals(authMethod)) { @@ -555,8 +561,10 @@ private HttpConfiguration httpConfiguration() { private SslContextFactory.Server sslContextFactory() { try { SslContextFactory.Server contextFactory = new SslContextFactory.Server(); - contextFactory.setKeyStorePath(getKeyStorePathPath()); - contextFactory.setKeyStorePassword(context.replaceDynamicContentInString(sslKeyStorePassword)); + if (useSslKeyStore && StringUtils.hasText(sslKeyStorePath)) { + contextFactory.setKeyStorePath(getKeyStorePathPath()); + contextFactory.setKeyStorePassword(context.replaceDynamicContentInString(sslKeyStorePassword)); + } return contextFactory; } catch (IOException e) { throw new CitrusRuntimeException("Failed to read keystore file in path: " + sslKeyStorePath); diff --git a/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpSettings.java b/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpSettings.java index b37dedec..2a5d4305 100644 --- a/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpSettings.java +++ b/java/steps/yaks-http/src/main/java/org/citrusframework/yaks/http/HttpSettings.java @@ -90,6 +90,14 @@ public class HttpSettings { private static final String SECURE_KEYSTORE_PASSWORD_ENV = HTTP_ENV_PREFIX + "SECURE_KEYSTORE_PASSWORD"; private static final String SECURE_KEYSTORE_PASSWORD_DEFAULT = "secret"; + private static final String USE_SECURE_CONNECTOR_PROPERTY = HTTP_PROPERTY_PREFIX + "use.secure.connector"; + private static final String USE_SECURE_CONNECTOR_ENV = HTTP_ENV_PREFIX + "USE_SECURE_CONNECTOR"; + private static final String USE_SECURE_CONNECTOR_DEFAULT = "false"; + + private static final String USE_SECURE_KEYSTORE_PROPERTY = HTTP_PROPERTY_PREFIX + "use.secure.keystore"; + private static final String USE_SECURE_KEYSTORE_ENV = HTTP_ENV_PREFIX + "USE_SECURE_KEYSTORE"; + private static final String USE_SECURE_KEYSTORE_DEFAULT = "false"; + private static final String HEADER_NAME_IGNORE_CASE_PROPERTY = HTTP_PROPERTY_PREFIX + "header.name.ignore.case"; private static final String HEADER_NAME_IGNORE_CASE_ENV = HTTP_ENV_PREFIX + "HEADER_NAME_IGNORE_CASE"; private static final String HEADER_NAME_IGNORE_CASE_DEFAULT = "false"; @@ -143,6 +151,18 @@ public static int getSecurePort() { System.getenv(SECURE_PORT_ENV) != null ? System.getenv(SECURE_PORT_ENV) : SECURE_PORT_DEFAULT)); } + public static boolean isUseSslConnector() { + return Boolean.parseBoolean(System.getProperty(USE_SECURE_CONNECTOR_PROPERTY, + System.getenv(USE_SECURE_CONNECTOR_ENV) != null ? System.getenv(USE_SECURE_CONNECTOR_ENV) : + USE_SECURE_CONNECTOR_DEFAULT)); + } + + public static boolean isUseSslKeyStore() { + return Boolean.parseBoolean(System.getProperty(USE_SECURE_KEYSTORE_PROPERTY, + System.getenv(USE_SECURE_KEYSTORE_ENV) != null ? System.getenv(USE_SECURE_KEYSTORE_ENV) : + USE_SECURE_KEYSTORE_DEFAULT)); + } + /** * SSL key store path. * @return