diff --git a/spring-cloud-config-dependencies/pom.xml b/spring-cloud-config-dependencies/pom.xml index 5e8ba76a..fbe60054 100644 --- a/spring-cloud-config-dependencies/pom.xml +++ b/spring-cloud-config-dependencies/pom.xml @@ -48,6 +48,11 @@ org.eclipse.jgit.junit.http ${jgit.version} + + org.eclipse.jgit + org.eclipse.jgit.http.apache + ${jgit.version} + com.jcraft jsch diff --git a/spring-cloud-config-server/pom.xml b/spring-cloud-config-server/pom.xml index 86ab7029..d6cb96b4 100644 --- a/spring-cloud-config-server/pom.xml +++ b/spring-cloud-config-server/pom.xml @@ -51,6 +51,10 @@ org.eclipse.jgit org.eclipse.jgit + + org.eclipse.jgit + org.eclipse.jgit.http.apache + org.yaml snakeyaml diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java index ad766d58..538d7713 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java @@ -15,20 +15,12 @@ */ package org.springframework.cloud.config.server.config; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.Optional; -import javax.net.ssl.SSLContext; import javax.servlet.http.HttpServletRequest; import org.apache.http.client.HttpClient; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.ssl.SSLContextBuilder; import org.eclipse.jgit.api.TransportConfigCallback; import org.tmatesoft.svn.core.SVNException; @@ -47,6 +39,9 @@ import org.springframework.cloud.config.server.environment.CompositeEnvironmentR import org.springframework.cloud.config.server.environment.ConsulEnvironmentWatch; import org.springframework.cloud.config.server.environment.EnvironmentRepository; import org.springframework.cloud.config.server.environment.EnvironmentWatch; +import org.springframework.cloud.config.server.environment.HttpClientConfigurableHttpConnectionFactory; +import org.springframework.cloud.config.server.environment.HttpClientVaultRestTemplateFactory; +import org.springframework.cloud.config.server.environment.ConfigurableHttpConnectionFactory; import org.springframework.cloud.config.server.environment.JdbcEnvironmentProperties; import org.springframework.cloud.config.server.environment.JdbcEnvironmentRepository; import org.springframework.cloud.config.server.environment.JdbcEnvironmentRepositoryFactory; @@ -70,9 +65,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Profile; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.client.RestTemplate; /** * @author Dave Syer @@ -124,11 +117,24 @@ public class EnvironmentRepositoryConfiguration { @Configuration @ConditionalOnClass(TransportConfigCallback.class) static class JGitFactoryConfig { + @Bean public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory( ConfigurableEnvironment environment, ConfigServerProperties server, + Optional jgitHttpConnectionFactory, Optional customTransportConfigCallback) { - return new MultipleJGitEnvironmentRepositoryFactory(environment, server, customTransportConfigCallback); + return new MultipleJGitEnvironmentRepositoryFactory(environment, server, jgitHttpConnectionFactory, + customTransportConfigCallback); + } + } + + @Configuration + @ConditionalOnClass({ HttpClient.class, TransportConfigCallback.class }) + static class JGitHttpClientConfig { + + @Bean + public ConfigurableHttpConnectionFactory httpClientConnectionFactory() { + return new HttpClientConfigurableHttpConnectionFactory(); } } @@ -144,17 +150,28 @@ public class EnvironmentRepositoryConfiguration { @Configuration static class VaultFactoryConfig { + @Bean public VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory( ObjectProvider request, EnvironmentWatch watch, - Optional skipSslValidationRestTemplate) { - return new VaultEnvironmentRepositoryFactory(request, watch, skipSslValidationRestTemplate); + Optional vaultRestTemplateFactory) { + return new VaultEnvironmentRepositoryFactory(request, watch, vaultRestTemplateFactory); + } + } + + @Configuration + @ConditionalOnClass(HttpClient.class) + static class VaultHttpClientConfig { + + @Bean + public VaultEnvironmentRepositoryFactory.VaultRestTemplateFactory vaultRestTemplateFactory() { + return new HttpClientVaultRestTemplateFactory(); } } @Configuration @ConditionalOnClass(JdbcTemplate.class) - static class JdbcCompositeConfig { + static class JdbcFactoryConfig { @Bean @ConditionalOnBean(JdbcTemplate.class) public JdbcEnvironmentRepositoryFactory jdbcEnvironmentRepositoryFactory(JdbcTemplate jdbc) { @@ -171,24 +188,6 @@ public class EnvironmentRepositoryConfiguration { } } - @Bean - @ConditionalOnClass(HttpClient.class) - public RestTemplate skipSslValidationRestTemplate() { - try { - SSLContext sslContext = new SSLContextBuilder() - .loadTrustMaterial(null, (certificate, authType) -> true) - .build(); - CloseableHttpClient httpClient = HttpClients.custom() - .setSSLContext(sslContext) - .setSSLHostnameVerifier(new NoopHostnameVerifier()) - .build(); - HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); - requestFactory.setHttpClient(httpClient); - return new RestTemplate(requestFactory); - } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { - throw new RuntimeException(e); - } - } } @Configuration @@ -206,7 +205,7 @@ class DefaultRepositoryConfiguration { @Bean public MultipleJGitEnvironmentRepository defaultEnvironmentRepository( MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory, - MultipleJGitEnvironmentProperties environmentProperties) { + MultipleJGitEnvironmentProperties environmentProperties) throws Exception { return gitEnvironmentRepositoryFactory.build(environmentProperties); } } @@ -245,7 +244,8 @@ class VaultRepositoryConfiguration { @Bean public VaultEnvironmentRepository vaultEnvironmentRepository(VaultEnvironmentRepositoryFactory factory, - VaultEnvironmentProperties environmentProperties) { + VaultEnvironmentProperties environmentProperties) + throws Exception { return factory.build(environmentProperties); } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/ConfigurableHttpConnectionFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/ConfigurableHttpConnectionFactory.java new file mode 100644 index 00000000..2b162c4f --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/ConfigurableHttpConnectionFactory.java @@ -0,0 +1,25 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.environment; + +import org.eclipse.jgit.transport.http.HttpConnectionFactory; + +/** + * @author Dylan Roberts + */ +public interface ConfigurableHttpConnectionFactory extends HttpConnectionFactory { + void addConfiguration(MultipleJGitEnvironmentProperties environmentProperties) throws Exception; +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/HttpClientConfigurableHttpConnectionFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/HttpClientConfigurableHttpConnectionFactory.java new file mode 100644 index 00000000..a163a0ac --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/HttpClientConfigurableHttpConnectionFactory.java @@ -0,0 +1,74 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.environment; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.http.impl.client.HttpClientBuilder; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.transport.http.apache.HttpClientConnection; + +import org.springframework.cloud.config.server.support.HttpClientSupport; + +/** + * @author Dylan Roberts + */ +public class HttpClientConfigurableHttpConnectionFactory implements ConfigurableHttpConnectionFactory { + + private Map httpClientsByUri = new HashMap<>(); + + @Override + public void addConfiguration(MultipleJGitEnvironmentProperties environmentProperties) + throws GeneralSecurityException { + addHttpClient(environmentProperties); + for (JGitEnvironmentProperties repo : environmentProperties.getRepos().values()) { + addHttpClient(repo); + } + } + + @Override + public HttpConnection create(URL url) throws IOException { + return create(url, null); + } + + @Override + public HttpConnection create(URL url, Proxy proxy) throws IOException { + return new HttpClientConnection(url.toString(), proxy, lookupHttpClientBuilder(url).build()); + } + + private void addHttpClient(JGitEnvironmentProperties properties) throws GeneralSecurityException { + if (properties.getUri().startsWith("http")) { + httpClientsByUri.put(properties.getUri(), HttpClientSupport.builder(properties)); + } + } + + private HttpClientBuilder lookupHttpClientBuilder(URL url) throws MalformedURLException { + String spec = url.toString(); + HttpClientBuilder builder = httpClientsByUri.get(spec); + while (builder == null) { + spec = spec.substring(0, spec.lastIndexOf(File.separator)); + builder = httpClientsByUri.get(spec); + } + return builder; + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/HttpClientVaultRestTemplateFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/HttpClientVaultRestTemplateFactory.java new file mode 100644 index 00000000..dab95a6d --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/HttpClientVaultRestTemplateFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.environment; + +import java.security.GeneralSecurityException; + +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.CloseableHttpClient; + +import org.springframework.cloud.config.server.support.HttpClientSupport; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * @author Dylan Roberts + */ +public class HttpClientVaultRestTemplateFactory implements VaultEnvironmentRepositoryFactory.VaultRestTemplateFactory { + + @Override + public RestTemplate build(VaultEnvironmentProperties environmentProperties) throws GeneralSecurityException { + HttpClient httpClient = HttpClientSupport.builder(environmentProperties).build(); + return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient)); + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java index 5dcacd7c..3598f62b 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java @@ -15,15 +15,20 @@ */ package org.springframework.cloud.config.server.environment; +import java.util.HashMap; +import java.util.Map; import javax.validation.constraints.Pattern; +import org.springframework.cloud.config.server.proxy.ProxyHostProperties; import org.springframework.cloud.config.server.support.AbstractScmAccessorProperties; +import org.springframework.cloud.config.server.support.HttpEnvironmentRepositoryProperties; /** * @author Dylan Roberts * @author Gareth Clay */ -public class JGitEnvironmentProperties extends AbstractScmAccessorProperties { +public class JGitEnvironmentProperties extends AbstractScmAccessorProperties + implements HttpEnvironmentRepositoryProperties { private static final String DEFAULT_LABEL = "master"; /** Flag to indicate that the repository should be cloned on startup (not on demand). Generally leads to slower startup but faster first query. */ @@ -85,6 +90,11 @@ public class JGitEnvironmentProperties extends AbstractScmAccessorProperties { */ private boolean strictHostKeyChecking = true; + /** + * HTTP proxy configuration. + */ + private Map proxy = new HashMap<>(); + public JGitEnvironmentProperties() { super(); setDefaultLabel(DEFAULT_LABEL); @@ -188,6 +198,7 @@ public class JGitEnvironmentProperties extends AbstractScmAccessorProperties { this.strictHostKeyChecking = strictHostKeyChecking; } + @Override public boolean isSkipSslValidation() { return skipSslValidation; } @@ -195,4 +206,13 @@ public class JGitEnvironmentProperties extends AbstractScmAccessorProperties { public void setSkipSslValidation(boolean skipSslValidation) { this.skipSslValidation = skipSslValidation; } + + @Override + public Map getProxy() { + return proxy; + } + + public void setProxy(Map proxy) { + this.proxy = proxy; + } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryFactory.java index a01aaa9b..80f5e922 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryFactory.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryFactory.java @@ -18,6 +18,7 @@ package org.springframework.cloud.config.server.environment; import java.util.Optional; import org.eclipse.jgit.api.TransportConfigCallback; +import org.eclipse.jgit.transport.HttpTransport; import org.springframework.cloud.config.server.config.ConfigServerProperties; import org.springframework.cloud.config.server.ssh.FileBasedSshTransportConfigCallback; @@ -31,17 +32,33 @@ public class MultipleJGitEnvironmentRepositoryFactory implements EnvironmentRepo MultipleJGitEnvironmentProperties> { private ConfigurableEnvironment environment; private ConfigServerProperties server; + private Optional connectionFactory; private Optional customTransportConfigCallback; - public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties server, - Optional customTransportConfigCallback) { + @Deprecated + public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties server, + Optional customTransportConfigCallback) { + this(environment, server, Optional.empty(), customTransportConfigCallback); + } + + public MultipleJGitEnvironmentRepositoryFactory( + ConfigurableEnvironment environment, ConfigServerProperties server, + Optional connectionFactory, + Optional customTransportConfigCallback) { this.environment = environment; this.server = server; + this.connectionFactory = connectionFactory; this.customTransportConfigCallback = customTransportConfigCallback; } @Override - public MultipleJGitEnvironmentRepository build(MultipleJGitEnvironmentProperties environmentProperties) { + public MultipleJGitEnvironmentRepository build(MultipleJGitEnvironmentProperties environmentProperties) + throws Exception { + if (connectionFactory.isPresent()) { + HttpTransport.setConnectionFactory(connectionFactory.get()); + connectionFactory.get().addConfiguration(environmentProperties); + } + MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(environment, environmentProperties); repository.setTransportConfigCallback(customTransportConfigCallback @@ -52,10 +69,11 @@ public class MultipleJGitEnvironmentRepositoryFactory implements EnvironmentRepo return repository; } - private TransportConfigCallback buildTransportConfigCallback(final MultipleJGitEnvironmentProperties gitEnvironmentProperties) { + private TransportConfigCallback buildTransportConfigCallback(MultipleJGitEnvironmentProperties gitEnvironmentProperties) { if (gitEnvironmentProperties.isIgnoreLocalSshSettings()) { return new PropertiesBasedSshTransportConfigCallback(gitEnvironmentProperties); } return new FileBasedSshTransportConfigCallback(gitEnvironmentProperties); } + } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java index 3ae680e1..76dda1fc 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java @@ -15,15 +15,19 @@ */ package org.springframework.cloud.config.server.environment; +import java.util.HashMap; +import java.util.Map; + import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties; +import org.springframework.cloud.config.server.proxy.ProxyHostProperties; +import org.springframework.cloud.config.server.support.HttpEnvironmentRepositoryProperties; import org.springframework.core.Ordered; /** * @author Dylan Roberts */ @ConfigurationProperties("spring.cloud.config.server.vault") -public class VaultEnvironmentProperties implements EnvironmentRepositoryProperties { +public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProperties { /** Vault host. Defaults to 127.0.0.1. */ private String host = "127.0.0.1"; /** Vault port. Defaults to 8200. */ @@ -41,6 +45,12 @@ public class VaultEnvironmentProperties implements EnvironmentRepositoryProperti * over an HTTPS connection. */ private boolean skipSslValidation = false; + + /** + * HTTP proxy configuration. + */ + private Map proxy = new HashMap<>(); + private int order = Ordered.LOWEST_PRECEDENCE; public String getHost() { @@ -91,6 +101,7 @@ public class VaultEnvironmentProperties implements EnvironmentRepositoryProperti this.profileSeparator = profileSeparator; } + @Override public boolean isSkipSslValidation() { return skipSslValidation; } @@ -99,6 +110,15 @@ public class VaultEnvironmentProperties implements EnvironmentRepositoryProperti this.skipSslValidation = skipSslValidation; } + @Override + public Map getProxy() { + return proxy; + } + + public void setProxy(Map proxy) { + this.proxy = proxy; + } + public int getOrder() { return order; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryFactory.java index 6128d5dd..90659fb5 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryFactory.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryFactory.java @@ -28,21 +28,26 @@ public class VaultEnvironmentRepositoryFactory implements EnvironmentRepositoryF VaultEnvironmentProperties> { private ObjectProvider request; private EnvironmentWatch watch; - private Optional skipSslValidationRestTemplate; + private Optional vaultRestTemplateFactory; public VaultEnvironmentRepositoryFactory(ObjectProvider request, EnvironmentWatch watch, - Optional skipSslValidationRestTemplate) { + Optional vaultRestTemplateFactory) { this.request = request; this.watch = watch; - this.skipSslValidationRestTemplate = skipSslValidationRestTemplate; + this.vaultRestTemplateFactory = vaultRestTemplateFactory; } @Override - public VaultEnvironmentRepository build(VaultEnvironmentProperties environmentProperties) { - if (environmentProperties.isSkipSslValidation() && skipSslValidationRestTemplate.isPresent()) { - return new VaultEnvironmentRepository(request, watch, skipSslValidationRestTemplate.get(), - environmentProperties); + public VaultEnvironmentRepository build(VaultEnvironmentProperties environmentProperties) throws Exception { + if (vaultRestTemplateFactory.isPresent()) { + RestTemplate restTemplate = vaultRestTemplateFactory.get().build(environmentProperties); + return new VaultEnvironmentRepository(request, watch, restTemplate, environmentProperties); } return new VaultEnvironmentRepository(request, watch, new RestTemplate(), environmentProperties); } + + public interface VaultRestTemplateFactory { + + RestTemplate build(VaultEnvironmentProperties environmentProperties) throws Exception; + } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/ProxyHostCredentialsProvider.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/ProxyHostCredentialsProvider.java new file mode 100644 index 00000000..a6cfb140 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/ProxyHostCredentialsProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.proxy; + +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.impl.client.BasicCredentialsProvider; + +/** + * @author Dylan Roberts + */ +public class ProxyHostCredentialsProvider extends BasicCredentialsProvider { + + public ProxyHostCredentialsProvider(ProxyHostProperties... proxyHostProperties) { + + for (ProxyHostProperties proxy : proxyHostProperties) { + + if (proxy != null && proxy.getUsername() != null && proxy.getPassword() != null) { + AuthScope authscope = new AuthScope(proxy.getHost(), proxy.getPort()); + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(proxy.getUsername(), + proxy.getPassword()); + setCredentials(authscope, credentials); + } + } + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/ProxyHostProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/ProxyHostProperties.java new file mode 100644 index 00000000..67bdee27 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/ProxyHostProperties.java @@ -0,0 +1,82 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.proxy; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public class ProxyHostProperties { + private String host; + private int port; + private String nonProxyHosts; + private String username; + private String password; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getNonProxyHosts() { + return nonProxyHosts; + } + + public void setNonProxyHosts(String nonProxyHosts) { + this.nonProxyHosts = nonProxyHosts; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public enum ProxyForScheme { + HTTP, + HTTPS; + + @JsonCreator + public static ProxyForScheme forLowerCaseName(String lowerCaseName) { + return ProxyForScheme.valueOf(lowerCaseName.toUpperCase()); + } + + @JsonValue + public String lowercaseName() { + return this.name().toLowerCase(); + } + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/SchemeBasedRoutePlanner.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/SchemeBasedRoutePlanner.java new file mode 100644 index 00000000..e1d3510e --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/proxy/SchemeBasedRoutePlanner.java @@ -0,0 +1,50 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.proxy; + +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.impl.conn.DefaultRoutePlanner; +import org.apache.http.protocol.HttpContext; + +/** + * @author Dylan Roberts + */ +public class SchemeBasedRoutePlanner extends DefaultRoutePlanner { + + private final ProxyHostProperties httpsProxy; + private final ProxyHostProperties httpProxy; + + public SchemeBasedRoutePlanner(ProxyHostProperties httpsProxy, ProxyHostProperties httpProxy) { + super(null); + this.httpsProxy = httpsProxy; + this.httpProxy = httpProxy; + } + + @Override + protected HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) { + return "https".equals(target.getSchemeName()) ? + determineProxy(httpsProxy) : + determineProxy(httpProxy); + } + + private HttpHost determineProxy(ProxyHostProperties properties) { + if (properties == null) { + return null; + } + return new HttpHost(properties.getHost(), properties.getPort()); + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/HttpClientSupport.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/HttpClientSupport.java new file mode 100644 index 00000000..0974e86a --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/HttpClientSupport.java @@ -0,0 +1,55 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.support; + +import java.security.GeneralSecurityException; + +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; + +import org.springframework.cloud.config.server.proxy.ProxyHostCredentialsProvider; +import org.springframework.cloud.config.server.proxy.ProxyHostProperties; +import org.springframework.cloud.config.server.proxy.SchemeBasedRoutePlanner; +import org.springframework.util.CollectionUtils; + +/** + * @author Dylan Roberts + */ +public class HttpClientSupport { + + public static HttpClientBuilder builder(HttpEnvironmentRepositoryProperties environmentProperties) + throws GeneralSecurityException { + SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); + HttpClientBuilder httpClientBuilder = HttpClients.custom(); + + if (environmentProperties.isSkipSslValidation()) { + sslContextBuilder.loadTrustMaterial(null, (certificate, authType) -> true); + httpClientBuilder.setSSLHostnameVerifier(new NoopHostnameVerifier()); + } + + if (!CollectionUtils.isEmpty(environmentProperties.getProxy())) { + ProxyHostProperties httpsProxy = environmentProperties.getProxy().get(ProxyHostProperties.ProxyForScheme.HTTPS); + ProxyHostProperties httpProxy = environmentProperties.getProxy().get(ProxyHostProperties.ProxyForScheme.HTTP); + + httpClientBuilder.setRoutePlanner(new SchemeBasedRoutePlanner(httpsProxy, httpProxy)); + httpClientBuilder.setDefaultCredentialsProvider(new ProxyHostCredentialsProvider(httpProxy, httpsProxy)); + } + + return httpClientBuilder.setSSLContext(sslContextBuilder.build()); + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/HttpEnvironmentRepositoryProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/HttpEnvironmentRepositoryProperties.java new file mode 100644 index 00000000..bb5cc3e0 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/HttpEnvironmentRepositoryProperties.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.support; + +import java.util.Map; + +import org.springframework.cloud.config.server.proxy.ProxyHostProperties; +import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties; + +/** + * @author Dylan Roberts + */ +public interface HttpEnvironmentRepositoryProperties extends EnvironmentRepositoryProperties { + + Map getProxy(); + + boolean isSkipSslValidation(); +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java index 4bdccf4a..f8ccf98d 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java @@ -45,6 +45,26 @@ public class CompositeClasspathTests { } } + @RunWith(ModifiedClassPathRunner.class) + @ClassPathExclusions("httpclient-*.jar") + public static class HttpClientTests { + + @Test + public void contextLoads() { + new WebApplicationContextRunner() + .withUserConfiguration(ConfigServerApplication.class) + .withPropertyValues("spring.profiles.active:test,composite", + "spring.config.name:compositeconfigserver", + "spring.cloud.config.server.composite[0].uri:file:./target/repos/config-repo", + "spring.cloud.config.server.composite[0].type:git", + "spring.cloud.config.server.composite[1].uri:file:///./target/repos/svn-config-repo", + "spring.cloud.config.server.composite[1].type:svn") + .run(context -> { + CompositeUtils.getCompositeTypeList(context.getEnvironment()); + }); + } + } + @RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("svnkit-*.jar") public static class SvnTests { diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/HttpClientVaultRestTemplateFactoryTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/HttpClientVaultRestTemplateFactoryTest.java new file mode 100644 index 00000000..70853c51 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/HttpClientVaultRestTemplateFactoryTest.java @@ -0,0 +1,150 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.config; + +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.cloud.config.server.environment.HttpClientVaultRestTemplateFactory; +import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties; +import org.springframework.cloud.config.server.proxy.ProxyHostProperties; +import org.springframework.web.client.RestTemplate; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.instanceOf; + +/** + * @author Dylan Roberts + */ +public class HttpClientVaultRestTemplateFactoryTest { + private static final ProxyHostProperties AUTHENTICATED_HTTP_PROXY = new ProxyHostProperties(); + static { + AUTHENTICATED_HTTP_PROXY.setHost("http://authenticated.http.proxy"); + AUTHENTICATED_HTTP_PROXY.setPort(8080); + AUTHENTICATED_HTTP_PROXY.setUsername("username"); + AUTHENTICATED_HTTP_PROXY.setPassword("password"); + } + private static final ProxyHostProperties AUTHENTICATED_HTTPS_PROXY = new ProxyHostProperties(); + static { + AUTHENTICATED_HTTPS_PROXY.setHost("http://authenticated.https.proxy"); + AUTHENTICATED_HTTPS_PROXY.setPort(8081); + AUTHENTICATED_HTTPS_PROXY.setUsername("username2"); + AUTHENTICATED_HTTPS_PROXY.setPassword("password2"); + } + private static final ProxyHostProperties HTTP_PROXY = new ProxyHostProperties(); + static { + HTTP_PROXY.setHost("http://http.proxy"); + HTTP_PROXY.setPort(8080); + } + private static final ProxyHostProperties HTTPS_PROXY = new ProxyHostProperties(); + static { + HTTPS_PROXY.setHost("http://https.proxy"); + HTTPS_PROXY.setPort(8081); + } + + private HttpClientVaultRestTemplateFactory factory; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() { + factory = new HttpClientVaultRestTemplateFactory(); + } + + @Test + public void authenticatedHttpsProxy() throws Exception { + VaultEnvironmentProperties properties = getVaultEnvironmentProperties(null, AUTHENTICATED_HTTPS_PROXY); + RestTemplate restTemplate = factory.build(properties); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(AUTHENTICATED_HTTPS_PROXY.getHost())))); + + restTemplate.getForObject("https://somehost", String.class); + } + + @Test + public void httpsProxy() throws Exception { + VaultEnvironmentProperties properties = getVaultEnvironmentProperties(null, HTTPS_PROXY); + RestTemplate restTemplate = factory.build(properties); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(HTTPS_PROXY.getHost())))); + + restTemplate.getForObject("https://somehost", String.class); + } + + @Test + public void httpsProxy_notCalled() throws Exception { + VaultEnvironmentProperties properties = getVaultEnvironmentProperties(null, HTTPS_PROXY); + RestTemplate restTemplate = factory.build(properties); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString("somehost")))); + + restTemplate.getForObject("http://somehost", String.class); + } + + @Test + public void authenticatedHttpProxy() throws Exception { + VaultEnvironmentProperties properties = getVaultEnvironmentProperties(AUTHENTICATED_HTTP_PROXY, null); + RestTemplate restTemplate = factory.build(properties); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(AUTHENTICATED_HTTP_PROXY.getHost())))); + + restTemplate.getForObject("http://somehost", String.class); + } + + @Test + public void httpProxy() throws Exception { + VaultEnvironmentProperties properties = getVaultEnvironmentProperties(HTTP_PROXY, null); + RestTemplate restTemplate = factory.build(properties); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(HTTP_PROXY.getHost())))); + + restTemplate.getForObject("http://somehost", String.class); + } + + @Test + public void httpProxy_notCalled() throws Exception { + VaultEnvironmentProperties properties = getVaultEnvironmentProperties(HTTP_PROXY, null); + RestTemplate restTemplate = factory.build(properties); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString("somehost")))); + + restTemplate.getForObject("https://somehost", String.class); + } + + private VaultEnvironmentProperties getVaultEnvironmentProperties(ProxyHostProperties httpProxy, ProxyHostProperties httpsProxy) { + Map proxyMap = new HashMap<>(); + proxyMap.put(ProxyHostProperties.ProxyForScheme.HTTP, httpProxy); + proxyMap.put(ProxyHostProperties.ProxyForScheme.HTTPS, httpsProxy); + VaultEnvironmentProperties properties = new VaultEnvironmentProperties(); + properties.setProxy(proxyMap); + return properties; + } +} \ No newline at end of file diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryHttpProxyTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryHttpProxyTests.java new file mode 100644 index 00000000..d7bc11c7 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryHttpProxyTests.java @@ -0,0 +1,222 @@ +/* + * Copyright 2018 the original author or authors. + * + * 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.springframework.cloud.config.server.environment; + +import java.io.IOException; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.http.client.HttpClient; +import org.eclipse.jgit.transport.HttpTransport; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.transport.http.HttpConnectionFactory; +import org.eclipse.jgit.transport.http.apache.HttpClientConnection; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.config.server.config.ConfigServerProperties; +import org.springframework.cloud.config.server.config.EnvironmentRepositoryConfiguration; +import org.springframework.cloud.config.server.proxy.ProxyHostProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.instanceOf; + +/** + * @author Dylan Roberts + */ +public class JGitEnvironmentRepositoryHttpProxyTests { + private static final ProxyHostProperties AUTHENTICATED_HTTP_PROXY = new ProxyHostProperties(); + static { + AUTHENTICATED_HTTP_PROXY.setHost("http://authenticated.http.proxy"); + AUTHENTICATED_HTTP_PROXY.setPort(8080); + AUTHENTICATED_HTTP_PROXY.setUsername("username"); + AUTHENTICATED_HTTP_PROXY.setPassword("password"); + } + private static final ProxyHostProperties AUTHENTICATED_HTTPS_PROXY = new ProxyHostProperties(); + static { + AUTHENTICATED_HTTPS_PROXY.setHost("http://authenticated.https.proxy"); + AUTHENTICATED_HTTPS_PROXY.setPort(8081); + AUTHENTICATED_HTTPS_PROXY.setUsername("username2"); + AUTHENTICATED_HTTPS_PROXY.setPassword("password2"); + } + private static final ProxyHostProperties HTTP_PROXY = new ProxyHostProperties(); + static { + HTTP_PROXY.setHost("http://http.proxy"); + HTTP_PROXY.setPort(8080); + } + private static final ProxyHostProperties HTTPS_PROXY = new ProxyHostProperties(); + static { + HTTPS_PROXY.setHost("http://https.proxy"); + HTTPS_PROXY.setPort(8081); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void authenticatedHttpsProxy() throws Exception { + String repoUrl = "https://myrepo/repo.git"; + new SpringApplicationBuilder(TestConfiguration.class) + .web(WebApplicationType.NONE) + .properties(gitProperties(repoUrl, null, AUTHENTICATED_HTTPS_PROXY)) + .run(); + HttpClient httpClient = getHttpClientForUrl(repoUrl); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(AUTHENTICATED_HTTPS_PROXY.getHost())))); + + makeRequest(httpClient, "https://somehost"); + } + + @Test + public void httpsProxy() throws Exception { + String repoUrl = "https://myrepo/repo.git"; + new SpringApplicationBuilder(TestConfiguration.class) + .web(WebApplicationType.NONE) + .properties(gitProperties(repoUrl, null, HTTPS_PROXY)) + .run(); + HttpClient httpClient = getHttpClientForUrl(repoUrl); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(HTTPS_PROXY.getHost())))); + + makeRequest(httpClient, "https://somehost"); + } + + @Test + public void httpsProxy_notCalled() throws Exception { + String repoUrl = "https://myrepo/repo.git"; + new SpringApplicationBuilder(TestConfiguration.class) + .web(WebApplicationType.NONE) + .properties(gitProperties(repoUrl, null, HTTPS_PROXY)) + .run(); + HttpClient httpClient = getHttpClientForUrl(repoUrl); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString("somehost")))); + + makeRequest(httpClient, "http://somehost"); + } + + @Test + public void authenticatedHttpProxy() throws Exception { + String repoUrl = "https://myrepo/repo.git"; + new SpringApplicationBuilder(TestConfiguration.class) + .web(WebApplicationType.NONE) + .properties(gitProperties(repoUrl, AUTHENTICATED_HTTP_PROXY, null)) + .run(); + HttpClient httpClient = getHttpClientForUrl(repoUrl); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(AUTHENTICATED_HTTP_PROXY.getHost())))); + + makeRequest(httpClient, "http://somehost"); + } + + @Test + public void httpProxy() throws Exception { + String repoUrl = "https://myrepo/repo.git"; + new SpringApplicationBuilder(TestConfiguration.class) + .web(WebApplicationType.NONE) + .properties(gitProperties(repoUrl, HTTP_PROXY, null)) + .run(); + HttpClient httpClient = getHttpClientForUrl(repoUrl); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString(HTTP_PROXY.getHost())))); + + makeRequest(httpClient, "http://somehost"); + } + + @Test + public void httpProxy_notCalled() throws Exception { + String repoUrl = "https://myrepo/repo.git"; + new SpringApplicationBuilder(TestConfiguration.class) + .web(WebApplicationType.NONE) + .properties(gitProperties(repoUrl, HTTP_PROXY, null)) + .run(); + HttpClient httpClient = getHttpClientForUrl(repoUrl); + expectedException.expectCause(allOf( + instanceOf(UnknownHostException.class), + hasProperty("message", containsString("somehost")))); + + makeRequest(httpClient, "https://somehost"); + } + + private String[] gitProperties(String repoUrl, ProxyHostProperties httpProxy, ProxyHostProperties httpsProxy) { + List result = new ArrayList<>(); + result.add("spring.cloud.config.server.git.uri=" + repoUrl); + if (httpProxy != null) { + result.addAll(Arrays.asList( + "spring.cloud.config.server.git.proxy.http.host=" + httpProxy.getHost(), + "spring.cloud.config.server.git.proxy.http.port=" + httpProxy.getPort() + )); + if (httpProxy.getUsername() != null && httpProxy.getPassword() != null) { + result.addAll(Arrays.asList( + "spring.cloud.config.server.git.proxy.http.username=" + httpProxy.getUsername(), + "spring.cloud.config.server.git.proxy.http.password=" + httpProxy.getPassword() + )); + } + } + if (httpsProxy != null) { + result.addAll(Arrays.asList( + "spring.cloud.config.server.git.proxy.https.host=" + httpsProxy.getHost(), + "spring.cloud.config.server.git.proxy.https.port=" + httpsProxy.getPort() + )); + if (httpsProxy.getUsername() != null && httpsProxy.getPassword() != null) { + result.addAll(Arrays.asList( + "spring.cloud.config.server.git.proxy.https.username=" + httpsProxy.getUsername(), + "spring.cloud.config.server.git.proxy.https.password=" + httpsProxy.getPassword() + )); + } + } + return result.toArray(new String[0]); + } + + private void makeRequest(HttpClient httpClient, String url) { + RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient)); + restTemplate.getForObject(url, String.class); + } + + private HttpClient getHttpClientForUrl(String repoUrl) throws IOException { + HttpConnectionFactory connectionFactory = HttpTransport.getConnectionFactory(); + HttpConnection httpConnection = connectionFactory.create(new URL(repoUrl)); + assertThat(httpConnection).isInstanceOf(HttpClientConnection.class); + return (HttpClient) ReflectionTestUtils.getField(httpConnection, "client"); + } + + @Configuration + @EnableConfigurationProperties(ConfigServerProperties.class) + @Import({ PropertyPlaceholderAutoConfiguration.class, EnvironmentRepositoryConfiguration.class }) + protected static class TestConfiguration { + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryIntegrationTests.java index 3ea6ebe4..5842de0f 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryIntegrationTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryIntegrationTests.java @@ -15,18 +15,10 @@ */ package org.springframework.cloud.config.server.environment; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; import java.util.Optional; -import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; import javax.servlet.http.HttpServletRequest; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.ssl.SSLContextBuilder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -39,9 +31,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.cloud.config.environment.Environment; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.instanceOf; @@ -55,10 +45,10 @@ import static org.mockito.Mockito.when; @SpringBootTest(classes = VaultEnvironmentRepositoryIntegrationTests.TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { - "server.ssl.key-store=classpath:ssl-test.jks", - "server.ssl.key-store-password=password", - "server.ssl.key-password=password", - "server.key-alias=ssl-test"}) + "server.ssl.key-store=classpath:ssl-test.jks", + "server.ssl.key-store-password=password", + "server.ssl.key-password=password", + "server.key-alias=ssl-test"}) public class VaultEnvironmentRepositoryIntegrationTests { @LocalServerPort @@ -68,10 +58,10 @@ public class VaultEnvironmentRepositoryIntegrationTests { public ExpectedException expectedException = ExpectedException.none(); @Test - public void withSslValidation() { + public void withSslValidation() throws Exception { VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory = new VaultEnvironmentRepositoryFactory(withRequest(), new EnvironmentWatch.Default(), - Optional.of(skipSslValidationRestTemplate())); + Optional.of(new HttpClientVaultRestTemplateFactory())); VaultEnvironmentRepository vaultEnvironmentRepository = vaultEnvironmentRepositoryFactory.build(withEnvironmentProperties(false)); expectedException.expectCause(instanceOf(SSLHandshakeException.class)); @@ -80,10 +70,10 @@ public class VaultEnvironmentRepositoryIntegrationTests { } @Test - public void skipSslValidation() { + public void skipSslValidation() throws Exception { VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory = new VaultEnvironmentRepositoryFactory(withRequest(), new EnvironmentWatch.Default(), - Optional.of(skipSslValidationRestTemplate())); + Optional.of(new HttpClientVaultRestTemplateFactory())); VaultEnvironmentRepository vaultEnvironmentRepository = vaultEnvironmentRepositoryFactory.build(withEnvironmentProperties(true)); @@ -92,23 +82,6 @@ public class VaultEnvironmentRepositoryIntegrationTests { assertThat(actual).isNotNull(); } - private RestTemplate skipSslValidationRestTemplate() { - try { - SSLContext sslContext = new SSLContextBuilder() - .loadTrustMaterial(null, (certificate, authType) -> true) - .build(); - CloseableHttpClient httpClient = HttpClients.custom() - .setSSLContext(sslContext) - .setSSLHostnameVerifier(new NoopHostnameVerifier()) - .build(); - HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); - requestFactory.setHttpClient(httpClient); - return new RestTemplate(requestFactory); - } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { - throw new RuntimeException(e); - } - } - private VaultEnvironmentProperties withEnvironmentProperties(boolean skipSslValidation) { VaultEnvironmentProperties environmentProperties = new VaultEnvironmentProperties(); environmentProperties.setPort(Integer.decode(localServerPort));