diff --git a/services/src/main/java/org/keycloak/connections/httpclient/DefaultHttpClientFactory.java b/services/src/main/java/org/keycloak/connections/httpclient/DefaultHttpClientFactory.java index 7e6b4c16fe..f5b15c8f72 100755 --- a/services/src/main/java/org/keycloak/connections/httpclient/DefaultHttpClientFactory.java +++ b/services/src/main/java/org/keycloak/connections/httpclient/DefaultHttpClientFactory.java @@ -144,15 +144,8 @@ public class DefaultHttpClientFactory implements HttpClientFactory { String clientKeystorePassword = config.get("client-keystore-password"); String clientPrivateKeyPassword = config.get("client-key-password"); String[] proxyMappings = config.getArray("proxy-mappings"); - - TruststoreProvider truststoreProvider = session.getProvider(TruststoreProvider.class); - boolean disableTrustManager = truststoreProvider == null || truststoreProvider.getTruststore() == null; - if (disableTrustManager) { - logger.warn("Truststore is disabled"); - } - HttpClientBuilder.HostnameVerificationPolicy hostnamePolicy = disableTrustManager ? null - : HttpClientBuilder.HostnameVerificationPolicy.valueOf(truststoreProvider.getPolicy().name()); - + boolean disableTrustManager = config.getBoolean("disable-trust-manager", false); + HttpClientBuilder builder = new HttpClientBuilder(); builder.socketTimeout(socketTimeout, TimeUnit.MILLISECONDS) @@ -164,11 +157,13 @@ public class DefaultHttpClientFactory implements HttpClientFactory { .disableCookies(disableCookies) .proxyMappings(ProxyMappings.valueOf(proxyMappings)); - if (disableTrustManager) { - // TODO: is it ok to do away with disabling trust manager? - //builder.disableTrustManager(); + TruststoreProvider truststoreProvider = session.getProvider(TruststoreProvider.class); + boolean disableTruststoreProvider = truststoreProvider == null || truststoreProvider.getTruststore() == null; + + if (disableTruststoreProvider) { + logger.warn("TruststoreProvider is disabled"); } else { - builder.hostnameVerification(hostnamePolicy); + builder.hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.valueOf(truststoreProvider.getPolicy().name())); try { builder.trustStore(truststoreProvider.getTruststore()); } catch (Exception e) { @@ -176,6 +171,11 @@ public class DefaultHttpClientFactory implements HttpClientFactory { } } + if (disableTrustManager) { + logger.warn("TrustManager is disabled"); + builder.disableTrustManager(); + } + if (clientKeystore != null) { clientKeystore = EnvUtil.replace(clientKeystore); try { diff --git a/services/src/test/java/org/keycloak/connections/httpclient/DefaultHttpClientFactoryTest.java b/services/src/test/java/org/keycloak/connections/httpclient/DefaultHttpClientFactoryTest.java new file mode 100644 index 0000000000..1b58c2730f --- /dev/null +++ b/services/src/test/java/org/keycloak/connections/httpclient/DefaultHttpClientFactoryTest.java @@ -0,0 +1,119 @@ +/* + * Copyright 2019 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.connections.httpclient; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +import javax.net.ssl.SSLPeerUnverifiedException; + +import org.apache.commons.lang.StringUtils; +import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.Assume; +import org.junit.Test; +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.DefaultKeycloakSession; +import org.keycloak.services.DefaultKeycloakSessionFactory; +import org.keycloak.services.util.JsonConfigProvider; +import org.keycloak.services.util.JsonConfigProvider.JsonScope; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class DefaultHttpClientFactoryTest { + private static final String DISABLE_TRUST_MANAGER_PROPERTY = "disable-trust-manager"; + private static final String TEST_DOMAIN = "www.google.com"; + + @Test + public void createHttpClientProviderWithDisableTrustManager() throws ClientProtocolException, IOException{ + Map values = new HashMap<>(); + values.put(DISABLE_TRUST_MANAGER_PROPERTY, "true"); + DefaultHttpClientFactory factory = new DefaultHttpClientFactory(); + factory.init(scope(values)); + KeycloakSession session = new DefaultKeycloakSession(new DefaultKeycloakSessionFactory()); + HttpClientProvider provider = factory.create(session); + CloseableHttpResponse response; + try(CloseableHttpClient httpClient = (CloseableHttpClient) provider.getHttpClient()){ + Optional testURL = getTestURL(); + Assume.assumeTrue( "Could not get test url for domain", testURL.isPresent() ); + response = httpClient.execute(new HttpGet(testURL.get())); + } + assertEquals(HttpStatus.SC_OK,response.getStatusLine().getStatusCode()); + } + + @Test(expected = SSLPeerUnverifiedException.class) + public void createHttpClientProviderWithUnvailableURL() throws ClientProtocolException, IOException { + DefaultHttpClientFactory factory = new DefaultHttpClientFactory(); + factory.init(scope(new HashMap<>())); + KeycloakSession session = new DefaultKeycloakSession(new DefaultKeycloakSessionFactory()); + HttpClientProvider provider = factory.create(session); + try (CloseableHttpClient httpClient = (CloseableHttpClient) provider.getHttpClient()) { + Optional testURL = getTestURL(); + Assume.assumeTrue("Could not get test url for domain", testURL.isPresent()); + httpClient.execute(new HttpGet(testURL.get())); + } + } + + private JsonScope scope(Map properties) { + ObjectMapper mapper = new ObjectMapper(); + try { + JsonNode config = mapper.readTree(json(properties)); + return new JsonConfigProvider(config,new Properties()).new JsonScope(config); + } catch (IOException e) { + fail("Could not parse json"); + } + return null; + } + + private String json(Map properties) { + String[] params = properties.entrySet().stream().map(e -> param(e.getKey(), e.getValue())).toArray(String[]::new); + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + sb.append(StringUtils.join(params, ',')); + sb.append("}"); + + return sb.toString(); + } + + private String param(String key, String value) { + return "\"" + key + "\"" + " : " + "\"" + value + "\""; + } + + private Optional getTestURL() { + try { + // Convert domain name to ip to make request by ip + return Optional.of("https://" + InetAddress.getByName(TEST_DOMAIN).getHostAddress()); + } catch (UnknownHostException e) { + return Optional.empty(); + } + } + +}