diff --git a/docs/src/main/asciidoc/spring-cloud-config.adoc b/docs/src/main/asciidoc/spring-cloud-config.adoc index de9701f5..aecd11aa 100644 --- a/docs/src/main/asciidoc/spring-cloud-config.adoc +++ b/docs/src/main/asciidoc/spring-cloud-config.adoc @@ -105,6 +105,7 @@ If there are profile-specific YAML (or properties) files, these are also applied Higher precedence translates to a `PropertySource` listed earlier in the `Environment`. (These same rules apply in a standalone Spring Boot application.) +You can set spring.cloud.config.server.accept-empty to false so that Server would return a HTTP 404 status, if the application is not found.By default, this flag is set to true. ==== Git Backend The default implementation of `EnvironmentRepository` uses a Git backend, which is very convenient for managing upgrades and physical diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java index 4bf3d3f4..b3c8e592 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java @@ -233,14 +233,16 @@ public class ConfigClientProperties { String bare = UriComponentsBuilder.fromHttpUrl(uri).userInfo(null).build() .toUriString(); result.uri = bare; - // handle the password only case + + // if userInfo does not contain a :, then append a : to it if (!userInfo.contains(":")) { userInfo = userInfo + ":"; } - String[] split = userInfo.split(":"); + + int sepIndex=userInfo.indexOf(":"); // set username and password from uri - result.username = split[0]; - result.password = split[1]; + result.username = userInfo.substring(0, sepIndex); + result.password = userInfo.substring(sepIndex +1); // override password if explicitly set if (explicitCredentials.password != null) { diff --git a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientPropertiesTests.java b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientPropertiesTests.java index 8d55e2ef..eda6afae 100644 --- a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientPropertiesTests.java +++ b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientPropertiesTests.java @@ -59,7 +59,51 @@ public class ConfigClientPropertiesTests { assertEquals("foo", locator.getUsername()); assertEquals("secret", locator.getPassword()); } + + @Test + public void testIfNoColonPresentInUriCreds() { + locator.setUri("http://foobar@localhost:9999"); + locator.setPassword("secret"); + assertEquals("http://localhost:9999", locator.getRawUri()); + assertEquals("foobar", locator.getUsername()); + assertEquals("secret", locator.getPassword()); + } + @Test + public void testIfColonPresentAtTheEndInUriCreds() { + locator.setUri("http://foobar:@localhost:9999"); + locator.setPassword("secret"); + assertEquals("http://localhost:9999", locator.getRawUri()); + assertEquals("foobar", locator.getUsername()); + assertEquals("secret", locator.getPassword()); + } + + @Test + public void testIfColonPresentAtTheStartInUriCreds() { + locator.setUri("http://:foobar@localhost:9999"); + assertEquals("http://localhost:9999", locator.getRawUri()); + assertEquals("", locator.getUsername()); + assertEquals("foobar", locator.getPassword()); + } + + @Test + public void testIfColonPresentAtTheStartAndEndInUriCreds() { + locator.setUri("http://:foobar:@localhost:9999"); + assertEquals("http://localhost:9999", locator.getRawUri()); + assertEquals("", locator.getUsername()); + assertEquals("foobar:", locator.getPassword()); + } + + + @Test + public void testIfsolonPresentAtTheStartAndEndInUriCreds() { + locator.setUri("http:// @localhost:9999"); + locator.setPassword("secret"); + assertEquals("http://localhost:9999", locator.getRawUri()); + assertEquals(" ", locator.getUsername()); + assertEquals("secret", locator.getPassword()); + } + @Test public void changeNameInOverride() { locator.setName("one"); diff --git a/spring-cloud-config-dependencies/pom.xml b/spring-cloud-config-dependencies/pom.xml index c1a24c50..5e8ba76a 100644 --- a/spring-cloud-config-dependencies/pom.xml +++ b/spring-cloud-config-dependencies/pom.xml @@ -13,6 +13,9 @@ pom spring-cloud-config-dependencies Spring Cloud Config Dependencies + + 4.11.0.201803080745-r + @@ -38,7 +41,12 @@ org.eclipse.jgit org.eclipse.jgit - 4.8.0.201706111038-r + ${jgit.version} + + + org.eclipse.jgit + org.eclipse.jgit.junit.http + ${jgit.version} com.jcraft diff --git a/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfiguration.java b/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfiguration.java index 6ecbe029..4f53e812 100644 --- a/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfiguration.java +++ b/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfiguration.java @@ -21,6 +21,7 @@ import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.cloud.bus.BusProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -39,8 +40,8 @@ public class EnvironmentMonitorAutoConfiguration { private List extractors; @Bean - public PropertyPathEndpoint propertyPathEndpoint() { - return new PropertyPathEndpoint(new CompositePropertyPathNotificationExtractor(this.extractors)); + public PropertyPathEndpoint propertyPathEndpoint(BusProperties busProperties) { + return new PropertyPathEndpoint(new CompositePropertyPathNotificationExtractor(this.extractors), busProperties.getId()); } @Configuration diff --git a/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/PropertyPathEndpoint.java b/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/PropertyPathEndpoint.java index edadbc77..661b37bc 100644 --- a/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/PropertyPathEndpoint.java +++ b/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/PropertyPathEndpoint.java @@ -22,12 +22,10 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.UUID; -import org.springframework.beans.BeansException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.http.HttpHeaders; @@ -40,31 +38,30 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import lombok.RequiredArgsConstructor; -import lombok.extern.apachecommons.CommonsLog; - /** * HTTP endpoint for webhooks coming from repository providers. * * @author Dave Syer * */ -@RequiredArgsConstructor @RestController @RequestMapping(path = "${spring.cloud.config.monitor.endpoint.path:}/monitor") -@CommonsLog public class PropertyPathEndpoint - implements ApplicationEventPublisherAware, ApplicationContextAware { + implements ApplicationEventPublisherAware { + + private static Log log = LogFactory.getLog(PropertyPathEndpoint.class); private final PropertyPathNotificationExtractor extractor; private ApplicationEventPublisher applicationEventPublisher; + private String busId; - private String contextId = UUID.randomUUID().toString(); + public PropertyPathEndpoint(PropertyPathNotificationExtractor extractor, String busId) { + this.extractor = extractor; + this.busId = busId; + } - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - this.contextId = applicationContext.getId(); + /* for testing */ String getBusId() { + return busId; } @Override @@ -89,7 +86,7 @@ public class PropertyPathEndpoint log.info("Refresh for: " + service); this.applicationEventPublisher .publishEvent(new RefreshRemoteApplicationEvent(this, - this.contextId, service)); + this.busId, service)); } return services; } diff --git a/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfigurationTests.java b/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfigurationTests.java index 4a2feab6..43f17ee4 100644 --- a/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfigurationTests.java +++ b/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfigurationTests.java @@ -17,7 +17,6 @@ package org.springframework.cloud.config.monitor; import java.util.Collection; -import java.util.Map; import org.junit.Test; @@ -25,11 +24,11 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.bus.BusProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.MultiValueMap; import static org.junit.Assert.assertEquals; @@ -40,8 +39,9 @@ import static org.junit.Assert.assertEquals; public class EnvironmentMonitorAutoConfigurationTests { @Test - public void test() { + public void testExtractorsCount() { ConfigurableApplicationContext context = new SpringApplicationBuilder( + BusConfig.class, EnvironmentMonitorAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class, ServerProperties.class, PropertyPlaceholderAutoConfiguration.class).properties("server.port=-1") @@ -57,6 +57,7 @@ public class EnvironmentMonitorAutoConfigurationTests { @Test public void testCanAddCustomPropertyPathNotificationExtractor() { ConfigurableApplicationContext context = new SpringApplicationBuilder( + BusConfig.class, CustomPropertyPathNotificationExtractorConfig.class, EnvironmentMonitorAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class, ServerProperties.class, @@ -70,18 +71,22 @@ public class EnvironmentMonitorAutoConfigurationTests { context.close(); } + @Configuration + static class BusConfig { + + @Bean + public BusProperties busProperties() { + return new BusProperties(); + } + } + @Configuration static class CustomPropertyPathNotificationExtractorConfig { @Bean public PropertyPathNotificationExtractor customNotificationExtractor() { - return new PropertyPathNotificationExtractor() { - @Override - public PropertyPathNotification extract( - MultiValueMap headers, - Map payload) { - throw new UnsupportedOperationException("doesn't do anything"); - } - }; + return (headers, payload) -> { + throw new UnsupportedOperationException("doesn't do anything"); + }; } } diff --git a/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/PropertyPathEndpointTests.java b/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/PropertyPathEndpointTests.java index 53a967bf..7807ba12 100644 --- a/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/PropertyPathEndpointTests.java +++ b/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/PropertyPathEndpointTests.java @@ -35,7 +35,7 @@ public class PropertyPathEndpointTests { private PropertyPathEndpoint endpoint = new PropertyPathEndpoint( new CompositePropertyPathNotificationExtractor( - Collections. emptyList())); + Collections.emptyList()), "abc1"); @Before public void init() { @@ -44,15 +44,20 @@ public class PropertyPathEndpointTests { publisher.refresh(); } + @Test + public void testBusId() { + assertEquals("abc1", this.endpoint.getBusId()); + } + @Test - public void testNotifyByForm() throws Exception { + public void testNotifyByForm() { assertEquals(0, this.endpoint - .notifyByForm(new HttpHeaders(), new ArrayList()).size()); + .notifyByForm(new HttpHeaders(), new ArrayList<>()).size()); } @Test - public void testNotifySeveral() throws Exception { - List request = new ArrayList(); + public void testNotifySeveral() { + List request = new ArrayList<>(); request.add("/foo/bar.properties"); request.add("/application.properties"); assertEquals("[bar, *]", @@ -60,56 +65,56 @@ public class PropertyPathEndpointTests { } @Test - public void testNotifyAll() throws Exception { + public void testNotifyAll() { assertEquals("[*]", this.endpoint .notifyByPath(new HttpHeaders(), Collections - . singletonMap("path", "application.yml")) + .singletonMap("path", "application.yml")) .toString()); } @Test - public void testNotifyAllWithProfile() throws Exception { + public void testNotifyAllWithProfile() { assertEquals("[*:local]", this.endpoint .notifyByPath(new HttpHeaders(), Collections - . singletonMap("path", "application-local.yml")) + .singletonMap("path", "application-local.yml")) .toString()); } @Test - public void testNotifyOne() throws Exception { + public void testNotifyOne() { assertEquals("[foo]", this.endpoint .notifyByPath(new HttpHeaders(), Collections - . singletonMap("path", "foo.yml")) + .singletonMap("path", "foo.yml")) .toString()); } @Test - public void testNotifyOneWithWindowsPath() throws Exception { + public void testNotifyOneWithWindowsPath() { assertEquals("[foo]", this.endpoint .notifyByPath(new HttpHeaders(), Collections - . singletonMap("path", "C:\\config\\foo.yml")) + .singletonMap("path", "C:\\config\\foo.yml")) .toString()); } @Test - public void testNotifyOneWithProfile() throws Exception { + public void testNotifyOneWithProfile() { assertEquals("[foo:local, foo-local]", this.endpoint .notifyByPath(new HttpHeaders(), Collections - . singletonMap("path", "foo-local.yml")) + .singletonMap("path", "foo-local.yml")) .toString()); } @Test - public void testNotifyMultiDash() throws Exception { + public void testNotifyMultiDash() { assertEquals("[foo:local-dev, foo-local:dev, foo-local-dev]", this.endpoint .notifyByPath(new HttpHeaders(), Collections - . singletonMap("path", "foo-local-dev.yml")) + .singletonMap("path", "foo-local-dev.yml")) .toString()); } diff --git a/spring-cloud-config-sample/src/main/resources/bootstrapservercomposite.yml b/spring-cloud-config-sample/src/main/resources/bootstrapservercomposite.yml new file mode 100644 index 00000000..29f14d2d --- /dev/null +++ b/spring-cloud-config-sample/src/main/resources/bootstrapservercomposite.yml @@ -0,0 +1,12 @@ +spring: + cloud: + config: + server: + composite: + - type: git + uri: ${repo1} + - type: git + uri: ${repo1} + bootstrap: true + profiles: + active: composite \ No newline at end of file diff --git a/spring-cloud-config-sample/src/test/java/sample/ApplicationBootstrapTests.java b/spring-cloud-config-sample/src/test/java/sample/ApplicationBootstrapTests.java new file mode 100644 index 00000000..7dccace2 --- /dev/null +++ b/spring-cloud-config-sample/src/test/java/sample/ApplicationBootstrapTests.java @@ -0,0 +1,88 @@ +package sample; + +import java.io.IOException; +import java.util.Map; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.config.server.test.ConfigServerTestUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.SocketUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * Test for gh-975. + * org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name + * 'vaultEnvironmentRepositoryFactory' defined in + * org.springframework.cloud.config.server.config.CompositeRepositoryConfiguration: Unsatisfied dependency + * No qualifying bean of type 'javax.servlet.http.HttpServletRequest' available: + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class, +// Normally spring.cloud.config.enabled:true is the default but since we have the config +// server on the classpath we need to set it explicitly + properties = { "spring.cloud.config.enabled:true", "", + "management.security.enabled=false", "management.endpoints.web.exposure.include=*" }, webEnvironment = RANDOM_PORT) +public class ApplicationBootstrapTests { + private static final String BASE_PATH = new WebEndpointProperties().getBasePath(); + + private static int configPort = SocketUtils.findAvailableTcpPort(); + + @LocalServerPort + private int port; + + private static ConfigurableApplicationContext server; + + @BeforeClass + public static void startConfigServer() throws IOException { + System.setProperty("spring.cloud.bootstrap.name", "bootstrapservercomposite"); + String baseDir = ConfigServerTestUtils + .getBaseDirectory("spring-cloud-config-sample"); + String repo = ConfigServerTestUtils.prepareLocalRepo(baseDir, "target/repos", + "config-repo", "target/config"); + System.setProperty("repo1", repo); + server = SpringApplication.run( + org.springframework.cloud.config.server.ConfigServerApplication.class, + "--server.port=" + configPort, "--spring.config.name=compositeserver", + "--repo1=" + repo); + System.setProperty("config.port", "" + configPort); + } + + @AfterClass + public static void close() { + System.clearProperty("config.port"); + System.clearProperty("spring.cloud.bootstrap.name"); + System.clearProperty("repo1"); + if (server != null) { + server.close(); + } + } + + @Test + @SuppressWarnings("unchecked") + public void contextLoads() { + Map res = new TestRestTemplate() + .getForObject("http://localhost:" + port + BASE_PATH + "/env/info.foo", Map.class); + assertThat(res).containsKey("propertySources"); + Map property = (Map) res.get("property"); + assertThat(property).containsEntry("value", "bar"); + } + + public static void main(String[] args) throws IOException { + configPort = 8888; + startConfigServer(); + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-cloud-config-server/pom.xml b/spring-cloud-config-server/pom.xml index 65ebef34..86ab7029 100644 --- a/spring-cloud-config-server/pom.xml +++ b/spring-cloud-config-server/pom.xml @@ -80,6 +80,11 @@ spring-cloud-test-support test + + org.eclipse.jgit + org.eclipse.jgit.junit.http + test + diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/bootstrap/ConfigServerBootstrapConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/bootstrap/ConfigServerBootstrapConfiguration.java index 388144c0..f80631af 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/bootstrap/ConfigServerBootstrapConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/bootstrap/ConfigServerBootstrapConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-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. @@ -21,7 +21,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.cloud.config.client.ConfigClientProperties; import org.springframework.cloud.config.server.config.ConfigServerProperties; import org.springframework.cloud.config.server.config.EnvironmentRepositoryConfiguration; -import org.springframework.cloud.config.server.config.TransportConfiguration; import org.springframework.cloud.config.server.environment.EnvironmentRepository; import org.springframework.cloud.config.server.environment.EnvironmentRepositoryPropertySourceLocator; import org.springframework.context.annotation.Bean; @@ -45,7 +44,7 @@ import org.springframework.util.StringUtils; public class ConfigServerBootstrapConfiguration { @EnableConfigurationProperties(ConfigServerProperties.class) - @Import({ EnvironmentRepositoryConfiguration.class, TransportConfiguration.class }) + @Import({ EnvironmentRepositoryConfiguration.class }) protected static class LocalPropertySourceLocatorConfiguration { @Autowired diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java index 8a7a4140..0d36c9e6 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-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. @@ -29,7 +29,7 @@ import org.springframework.context.annotation.Import; @ConditionalOnBean(ConfigServerConfiguration.Marker.class) @EnableConfigurationProperties(ConfigServerProperties.class) @Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class, - ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class, TransportConfiguration.class }) + ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class }) public class ConfigServerAutoConfiguration { } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerMvcConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerMvcConfiguration.java index 61f762e3..4990f17b 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerMvcConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerMvcConfiguration.java @@ -57,6 +57,7 @@ public class ConfigServerMvcConfiguration extends WebMvcConfigurerAdapter { public EnvironmentController environmentController(EnvironmentRepository envRepository, ConfigServerProperties server) { EnvironmentController controller = new EnvironmentController(encrypted(envRepository, server), this.objectMapper); controller.setStripDocumentFromYaml(server.isStripDocumentFromYaml()); + controller.setAcceptEmpty(server.isAcceptEmpty()); return controller; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java index 1ee6cd57..261c4574 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java @@ -56,6 +56,10 @@ public class ConfigServerProperties { * should be returned in "native" form. */ private boolean stripDocumentFromYaml = true; + /** + * Flag to indicate that If HTTP 404 needs to be sent if Application is not Found + */ + private boolean acceptEmpty = true; /** * Default application name when incoming requests do not have a specific one. @@ -115,7 +119,14 @@ public class ConfigServerProperties { public void setStripDocumentFromYaml(boolean stripDocumentFromYaml) { this.stripDocumentFromYaml = stripDocumentFromYaml; } + + public boolean isAcceptEmpty() { + return this.acceptEmpty; + } + public void setAcceptEmpty(boolean acceptEmpty) { + this.acceptEmpty = acceptEmpty; + } public String getDefaultApplicationName() { return this.defaultApplicationName; } 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 62b1d608..9acd3b39 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 @@ -17,12 +17,12 @@ package org.springframework.cloud.config.server.config; import java.util.List; import java.util.Optional; - import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.api.TransportConfigCallback; import org.tmatesoft.svn.core.SVNException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -113,14 +113,14 @@ class DefaultRepositoryConfiguration { private ConfigServerProperties server; @Autowired(required = false) - private TransportConfigCallback transportConfigCallback; + private TransportConfigCallback customTransportConfigCallback; @Bean public MultipleJGitEnvironmentRepository defaultEnvironmentRepository( MultipleJGitEnvironmentProperties environmentProperties) { MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory = new MultipleJGitEnvironmentRepositoryFactory(environment, server, - Optional.ofNullable(transportConfigCallback)); + Optional.ofNullable(customTransportConfigCallback)); return gitEnvironmentRepositoryFactory.build(environmentProperties); } } @@ -169,7 +169,7 @@ class SvnRepositoryConfiguration { @Profile("vault") class VaultRepositoryConfiguration { @Bean - public VaultEnvironmentRepository vaultEnvironmentRepository(HttpServletRequest request, EnvironmentWatch watch, + public VaultEnvironmentRepository vaultEnvironmentRepository(ObjectProvider request, EnvironmentWatch watch, VaultEnvironmentProperties environmentProperties) { return new VaultEnvironmentRepositoryFactory(request, watch).build(environmentProperties); } @@ -198,8 +198,8 @@ class CompositeRepositoryConfiguration { @Bean public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory( ConfigurableEnvironment environment, ConfigServerProperties server, - Optional transportConfigCallback) { - return new MultipleJGitEnvironmentRepositoryFactory(environment, server, transportConfigCallback); + Optional customTransportConfigCallback) { + return new MultipleJGitEnvironmentRepositoryFactory(environment, server, customTransportConfigCallback); } } @@ -214,7 +214,7 @@ class CompositeRepositoryConfiguration { } @Bean - public VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory(HttpServletRequest request, + public VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory(ObjectProvider request, EnvironmentWatch watch) { return new VaultEnvironmentRepositoryFactory(request, watch); } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/TransportConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/TransportConfiguration.java deleted file mode 100644 index aa6aaccb..00000000 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/TransportConfiguration.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2015-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 com.jcraft.jsch.JSch; -import com.jcraft.jsch.Session; -import org.eclipse.jgit.api.TransportConfigCallback; -import org.eclipse.jgit.transport.*; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.config.server.ssh.PropertyBasedSshSessionFactory; -import org.springframework.cloud.config.server.ssh.SshUriProperties; -import org.springframework.cloud.config.server.ssh.SshUriPropertyProcessor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Configure a callback to set up a property based SSH settings before running a transport command (such as clone or fetch) - * - * @author Ollie Hughes - */ -@Configuration -@ConditionalOnClass(TransportConfigCallback.class) -@EnableConfigurationProperties(SshUriProperties.class) -public class TransportConfiguration { - - @ConditionalOnMissingBean(TransportConfigCallback.class) - @Bean - public TransportConfigCallback propertiesBasedSshTransportCallback(final SshUriProperties sshUriProperties) { - if(sshUriProperties.isIgnoreLocalSshSettings()) { - return new PropertiesBasedSshTransportConfigCallback(sshUriProperties); - } - else return new FileBasedSshTransportConfigCallback(sshUriProperties); - } - - /** - * Configure JGit transport command to use a SSH session factory that is configured using properties defined - * in {@link SshUriProperties} - */ - public static class PropertiesBasedSshTransportConfigCallback implements TransportConfigCallback { - - private SshUriProperties sshUriProperties; - - public PropertiesBasedSshTransportConfigCallback(SshUriProperties sshUriProperties) { - this.sshUriProperties = sshUriProperties; - } - - public SshUriProperties getSshUriProperties() { - return sshUriProperties; - } - - @Override - public void configure(Transport transport) { - if (transport instanceof SshTransport) { - SshTransport sshTransport = (SshTransport) transport; - sshTransport.setSshSessionFactory( - new PropertyBasedSshSessionFactory( - new SshUriPropertyProcessor(sshUriProperties).getSshKeysByHostname(), new JSch())); - } - } - } - - /** - * Configure JGit transport command to use a default SSH session factory based on local machines SSH config. - * Allow strict host key checking to be set. - */ - public static class FileBasedSshTransportConfigCallback implements TransportConfigCallback { - - private SshUriProperties sshUriProperties; - - public FileBasedSshTransportConfigCallback(SshUriProperties sshUriProperties) { - this.sshUriProperties = sshUriProperties; - } - - public SshUriProperties getSshUriProperties() { - return sshUriProperties; - } - - @Override - public void configure(Transport transport) { - SshSessionFactory.setInstance(new JschConfigSessionFactory() { - @Override - protected void configure(OpenSshConfig.Host hc, Session session) { - session.setConfig("StrictHostKeyChecking", - sshUriProperties.isStrictHostKeyChecking() ? "yes" : "no"); - } - }); - } - } -} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java index 3d6c0e3a..29a6f049 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentController.java @@ -69,6 +69,7 @@ public class EnvironmentController { private ObjectMapper objectMapper; private boolean stripDocument = true; + private boolean acceptEmpty = true; public EnvironmentController(EnvironmentRepository repository) { this(repository, new ObjectMapper()); @@ -90,6 +91,15 @@ public class EnvironmentController { this.stripDocument = stripDocument; } + /** + * Flag to indicate that If HTTP 404 needs to be sent if Application is not Found + * + * @param acceptEmpty the flag to set + */ + public void setAcceptEmpty(boolean acceptEmpty) { + this.acceptEmpty = acceptEmpty; + } + @RequestMapping("/{name}/{profiles:.*[^-].*}") public Environment defaultLabel(@PathVariable String name, @PathVariable String profiles) { @@ -110,6 +120,9 @@ public class EnvironmentController { label = label.replace("(_)", "/"); } Environment environment = this.repository.findOne(name, profiles, label); + if(!acceptEmpty && (environment == null || environment.getPropertySources().isEmpty())){ + throw new EnvironmentNotFoundException("Profile Not found"); + } return environment; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentNotFoundException.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentNotFoundException.java new file mode 100644 index 00000000..31908c89 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/EnvironmentNotFoundException.java @@ -0,0 +1,33 @@ +/* + * 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.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * @author Chids + * + */ +@SuppressWarnings("serial") +@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Application Not Found") +public class EnvironmentNotFoundException extends RuntimeException { + + public EnvironmentNotFoundException(String string) { + super(string); + } + +} 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 07411da4..5dcacd7c 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,19 +15,76 @@ */ package org.springframework.cloud.config.server.environment; +import javax.validation.constraints.Pattern; + import org.springframework.cloud.config.server.support.AbstractScmAccessorProperties; /** * @author Dylan Roberts + * @author Gareth Clay */ public class JGitEnvironmentProperties extends AbstractScmAccessorProperties { 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. */ private boolean cloneOnStart = false; + + /** Flag to indicate that the repository should force pull. If true discard any local changes and take from remote repository. */ private boolean forcePull; + + /** Timeout (in seconds) for obtaining HTTP or SSH connection (if applicable), defaults to 5 seconds. */ private int timeout = 5; + + /** Flag to indicate that the branch should be deleted locally if it's origin tracked branch was removed. */ private boolean deleteUntrackedBranches = false; + /** + * Flag to indicate that SSL certificate validation should be bypassed when + * communicating with a repository served over an HTTPS connection. + */ + private boolean skipSslValidation = false; + + /** + * Time (in seconds) between refresh of the git repository + */ + private int refreshRate = 0; + + /** + * Valid SSH private key. Must be set if ignoreLocalSshSettings is true and Git URI is SSH format. + */ + private String privateKey; + + /** + * One of ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521. Must be set if hostKey is also set. + */ + private String hostKeyAlgorithm; + + /** + * Valid SSH host key. Must be set if hostKeyAlgorithm is also set. + */ + private String hostKey; + + /** + * Location of custom .known_hosts file. + */ + private String knownHostsFile; + + /** + * Override server authentication method order. This should allow for evading login prompts if server has keyboard-interactive authentication before the publickey method. + */ + @Pattern(regexp = "([\\w -]+,)*([\\w -]+)") + private String preferredAuthentications; + + /** + * If true, use property-based instead of file-based SSH config. + */ + private boolean ignoreLocalSshSettings; + + /** + * If false, ignore errors with host key + */ + private boolean strictHostKeyChecking = true; + public JGitEnvironmentProperties() { super(); setDefaultLabel(DEFAULT_LABEL); @@ -64,4 +121,78 @@ public class JGitEnvironmentProperties extends AbstractScmAccessorProperties { public void setDeleteUntrackedBranches(boolean deleteUntrackedBranches) { this.deleteUntrackedBranches = deleteUntrackedBranches; } + + public int getRefreshRate() { + return refreshRate; + } + + public void setRefreshRate(int refreshRate) { + this.refreshRate = refreshRate; + } + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public String getHostKeyAlgorithm() { + return hostKeyAlgorithm; + } + + public void setHostKeyAlgorithm(String hostKeyAlgorithm) { + this.hostKeyAlgorithm = hostKeyAlgorithm; + } + + public String getHostKey() { + return hostKey; + } + + public void setHostKey(String hostKey) { + this.hostKey = hostKey; + } + + public String getKnownHostsFile() { + return knownHostsFile; + } + + public void setKnownHostsFile(String knownHostsFile) { + this.knownHostsFile = knownHostsFile; + } + + public String getPreferredAuthentications() { + return preferredAuthentications; + } + + public void setPreferredAuthentications(String preferredAuthentications) { + this.preferredAuthentications = preferredAuthentications; + } + + public boolean isIgnoreLocalSshSettings() { + return ignoreLocalSshSettings; + } + + public void setIgnoreLocalSshSettings(boolean ignoreLocalSshSettings) { + this.ignoreLocalSshSettings = ignoreLocalSshSettings; + } + + @Override + public boolean isStrictHostKeyChecking() { + return strictHostKeyChecking; + } + + @Override + public void setStrictHostKeyChecking(boolean strictHostKeyChecking) { + this.strictHostKeyChecking = strictHostKeyChecking; + } + + public boolean isSkipSslValidation() { + return skipSslValidation; + } + + public void setSkipSslValidation(boolean skipSslValidation) { + this.skipSslValidation = skipSslValidation; + } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java index 7a40a718..9e430330 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java @@ -54,19 +54,18 @@ import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.TagOpt; import org.eclipse.jgit.transport.TrackingRefUpdate; -import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.util.FileUtils; import org.springframework.beans.factory.InitializingBean; -import org.springframework.cloud.config.server.support.PassphraseCredentialsProvider; +import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.UrlResource; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; + import static java.lang.String.format; import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE; -import static org.springframework.util.StringUtils.hasText; /** * An {@link EnvironmentRepository} backed by a single git repository. @@ -76,6 +75,7 @@ import static org.springframework.util.StringUtils.hasText; * @author Marcos Barbero * @author Daniel Lavoie * @author Ryan Lynch + * @author Gareth Clay */ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository implements EnvironmentRepository, SearchPathLocator, InitializingBean { @@ -90,6 +90,16 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository */ private int timeout; + /** + * Time (in seconds) between refresh of the git repository + */ + private int refreshRate = 0; + + /** + * Time of the last refresh of the git repository + */ + private long lastRefresh; + /** * Flag to indicate that the repository should be cloned on startup (not on demand). * Generally leads to slower startup but faster first query. @@ -101,9 +111,10 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository private String defaultLabel; /** - * The credentials provider to use to connect to the Git repository. + * Factory used to create the credentials provider to use to connect to the Git + * repository. */ - private CredentialsProvider gitCredentialsProvider; + private GitCredentialsProviderFactory gitCredentialsProviderFactory = new GitCredentialsProviderFactory(); /** * Transport configuration callback for JGit commands. @@ -122,6 +133,12 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository */ private boolean deleteUntrackedBranches; + /** + * Flag to indicate that SSL certificate validation should be bypassed when + * communicating with a repository served over an HTTPS connection. + */ + private boolean skipSslValidation; + public JGitEnvironmentRepository(ConfigurableEnvironment environment, JGitEnvironmentProperties properties) { super(environment, properties); this.cloneOnStart = properties.isCloneOnStart(); @@ -129,6 +146,8 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository this.forcePull = properties.isForcePull(); this.timeout = properties.getTimeout(); this.deleteUntrackedBranches = properties.isDeleteUntrackedBranches(); + this.refreshRate = properties.getRefreshRate(); + this.skipSslValidation = properties.isSkipSslValidation(); } public boolean isCloneOnStart() { @@ -147,6 +166,15 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository this.timeout = timeout; } + public int getRefreshRate() { + return refreshRate; + } + + public void setRefreshRate(int refreshRate) { + this.refreshRate = refreshRate; + } + + public TransportConfigCallback getTransportConfigCallback() { return transportConfigCallback; } @@ -164,6 +192,11 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository this.gitFactory = gitFactory; } + public void setGitCredentialsProviderFactory( + GitCredentialsProviderFactory gitCredentialsProviderFactory) { + this.gitCredentialsProviderFactory = gitCredentialsProviderFactory; + } + public String getDefaultLabel() { return this.defaultLabel; } @@ -188,6 +221,14 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository this.deleteUntrackedBranches = deleteUntrackedBranches; } + public boolean isSkipSslValidation() { + return skipSslValidation; + } + + public void setSkipSslValidation(boolean skipSslValidation) { + this.skipSslValidation = skipSslValidation; + } + @Override public synchronized Locations getLocations(String application, String profile, String label) { @@ -349,6 +390,11 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository protected boolean shouldPull(Git git) throws GitAPIException { boolean shouldPull; + + if (this.refreshRate > 0 && System.currentTimeMillis() - this.lastRefresh < (this.refreshRate * 1000)) { + return false; + } + Status gitStatus = git.status().call(); boolean isWorkingTreeClean = gitStatus.isClean(); String originUrl = git.getRepository().getConfig().getString("remote", "origin", @@ -394,6 +440,9 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository fetch.setRemote("origin"); fetch.setTagOpt(TagOpt.FETCH_TAGS); fetch.setRemoveDeletedRefs(deleteUntrackedBranches); + if (this.refreshRate > 0) { + this.setLastRefresh(System.currentTimeMillis()); + } configureCommand(fetch); try { @@ -555,19 +604,8 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository } private CredentialsProvider getCredentialsProvider() { - if (this.gitCredentialsProvider != null) { - return this.gitCredentialsProvider; - } - - if (hasText(getUsername()) && hasText(getPassword())) { - return new UsernamePasswordCredentialsProvider(getUsername(), getPassword()); - } - - if (hasText(getPassphrase())) { - return new PassphraseCredentialsProvider(getPassphrase()); - } - - return null; + return this.gitCredentialsProviderFactory.createFor(this.getUri(), getUsername(), + getPassword(), getPassphrase(), isSkipSslValidation()); } private boolean isClean(Git git, String label) { @@ -621,6 +659,14 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository } } + public void setLastRefresh(long lastRefresh) { + this.lastRefresh = lastRefresh; + } + + public long getLastRefresh() { + return lastRefresh; + } + /** * Wraps the static method calls to {@link org.eclipse.jgit.api.Git} and * {@link org.eclipse.jgit.api.CloneCommand} allowing for easier unit testing. @@ -637,18 +683,4 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository return command; } } - - /** - * @return the gitCredentialsProvider - */ - public CredentialsProvider getGitCredentialsProvider() { - return gitCredentialsProvider; - } - - /** - * @param gitCredentialsProvider the gitCredentialsProvider to set - */ - public void setGitCredentialsProvider(CredentialsProvider gitCredentialsProvider) { - this.gitCredentialsProvider = gitCredentialsProvider; - } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java index 4125fa93..5a05b344 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java @@ -27,6 +27,7 @@ public class JdbcEnvironmentProperties implements EnvironmentRepositoryPropertie private static final String DEFAULT_SQL = "SELECT KEY, VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?"; private int order = Ordered.LOWEST_PRECEDENCE - 10; + /** SQL used to query database for keys and values */ private String sql = DEFAULT_SQL; public int getOrder() { diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProperties.java index 72160806..3a1353a5 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProperties.java @@ -19,12 +19,25 @@ import java.util.LinkedHashMap; import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.config.server.ssh.HostKeyAlgoSupported; +import org.springframework.cloud.config.server.ssh.HostKeyAndAlgoBothExist; +import org.springframework.cloud.config.server.ssh.KnownHostsFileIsValid; +import org.springframework.cloud.config.server.ssh.PrivateKeyIsValid; +import org.springframework.validation.annotation.Validated; /** * @author Dylan Roberts */ @ConfigurationProperties("spring.cloud.config.server.git") +@Validated +@PrivateKeyIsValid +@HostKeyAndAlgoBothExist +@HostKeyAlgoSupported +@KnownHostsFileIsValid public class MultipleJGitEnvironmentProperties extends JGitEnvironmentProperties { + /** + * Map of repository identifier to location and other properties. + */ private Map repos = new LinkedHashMap<>(); public Map getRepos() { @@ -36,7 +49,13 @@ public class MultipleJGitEnvironmentProperties extends JGitEnvironmentProperties } public static class PatternMatchingJGitEnvironmentProperties extends JGitEnvironmentProperties { + /** + * Pattern to match on application name and profiles. + */ private String[] pattern = new String[0]; + /** + * Name of repository (same as map key by default). + */ private String name; public String[] getPattern() { diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java index 99b57fbc..d737a30a 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java @@ -27,7 +27,6 @@ import java.util.stream.Collectors; import org.springframework.beans.BeanUtils; import org.springframework.cloud.config.environment.Environment; -import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.util.PatternMatchUtils; import org.springframework.util.StringUtils; @@ -44,6 +43,7 @@ import org.springframework.util.StringUtils; * * @author Andy Chan (iceycake) * @author Dave Syer + * @author Gareth Clay * */ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository { @@ -66,9 +66,6 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository @Override public void afterPropertiesSet() throws Exception { - GitCredentialsProviderFactory credentialFactory = new GitCredentialsProviderFactory(); - super.setGitCredentialsProvider(credentialFactory.createFor(getUri(), - getUsername(), getPassword(), getPassphrase())); super.afterPropertiesSet(); for (String name : this.repos.keySet()) { PatternMatchingJGitEnvironmentRepository repo = this.repos.get(name); @@ -85,18 +82,21 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository if (getTimeout() != 0 && repo.getTimeout() == 0) { repo.setTimeout(getTimeout()); } + if (getRefreshRate() != 0 && repo.getRefreshRate() == 0) { + repo.setRefreshRate(getRefreshRate()); + } String user = repo.getUsername(); - String pass = repo.getPassword(); String passphrase = repo.getPassphrase(); if (user == null) { - user = getUsername(); - pass = getPassword(); + repo.setUsername(getUsername()); + repo.setPassword(getPassword()); } if (passphrase == null) { - passphrase = getPassphrase(); + repo.setPassphrase(getPassphrase()); + } + if (isSkipSslValidation()) { + repo.setSkipSslValidation(true); } - repo.setGitCredentialsProvider( - credentialFactory.createFor(repo.getUri(), user, pass, passphrase)); repo.afterPropertiesSet(); } if (!getBasedir().exists() && 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 73e02e14..a01aaa9b 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 @@ -20,6 +20,8 @@ import java.util.Optional; import org.eclipse.jgit.api.TransportConfigCallback; import org.springframework.cloud.config.server.config.ConfigServerProperties; +import org.springframework.cloud.config.server.ssh.FileBasedSshTransportConfigCallback; +import org.springframework.cloud.config.server.ssh.PropertiesBasedSshTransportConfigCallback; import org.springframework.core.env.ConfigurableEnvironment; /** @@ -29,23 +31,31 @@ public class MultipleJGitEnvironmentRepositoryFactory implements EnvironmentRepo MultipleJGitEnvironmentProperties> { private ConfigurableEnvironment environment; private ConfigServerProperties server; - private Optional transportConfigCallback; + private Optional customTransportConfigCallback; public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties server, - Optional transportConfigCallback) { + Optional customTransportConfigCallback) { this.environment = environment; this.server = server; - this.transportConfigCallback = transportConfigCallback; + this.customTransportConfigCallback = customTransportConfigCallback; } @Override public MultipleJGitEnvironmentRepository build(MultipleJGitEnvironmentProperties environmentProperties) { MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(environment, environmentProperties); - repository.setTransportConfigCallback(transportConfigCallback.orElse(null)); + repository.setTransportConfigCallback(customTransportConfigCallback + .orElse(buildTransportConfigCallback(environmentProperties))); if (server.getDefaultLabel() != null) { repository.setDefaultLabel(server.getDefaultLabel()); } return repository; } + + private TransportConfigCallback buildTransportConfigCallback(final 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/NativeEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentProperties.java index dcaeb883..432b52b4 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentProperties.java @@ -24,10 +24,23 @@ import org.springframework.core.Ordered; */ @ConfigurationProperties("spring.cloud.config.server.native") public class NativeEnvironmentProperties implements EnvironmentRepositoryProperties { + /** + * Flag to determine how to handle exceptions during decryption (default false). + */ private Boolean failOnError = false; + /** + * Flag to determine whether label locations should be added. + */ private Boolean addLabelLocations = true; private String defaultLabel = "master"; + /** + * Locations to search for configuration files. Defaults to the same as a Spring Boot + * app so [classpath:/,classpath:/config/,file:./,file:./config/]. + */ private String[] searchLocations = new String[0]; + /** + * Version string to be reported for native repository + */ private String version; private int order = Ordered.LOWEST_PRECEDENCE; 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 6f8a12f9..4d17c7af 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 @@ -24,11 +24,17 @@ import org.springframework.core.Ordered; */ @ConfigurationProperties("spring.cloud.config.server.vault") public class VaultEnvironmentProperties implements EnvironmentRepositoryProperties { + /** Vault host. Defaults to 127.0.0.1. */ private String host = "127.0.0.1"; + /** Vault port. Defaults to 8200. */ private Integer port = 8200; + /** Vault scheme. Defaults to http. */ private String scheme = "http"; + /** Vault backend. Defaults to secret. */ private String backend = "secret"; + /** The key in vault shared by all applications. Defaults to application. Set to empty to disable. */ private String defaultKey = "application"; + /** Vault profile separator. Defaults to comma. */ private String profileSeparator = ","; private int order = Ordered.LOWEST_PRECEDENCE; diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java index cec7df18..d8d53ae6 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java @@ -20,15 +20,18 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; + import javax.servlet.http.HttpServletRequest; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonRawValue; import com.fasterxml.jackson.databind.JsonNode; -import org.hibernate.validator.constraints.NotEmpty; -import org.hibernate.validator.constraints.Range; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.config.environment.PropertySource; @@ -61,7 +64,8 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere private String host; /** Vault port. Defaults to 8200. */ - @Range(min = 1, max = 65535) + @Min(1) + @Max(65535) private int port; /** Vault scheme. Defaults to http. */ @@ -83,11 +87,11 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere private RestTemplate rest; // TODO: move to watchState:String on findOne? - private HttpServletRequest request; + private ObjectProvider request; private EnvironmentWatch watch; - public VaultEnvironmentRepository(HttpServletRequest request, EnvironmentWatch watch, RestTemplate rest, + public VaultEnvironmentRepository(ObjectProvider request, EnvironmentWatch watch, RestTemplate rest, VaultEnvironmentProperties properties) { this.request = request; this.watch = watch; @@ -104,7 +108,12 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere @Override public Environment findOne(String application, String profile, String label) { - String state = request.getHeader(STATE_HEADER); + HttpServletRequest servletRequest = request.getIfAvailable(); + if (servletRequest == null) { + throw new IllegalStateException("No HttpServletRequest available"); + } + + String state = servletRequest.getHeader(STATE_HEADER); String newState = this.watch.watch(state); String[] profiles = StringUtils.commaDelimitedListToStringArray(profile); @@ -116,7 +125,7 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere for (String key : keys) { // read raw 'data' key from vault - String data = read(key); + String data = read(servletRequest, key); if (data != null) { // data is in json format of which, yaml is a superset, so parse final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); @@ -162,12 +171,12 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere } } - String read(String key) { + String read(HttpServletRequest servletRequest, String key) { String url = String.format("%s://%s:%s/v1/{backend}/{key}", this.scheme, this.host, this.port); HttpHeaders headers = new HttpHeaders(); - String token = request.getHeader(TOKEN_HEADER); + String token = servletRequest.getHeader(TOKEN_HEADER); if (!StringUtils.hasLength(token)) { throw new IllegalArgumentException("Missing required header: " + TOKEN_HEADER); } 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 ce2a6e4b..9639a716 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 @@ -17,6 +17,7 @@ package org.springframework.cloud.config.server.environment; import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.web.client.RestTemplate; /** @@ -24,10 +25,10 @@ import org.springframework.web.client.RestTemplate; */ public class VaultEnvironmentRepositoryFactory implements EnvironmentRepositoryFactory { - private HttpServletRequest request; + private ObjectProvider request; private EnvironmentWatch watch; - public VaultEnvironmentRepositoryFactory(HttpServletRequest request, EnvironmentWatch watch) { + public VaultEnvironmentRepositoryFactory(ObjectProvider request, EnvironmentWatch watch) { this.request = request; this.watch = watch; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshTransportConfigCallback.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshTransportConfigCallback.java new file mode 100644 index 00000000..b4541ac7 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshTransportConfigCallback.java @@ -0,0 +1,53 @@ +/* + * 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.ssh; + +import com.jcraft.jsch.Session; +import org.eclipse.jgit.api.TransportConfigCallback; +import org.eclipse.jgit.transport.JschConfigSessionFactory; +import org.eclipse.jgit.transport.OpenSshConfig; +import org.eclipse.jgit.transport.SshSessionFactory; +import org.eclipse.jgit.transport.Transport; + +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties; + +/** + * Configure JGit transport command to use a default SSH session factory based on local machines SSH config. + * Allow strict host key checking to be set. + */ +public class FileBasedSshTransportConfigCallback implements TransportConfigCallback { + + private MultipleJGitEnvironmentProperties sshUriProperties; + + public FileBasedSshTransportConfigCallback(MultipleJGitEnvironmentProperties sshUriProperties) { + this.sshUriProperties = sshUriProperties; + } + + public MultipleJGitEnvironmentProperties getSshUriProperties() { + return sshUriProperties; + } + + @Override + public void configure(Transport transport) { + SshSessionFactory.setInstance(new JschConfigSessionFactory() { + @Override + protected void configure(OpenSshConfig.Host hc, Session session) { + session.setConfig("StrictHostKeyChecking", + sshUriProperties.isStrictHostKeyChecking() ? "yes" : "no"); + } + }); + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java index d75d2e48..e3652592 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java @@ -23,6 +23,8 @@ import java.util.Set; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; +import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties; +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties; import org.springframework.validation.annotation.Validated; import static java.lang.String.format; @@ -30,14 +32,14 @@ import static org.springframework.cloud.config.server.ssh.SshPropertyValidator.i import static org.springframework.util.StringUtils.hasText; /** - * JSR-303 Cross Field validator that ensures that a {@link SshUriProperties} bean for the constraints: + * JSR-303 Cross Field validator that ensures that a {@link MultipleJGitEnvironmentProperties} bean for the constraints: * - If host key algo is supported * * Beans annotated with {@link HostKeyAlgoSupported} and {@link Validated} will have the constraints applied. * * @author Ollie Hughes */ -public class HostKeyAlgoSupportedValidator implements ConstraintValidator { +public class HostKeyAlgoSupportedValidator implements ConstraintValidator { private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git."; private final SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(); private static final Set VALID_HOST_KEY_ALGORITHMS = new LinkedHashSet<>(Arrays.asList( @@ -49,12 +51,12 @@ public class HostKeyAlgoSupportedValidator implements ConstraintValidator validationResults = new HashSet<>(); - List extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties); + List extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties); - for (SshUri extractedProperty : extractedProperties) { + for (JGitEnvironmentProperties extractedProperty : extractedProperties) { if (sshUriProperties.isIgnoreLocalSshSettings() && isSshUri(extractedProperty.getUri())) { validationResults.add(isHostKeySpecifiedWhenAlgorithmSet(extractedProperty, context)); } @@ -62,7 +64,7 @@ public class HostKeyAlgoSupportedValidator implements ConstraintValidator { +public class HostKeyAndAlgoBothExistValidator implements ConstraintValidator { private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git."; private final SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(); @@ -46,11 +48,11 @@ public class HostKeyAndAlgoBothExistValidator implements ConstraintValidator validationResults = new HashSet<>(); - List extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties); + List extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties); - for (SshUri extractedProperty : extractedProperties) { + for (JGitEnvironmentProperties extractedProperty : extractedProperties) { if (sshUriProperties.isIgnoreLocalSshSettings() && isSshUri(extractedProperty.getUri())) { validationResults.add( isAlgorithmSpecifiedWhenHostKeySet(extractedProperty, context) @@ -60,7 +62,7 @@ public class HostKeyAndAlgoBothExistValidator implements ConstraintValidator * Beans annotated with {@link KnownHostsFileIsValid} and {@link Validated} will have the constraints applied. * * @author Edgars Jasmans */ -public class KnownHostsFileValidator implements ConstraintValidator { +public class KnownHostsFileValidator implements ConstraintValidator { @Override public void initialize(KnownHostsFileIsValid knownHostsFileIsValid) { @@ -40,7 +41,7 @@ public class KnownHostsFileValidator implements ConstraintValidator { +public class PrivateKeyValidator implements ConstraintValidator { private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git."; private final SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(); @@ -49,12 +51,12 @@ public class PrivateKeyValidator implements ConstraintValidator validationResults = new HashSet<>(); - List extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties); + List extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties); - for (SshUri extractedProperty : extractedProperties) { + for (JGitEnvironmentProperties extractedProperty : extractedProperties) { if (extractedProperty.isIgnoreLocalSshSettings() && isSshUri(extractedProperty.getUri())) { validationResults.add( isPrivateKeyPresent(extractedProperty, context) @@ -65,7 +67,7 @@ public class PrivateKeyValidator implements ConstraintValidator sshKeysByHostname; + private final Map sshKeysByHostname; private final JSch jSch; - public PropertyBasedSshSessionFactory(Map sshKeysByHostname, JSch jSch) { + public PropertyBasedSshSessionFactory(Map sshKeysByHostname, JSch jSch) { this.sshKeysByHostname = sshKeysByHostname; this.jSch = jSch; } @Override protected void configure(Host hc, Session session) { - SshUri sshProperties = sshKeysByHostname.get(hc.getHostName()); + JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(hc.getHostName()); String hostKeyAlgorithm = sshProperties.getHostKeyAlgorithm(); if (hostKeyAlgorithm != null) { session.setConfig(SERVER_HOST_KEY, hostKeyAlgorithm); @@ -69,7 +71,7 @@ public class PropertyBasedSshSessionFactory extends JschConfigSessionFactory { @Override protected Session createSession(Host hc, String user, String host, int port, FS fs) throws JSchException { if (sshKeysByHostname.containsKey(host)) { - SshUri sshUriProperties = sshKeysByHostname.get(host); + JGitEnvironmentProperties sshUriProperties = sshKeysByHostname.get(host); jSch.addIdentity(host, sshUriProperties.getPrivateKey().getBytes(), null, null); if (sshUriProperties.getKnownHostsFile() != null) { jSch.setKnownHosts(sshUriProperties.getKnownHostsFile()); diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshPropertyValidator.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshPropertyValidator.java index 6b1b1475..75b1fc4a 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshPropertyValidator.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshPropertyValidator.java @@ -24,6 +24,8 @@ import java.util.Map; import org.eclipse.jgit.transport.URIish; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties; +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties; import org.springframework.stereotype.Component; import static org.springframework.util.StringUtils.hasText; @@ -34,7 +36,7 @@ import static org.springframework.util.StringUtils.hasText; * @author Ollie Hughes */ @Component -@EnableConfigurationProperties(SshUriProperties.class) +@EnableConfigurationProperties(MultipleJGitEnvironmentProperties.class) public class SshPropertyValidator { protected static boolean isSshUri(Object uri) { @@ -55,10 +57,10 @@ public class SshPropertyValidator { return false; } - protected List extractRepoProperties(SshUriProperties sshUriProperties) { - List allRepoProperties = new ArrayList<>(); + protected List extractRepoProperties(MultipleJGitEnvironmentProperties sshUriProperties) { + List allRepoProperties = new ArrayList<>(); allRepoProperties.add(sshUriProperties); - Map repos = sshUriProperties.getRepos(); + Map repos = sshUriProperties.getRepos(); if (repos != null) { allRepoProperties.addAll(repos.values()); } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUri.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUri.java deleted file mode 100644 index a7211160..00000000 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUri.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2017 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.ssh; - -import org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriNestedRepoProperties; - -import javax.validation.constraints.Pattern; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Base class that contains configuration properties for Git SSH properties - * - * @author Ollie Hughes - */ -public abstract class SshUri { - private String privateKey; - private String uri; - private String hostKeyAlgorithm; - private String hostKey; - private String knownHostsFile; - @Pattern(regexp = "([\\w -]+,)*([\\w -]+)") - private String preferredAuthentications; - private boolean ignoreLocalSshSettings; - private boolean strictHostKeyChecking = true; - - public static SshUriPropertiesBuilder builder() { - return new SshUriPropertiesBuilder(); - } - - public String getUri() { - return this.uri; - } - - public String getHostKeyAlgorithm() { - return this.hostKeyAlgorithm; - } - - public String getHostKey() { - return this.hostKey; - } - - public String getKnownHostsFile() { - return this.knownHostsFile; - } - - public String getPreferredAuthentications() { - return this.preferredAuthentications; - } - - public String getPrivateKey() { - return this.privateKey; - } - - public boolean isIgnoreLocalSshSettings() { - return this.ignoreLocalSshSettings; - } - - public boolean isStrictHostKeyChecking() { - return this.strictHostKeyChecking; - } - - public void setUri(String uri) { - this.uri = uri; - } - - public void setHostKeyAlgorithm(String hostKeyAlgorithm) { - this.hostKeyAlgorithm = hostKeyAlgorithm; - } - - public void setHostKey(String hostKey) { - this.hostKey = hostKey; - } - - public void setKnownHostsFile(String knownHostsFile) { - this.knownHostsFile = knownHostsFile; - } - - public void setPreferredAuthentications(String preferredAuthentications) { - this.preferredAuthentications = preferredAuthentications; - } - - public void setPrivateKey(String privateKey) { - this.privateKey = privateKey; - } - - public void setIgnoreLocalSshSettings(boolean ignoreLocalSshSettings) { - this.ignoreLocalSshSettings = ignoreLocalSshSettings; - } - - public void setStrictHostKeyChecking(boolean strictHostKeyChecking) { - this.strictHostKeyChecking = strictHostKeyChecking; - } - - public String toString() { - return "org.springframework.cloud.config.server.ssh.SshUriProperties(uri=" + this.getUri() - + " hostKeyAlgorithm=" + this.getHostKeyAlgorithm() - + ", hostKey=" + this.getHostKey() - + ", privateKey=" + this.getPrivateKey() - + ", ignoreLocalSshSettings=" + this.isIgnoreLocalSshSettings() - + ", knownHostsFile=" + this.getKnownHostsFile() - + ", preferredAuthentications=" + this.getPreferredAuthentications() - + ", strictHostKeyChecking=" + this.isStrictHostKeyChecking() + ",)"; - } - - public static class SshUriPropertiesBuilder { - private String uri; - private String hostKeyAlgorithm; - private String hostKey; - private String privateKey; - private String knownHostsFile; - private String preferredAuthentications; - private boolean ignoreLocalSshSettings; - private boolean strictHostKeyChecking = true; - private Map repos = new LinkedHashMap<>(); - - SshUriPropertiesBuilder() { - } - - public SshUri.SshUriPropertiesBuilder uri(String uri) { - this.uri = uri; - return this; - } - - public SshUri.SshUriPropertiesBuilder hostKeyAlgorithm(String hostKeyAlgorithm) { - this.hostKeyAlgorithm = hostKeyAlgorithm; - return this; - } - - public SshUri.SshUriPropertiesBuilder hostKey(String hostKey) { - this.hostKey = hostKey; - return this; - } - - public SshUri.SshUriPropertiesBuilder privateKey(String privateKey) { - this.privateKey = privateKey; - return this; - } - - public SshUri.SshUriPropertiesBuilder knownHostsFile(String knownHostsFile) { - this.knownHostsFile = knownHostsFile; - return this; - } - - public SshUri.SshUriPropertiesBuilder preferredAuthentications(String preferredAuthentications) { - this.preferredAuthentications = preferredAuthentications; - return this; - } - - public SshUri.SshUriPropertiesBuilder ignoreLocalSshSettings(boolean ignoreLocalSshSettings) { - this.ignoreLocalSshSettings = ignoreLocalSshSettings; - return this; - } - - public SshUri.SshUriPropertiesBuilder strictHostKeyChecking(boolean strictHostKeyChecking) { - this.strictHostKeyChecking = strictHostKeyChecking; - return this; - } - - public SshUri.SshUriPropertiesBuilder repos(Map repos) { - this.repos = repos; - return this; - } - - public SshUriProperties build() { - SshUriProperties sshUriProperties = new SshUriProperties(); - sshUriProperties.setRepos(repos); - build(sshUriProperties); - return sshUriProperties; - } - - public SshUriNestedRepoProperties buildAsNestedRepo() { - SshUriNestedRepoProperties sshUriNestedRepoProperties = new SshUriNestedRepoProperties(); - build(sshUriNestedRepoProperties); - return sshUriNestedRepoProperties; - } - - private void build(SshUri sshUriNestedRepoProperties) { - sshUriNestedRepoProperties.setUri(uri); - sshUriNestedRepoProperties.setHostKeyAlgorithm(hostKeyAlgorithm); - sshUriNestedRepoProperties.setHostKey(hostKey); - sshUriNestedRepoProperties.setPrivateKey(privateKey); - sshUriNestedRepoProperties.setKnownHostsFile(knownHostsFile); - sshUriNestedRepoProperties.setPreferredAuthentications(preferredAuthentications); - sshUriNestedRepoProperties.setIgnoreLocalSshSettings(ignoreLocalSshSettings); - sshUriNestedRepoProperties.setStrictHostKeyChecking(strictHostKeyChecking); - } - - public String toString() { - return "org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriPropertiesBuilder(uri=" + this.uri - + "hostKeyAlgorithm=" + this.hostKeyAlgorithm - + ", hostKey=" + this.hostKey - + ", privateKey=" + this.privateKey - + ", knownHostsFile=" + this.knownHostsFile - + ", preferredAuthentications=" + this.preferredAuthentications - + ", ignoreLocalSshSettings=" + this.ignoreLocalSshSettings - + ", strictHostKeyChecking=" + this.strictHostKeyChecking - + ", repos=" + this.repos + ")"; - } - } -} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUriProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUriProperties.java deleted file mode 100644 index 7aa9879d..00000000 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUriProperties.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2015 - 2017 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.ssh; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -/** - * Data container for property based SSH config - * - * @author Ollie Hughes - */ -@ConfigurationProperties("spring.cloud.config.server.git") -@Validated -@PrivateKeyIsValid -@HostKeyAndAlgoBothExist -@HostKeyAlgoSupported -@KnownHostsFileIsValid -public class SshUriProperties extends SshUri { - - private Map repos = new LinkedHashMap<>(); - - public Map getRepos() { - return this.repos; - } - - public void setRepos(Map repos) { - this.repos = repos; - } - - public void addRepo(String repoName, SshUriProperties.SshUriNestedRepoProperties properties) { - this.repos.put(repoName, properties); - } - - @Override - public String toString() { - return super.toString() + "{repos=" + repos + "}"; - } - - /** - * Differentiate between sets of properties that are defined in nested Git repos. - * This is to prevent boot from guarding against a potential infinite deserialization of nested properties. - * This sub class differentiates from {@link SshUriProperties} as it does not contain the self mao - */ - public static class SshUriNestedRepoProperties extends SshUri { - - } -} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessor.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessor.java index 36cbf85a..c5465fb1 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessor.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-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. @@ -21,7 +21,9 @@ import java.util.HashMap; import java.util.Map; import org.eclipse.jgit.transport.URIish; -import org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriNestedRepoProperties; + +import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties; +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties; import static org.springframework.cloud.config.server.ssh.SshPropertyValidator.isSshUri; @@ -32,25 +34,25 @@ import static org.springframework.cloud.config.server.ssh.SshPropertyValidator.i */ public class SshUriPropertyProcessor { - private final SshUriProperties sshUriProperties; + private final MultipleJGitEnvironmentProperties sshUriProperties; - public SshUriPropertyProcessor(SshUriProperties sshUriProperties) { + public SshUriPropertyProcessor(MultipleJGitEnvironmentProperties sshUriProperties) { this.sshUriProperties = sshUriProperties; } - public Map getSshKeysByHostname() { + public Map getSshKeysByHostname() { return extractNestedProperties(sshUriProperties); } - private Map extractNestedProperties(SshUriProperties uriProperties) { - Map sshUriPropertyMap = new HashMap<>(); + private Map extractNestedProperties(MultipleJGitEnvironmentProperties uriProperties) { + Map sshUriPropertyMap = new HashMap<>(); String parentUri = uriProperties.getUri(); if (isSshUri(parentUri) && getHostname(parentUri) != null) { sshUriPropertyMap.put(getHostname(parentUri), uriProperties); } - Map repos = uriProperties.getRepos(); + Map repos = uriProperties.getRepos(); if(repos != null) { - for (SshUriNestedRepoProperties repoProperties : repos.values()) { + for (MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties repoProperties : repos.values()) { String repoUri = repoProperties.getUri(); if (isSshUri(repoUri) && getHostname(repoUri) != null) { sshUriPropertyMap.put(getHostname(repoUri), repoProperties); diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/AbstractScmAccessorProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/AbstractScmAccessorProperties.java index 0e050ed8..650fb5cc 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/AbstractScmAccessorProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/AbstractScmAccessorProperties.java @@ -25,14 +25,39 @@ import org.springframework.core.Ordered; public class AbstractScmAccessorProperties implements EnvironmentRepositoryProperties { static final String[] DEFAULT_LOCATIONS = new String[] { "/" }; + /** + * URI of remote repository. + */ private String uri; + /** + * Base directory for local working copy of repository. + */ private File basedir; + /** + * Search paths to use within local working copy. By default searches only the root. + */ private String[] searchPaths = DEFAULT_LOCATIONS.clone();; + /** + * Username for authentication with remote repository. + */ private String username; + /** + * Password for authentication with remote repository. + */ private String password; + /** + * Passphrase for unlocking your ssh private key. + */ private String passphrase; + /** + * Reject incoming SSH host keys from remote servers not in the known host list. + */ private boolean strictHostKeyChecking = true; + + /** The order of the environment repository. */ private int order = Ordered.LOWEST_PRECEDENCE; + + /** The default label to be used with the remore repository */ private String defaultLabel; public String getUri() { diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GitCredentialsProviderFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GitCredentialsProviderFactory.java index 8766255f..f69fc817 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GitCredentialsProviderFactory.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GitCredentialsProviderFactory.java @@ -16,19 +16,21 @@ package org.springframework.cloud.config.server.support; -import static org.springframework.util.StringUtils.hasText; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; + import org.springframework.util.ClassUtils; +import static org.springframework.util.StringUtils.hasText; + /** - * A CredentialsProvider factory for Git repositories. Can handle AWS CodeCommit + * A CredentialsProvider factory for Git repositories. Can handle AWS CodeCommit * repositories and other repositories with username/password. * * @author Don Laidlaw + * @author Gareth Clay * */ public class GitCredentialsProviderFactory { @@ -41,20 +43,43 @@ public class GitCredentialsProviderFactory { * Enabled by default. */ protected boolean awsCodeCommitEnabled = true; - + /** - * Search for a credential provider that will handle the specified URI. If - * not found, and the username has text, then create a default using the - * provided username and password. Otherwise null. + * Search for a credential provider that will handle the specified URI. If not found, + * and the username or passphrase has text, then create a default using the provided + * username and password or passphrase. Otherwise null. * @param uri the URI of the repository (cannot be null) * @param username the username provided for the repository (may be null) * @param password the password provided for the repository (may be null) * @param passphrase the passphrase to unlock the ssh private key (may be null) * @return the first matched credentials provider or the default or null. + * @deprecated in favour of {@link #createFor(String, String, String, String, boolean)} */ - public CredentialsProvider createFor(String uri, - String username, String password, String passphrase) { + @Deprecated + public CredentialsProvider createFor(String uri, String username, String password, + String passphrase) { + return createFor(uri, username, password, passphrase, false); + } + /** + * Search for a credential provider that will handle the specified URI. If not found, + * and the username or passphrase has text, then create a default using the provided + * username and password or passphrase. + * + * If skipSslValidation is true and the URI has an https scheme, the default credential + * provider's behaviour is modified to suppress any SSL validation errors that occur + * when communicating via the URI. + * + * Otherwise null. + * @param uri the URI of the repository (cannot be null) + * @param username the username provided for the repository (may be null) + * @param password the password provided for the repository (may be null) + * @param passphrase the passphrase to unlock the ssh private key (may be null) + * @param skipSslValidation whether to skip SSL validation when connecting via HTTPS + * @return the first matched credentials provider or the default or null. + */ + public CredentialsProvider createFor(String uri, String username, String password, + String passphrase, boolean skipSslValidation) { CredentialsProvider provider = null; if (awsAvailable() && AwsCodeCommitCredentialProvider.canHandle(uri)) { logger.debug("Constructing AwsCodeCommitCredentialProvider for URI " + uri); @@ -72,10 +97,15 @@ public class GitCredentialsProviderFactory { provider = new PassphraseCredentialsProvider(passphrase); } + if (skipSslValidation && GitSkipSslValidationCredentialsProvider.canHandle(uri)) { + logger.debug("Constructing GitSkipSslValidationCredentialsProvider for URI " + + uri); + provider = new GitSkipSslValidationCredentialsProvider(provider); + } else { logger.debug("No credentials provider required for URI " + uri); } - + return provider; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GitSkipSslValidationCredentialsProvider.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GitSkipSslValidationCredentialsProvider.java new file mode 100644 index 00000000..c3d8a61a --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GitSkipSslValidationCredentialsProvider.java @@ -0,0 +1,133 @@ +/* + * 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.ArrayList; +import java.util.List; + +import org.eclipse.jgit.errors.UnsupportedCredentialItem; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.URIish; + +/** + * A {@link CredentialsProvider} that will ignore any SSL validation errors that occur. + * This is primarily intended as a convenience for testing scenarios where self-signed + * certificates are being used. + * + * This class can be used as a decorator for another CredentialsProvider, adding SSL + * validation skipping behaviour to a repository that also requires authentication, for + * example. + * + * @author Gareth Clay + */ +public class GitSkipSslValidationCredentialsProvider extends CredentialsProvider { + + private final CredentialsProvider delegate; + + public GitSkipSslValidationCredentialsProvider(CredentialsProvider delegate) { + this.delegate = delegate; + } + + /** + * This provider can handle uris like https://github.com/org/repo + */ + public static boolean canHandle(String uri) { + return uri != null && uri.toLowerCase().startsWith("https://"); + } + + @Override + public boolean isInteractive() { + return (delegate != null) && delegate.isInteractive(); + } + + @Override + public boolean supports(CredentialItem... items) { + List unprocessedItems = new ArrayList<>(); + + for (CredentialItem item : items) { + if (item instanceof CredentialItem.InformationalMessage + && item.getPromptText() != null && item.getPromptText() + .contains(JGitText.get().sslFailureTrustExplanation)) + continue; + + if (item instanceof CredentialItem.YesNoType && item.getPromptText() != null + && (item.getPromptText().equals(JGitText.get().sslTrustNow) + || item.getPromptText() + .startsWith(stripFormattingPlaceholders( + JGitText.get().sslTrustForRepo)) + || item.getPromptText() + .equals(JGitText.get().sslTrustAlways))) + continue; + + unprocessedItems.add(item); + } + + return unprocessedItems.isEmpty() || (delegate != null + && delegate.supports(unprocessedItems.toArray(new CredentialItem[0]))); + } + + @Override + public boolean get(URIish uri, CredentialItem... items) + throws UnsupportedCredentialItem { + List unprocessedItems = new ArrayList<>(); + + for (CredentialItem item : items) { + if (item instanceof CredentialItem.YesNoType) { + CredentialItem.YesNoType yesNoItem = (CredentialItem.YesNoType) item; + String prompt = yesNoItem.getPromptText(); + if (prompt == null) { + unprocessedItems.add(item); + } + else if (prompt.equals(JGitText.get().sslTrustNow) || prompt.startsWith( + stripFormattingPlaceholders(JGitText.get().sslTrustForRepo))) { + yesNoItem.setValue(true); + } + else if (prompt.equals(JGitText.get().sslTrustAlways)) { + yesNoItem.setValue(false); + } + else { + unprocessedItems.add(item); + } + } + else if (!item.getPromptText() + .contains(JGitText.get().sslFailureTrustExplanation)) { + unprocessedItems.add(item); + } + } + + if (unprocessedItems.isEmpty()) { + return true; + } + if (delegate != null) { + return delegate.get(uri, unprocessedItems.toArray(new CredentialItem[0])); + } + throw new UnsupportedCredentialItem(uri, + unprocessedItems.size() + " credential items not supported"); + } + + @Override + public void reset(URIish uri) { + if (delegate != null) { + delegate.reset(uri); + } + } + + private static String stripFormattingPlaceholders(String string) { + return string.replaceAll("\\s*\\{\\d}\\s*", ""); + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/AdhocTestSuite.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/AdhocTestSuite.java index 98a06fd9..3ca53211 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/AdhocTestSuite.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/AdhocTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-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. @@ -24,7 +24,6 @@ import org.junit.runners.Suite.SuiteClasses; import org.springframework.cloud.config.server.config.ConfigServerHealthIndicatorTests; import org.springframework.cloud.config.server.config.CustomCompositeEnvironmentRepositoryTests; import org.springframework.cloud.config.server.config.CustomEnvironmentRepositoryTests; -import org.springframework.cloud.config.server.config.TransportConfigurationTest; import org.springframework.cloud.config.server.credentials.AwsCodeCommitCredentialsProviderTests; import org.springframework.cloud.config.server.credentials.GitCredentialsProviderFactoryTests; import org.springframework.cloud.config.server.encryption.CipherEnvironmentEncryptorTests; @@ -108,7 +107,6 @@ import org.springframework.cloud.config.server.ssh.SshUriPropertyProcessorTest; ConfigServerHealthIndicatorTests.class, CustomCompositeEnvironmentRepositoryTests.class, CustomEnvironmentRepositoryTests.class, - TransportConfigurationTest.class, BootstrapConfigServerIntegrationTests.class, }) @Ignore diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java index b22b3166..3d755412 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-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. @@ -16,6 +16,9 @@ package org.springframework.cloud.config.server; +import java.io.File; +import java.lang.reflect.Method; + import com.jcraft.jsch.Session; import org.eclipse.jgit.api.TransportConfigCallback; import org.eclipse.jgit.transport.JschConfigSessionFactory; @@ -25,20 +28,22 @@ import org.eclipse.jgit.util.FS; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.config.server.config.TransportConfiguration; +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties; import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository; +import org.springframework.cloud.config.server.ssh.FileBasedSshTransportConfigCallback; +import org.springframework.cloud.config.server.ssh.PropertiesBasedSshTransportConfigCallback; import org.springframework.cloud.config.server.ssh.SshPropertyValidator; -import org.springframework.cloud.config.server.ssh.SshUriProperties; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; -import java.io.File; -import java.lang.reflect.Method; - import static junit.framework.TestCase.assertTrue; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -49,124 +54,251 @@ import static org.mockito.Mockito.verify; */ public class TransportConfigurationIntegrationTests { - @RunWith(SpringRunner.class) - @SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class}, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.config.name:ssh/ssh-private-key-block",}) - @ActiveProfiles({"test", "git"}) public static class PropertyBasedCallbackTest { - @Autowired - private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = {"spring.config.name:ssh/ssh-private-key-block"}) + @ActiveProfiles({"test", "git"}) + public static class StaticTest { - @Test - public void propertyBasedTransportCallbackIsConfigured() throws Exception { - TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); - assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class))); - } - } + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; - @RunWith(SpringRunner.class) - @SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class}, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.config.name:ssh/ssh-private-key-newline" - }) - @ActiveProfiles({"test", "git"}) - public static class PrivateKeyPropertyWithLineBreaks { - - @Autowired - private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; - - @Test - public void privateKeyPropertyWithLineBreaks() throws Exception { - TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); - assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class))); - - TransportConfiguration.PropertiesBasedSshTransportConfigCallback configCallback = - (TransportConfiguration.PropertiesBasedSshTransportConfigCallback) transportConfigCallback; - assertThat(configCallback.getSshUriProperties().getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1))); - } - } - - - @RunWith(SpringRunner.class) - @SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class}, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.config.name:ssh/ssh-nested-settings" - }) - @ActiveProfiles({"test", "git"}) - public static class SshPropertiesWithinNestedRepo { - - @Autowired - private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; - - @Test - public void sshPropertiesWithinNestedRepo() throws Exception { - TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); - assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class))); - - TransportConfiguration.PropertiesBasedSshTransportConfigCallback configCallback = - (TransportConfiguration.PropertiesBasedSshTransportConfigCallback) transportConfigCallback; - SshUriProperties sshUriProperties = configCallback.getSshUriProperties(); - assertThat(sshUriProperties.getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1))); - - assertThat(sshUriProperties.getRepos().get("repo1"), is(notNullValue())); - assertThat(sshUriProperties.getRepos().get("repo1").getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_2))); - } - } - - @RunWith(SpringRunner.class) - @SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class}, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.cloud.config.server.git.uri=git@gitserver.com:team/repo.git", - "spring.cloud.config.server.git.ignoreLocalSshSettings=false",}) - @ActiveProfiles({"test", "git"}) - public static class FileBasedCallbackTest { - - @Autowired - private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; - - @Test - public void fileBasedTransportCallbackIsConfigured() throws Exception { - TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); - assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.FileBasedSshTransportConfigCallback.class))); - } - - - @Test - public void strictHostKeyCheckShouldCheck() throws Exception { - String uri = "git+ssh://git@somegitserver/somegitrepo"; - SshSessionFactory.setInstance(null); - jGitEnvironmentRepository.setUri(uri); - jGitEnvironmentRepository.setBasedir(new File("./mybasedir")); - assertTrue(jGitEnvironmentRepository.isStrictHostKeyChecking()); - jGitEnvironmentRepository.setCloneOnStart(true); - try { - // this will throw but we don't care about connecting. - jGitEnvironmentRepository.afterPropertiesSet(); - } catch (Exception e) { - final OpenSshConfig.Host hc = OpenSshConfig.get(FS.detect()).lookup("github.com"); - JschConfigSessionFactory factory = (JschConfigSessionFactory) SshSessionFactory.getInstance(); - // There's no public method that can be used to inspect the ssh - // configuration, so we'll reflect - // the configure method to allow us to check that the config - // property is set as expected. - Method configure = factory.getClass().getDeclaredMethod("configure", OpenSshConfig.Host.class, - Session.class); - configure.setAccessible(true); - Session session = mock(Session.class); - ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); - configure.invoke(factory, hc, session); - verify(session).setConfig(keyCaptor.capture(), valueCaptor.capture()); - configure.setAccessible(false); - assertTrue("yes".equals(valueCaptor.getValue())); + @Test + public void propertyBasedTransportCallbackIsConfigured() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(PropertiesBasedSshTransportConfigCallback.class))); } } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = {"spring.config.name:ssh/ssh-private-key-block-list"}) + @ActiveProfiles({"test", "composite"}) + public static class ListTest { + + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + + @Test + public void propertyBasedTransportCallbackIsConfigured() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(PropertiesBasedSshTransportConfigCallback.class))); + } + } + + } + + public static class PrivateKeyPropertyWithLineBreaks { + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.config.name:ssh/ssh-private-key-newline" + }) + @ActiveProfiles({"test", "git"}) + public static class StaticTest { + + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + + @Test + public void privateKeyPropertyWithLineBreaks() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(PropertiesBasedSshTransportConfigCallback.class))); + + PropertiesBasedSshTransportConfigCallback configCallback = + (PropertiesBasedSshTransportConfigCallback) transportConfigCallback; + assertThat(configCallback.getSshUriProperties().getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1))); + } + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.config.name:ssh/ssh-private-key-newline-list" + }) + @ActiveProfiles({"test", "composite"}) + public static class ListTest { + + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + + @Test + public void privateKeyPropertyWithLineBreaks() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(PropertiesBasedSshTransportConfigCallback.class))); + + PropertiesBasedSshTransportConfigCallback configCallback = + (PropertiesBasedSshTransportConfigCallback) transportConfigCallback; + assertThat(configCallback.getSshUriProperties().getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1))); + } + } + + } + + public static class SshPropertiesWithinNestedRepo { + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.config.name:ssh/ssh-nested-settings" + }) + @ActiveProfiles({"test", "git"}) + public static class StaticTest { + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + + @Test + public void sshPropertiesWithinNestedRepo() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(PropertiesBasedSshTransportConfigCallback.class))); + + PropertiesBasedSshTransportConfigCallback configCallback = + (PropertiesBasedSshTransportConfigCallback) transportConfigCallback; + MultipleJGitEnvironmentProperties sshUriProperties = configCallback.getSshUriProperties(); + assertThat(sshUriProperties.getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1))); + + assertThat(sshUriProperties.getRepos().get("repo1"), is(notNullValue())); + assertThat(sshUriProperties.getRepos().get("repo1").getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_2))); + } + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.config.name:ssh/ssh-nested-settings-list" + }) + @ActiveProfiles({"test", "composite"}) + public static class ListTest { + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + + @Test + public void sshPropertiesWithinNestedRepo() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(PropertiesBasedSshTransportConfigCallback.class))); + + PropertiesBasedSshTransportConfigCallback configCallback = + (PropertiesBasedSshTransportConfigCallback) transportConfigCallback; + MultipleJGitEnvironmentProperties sshUriProperties = configCallback.getSshUriProperties(); + assertThat(sshUriProperties.getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1))); + + assertThat(sshUriProperties.getRepos().get("repo1"), is(notNullValue())); + assertThat(sshUriProperties.getRepos().get("repo1").getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_2))); + } + } + + } + + public static class FileBasedCallbackTest { + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.cloud.config.server.git.uri=git@gitserver.com:team/repo.git", + "spring.cloud.config.server.git.ignoreLocalSshSettings=false",}) + @ActiveProfiles({"test", "git"}) + public static class StaticTest { + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + + @Test + public void fileBasedTransportCallbackIsConfigured() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(FileBasedSshTransportConfigCallback.class))); + } + + @Test + public void strictHostKeyCheckShouldCheck() throws Exception { + String uri = "git+ssh://git@somegitserver/somegitrepo"; + SshSessionFactory.setInstance(null); + jGitEnvironmentRepository.setUri(uri); + jGitEnvironmentRepository.setBasedir(new File("./mybasedir")); + assertTrue(jGitEnvironmentRepository.isStrictHostKeyChecking()); + jGitEnvironmentRepository.setCloneOnStart(true); + try { + // this will throw but we don't care about connecting. + jGitEnvironmentRepository.afterPropertiesSet(); + } catch (Exception e) { + final OpenSshConfig.Host hc = OpenSshConfig.get(FS.detect()).lookup("github.com"); + JschConfigSessionFactory factory = (JschConfigSessionFactory) SshSessionFactory.getInstance(); + // There's no public method that can be used to inspect the ssh + // configuration, so we'll reflect + // the configure method to allow us to check that the config + // property is set as expected. + Method configure = factory.getClass().getDeclaredMethod("configure", OpenSshConfig.Host.class, + Session.class); + configure.setAccessible(true); + Session session = mock(Session.class); + ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); + configure.invoke(factory, hc, session); + verify(session).setConfig(keyCaptor.capture(), valueCaptor.capture()); + configure.setAccessible(false); + assertTrue("yes".equals(valueCaptor.getValue())); + } + } + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = {ConfigServerApplication.class, SshPropertyValidator.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.cloud.config.server.composite[0].type=git", + "spring.cloud.config.server.composite[0].uri=git@gitserver.com:team/repo.git", + "spring.cloud.config.server.composite[0].ignoreLocalSshSettings=false",}) + @ActiveProfiles({"test", "composite"}) + public static class ListTest { + @Autowired + private MultipleJGitEnvironmentRepository jGitEnvironmentRepository; + + @Test + public void fileBasedTransportCallbackIsConfigured() throws Exception { + TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback(); + assertThat(transportConfigCallback, is(instanceOf(FileBasedSshTransportConfigCallback.class))); + } + + @Test + public void strictHostKeyCheckShouldCheck() throws Exception { + String uri = "git+ssh://git@somegitserver/somegitrepo"; + SshSessionFactory.setInstance(null); + jGitEnvironmentRepository.setUri(uri); + jGitEnvironmentRepository.setBasedir(new File("./mybasedir")); + assertTrue(jGitEnvironmentRepository.isStrictHostKeyChecking()); + jGitEnvironmentRepository.setCloneOnStart(true); + try { + // this will throw but we don't care about connecting. + jGitEnvironmentRepository.afterPropertiesSet(); + } catch (Exception e) { + final OpenSshConfig.Host hc = OpenSshConfig.get(FS.detect()).lookup("github.com"); + JschConfigSessionFactory factory = (JschConfigSessionFactory) SshSessionFactory.getInstance(); + // There's no public method that can be used to inspect the ssh + // configuration, so we'll reflect + // the configure method to allow us to check that the config + // property is set as expected. + Method configure = factory.getClass().getDeclaredMethod("configure", OpenSshConfig.Host.class, + Session.class); + configure.setAccessible(true); + Session session = mock(Session.class); + ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); + configure.invoke(factory, hc, session); + verify(session).setConfig(keyCaptor.capture(), valueCaptor.capture()); + configure.setAccessible(false); + assertTrue("yes".equals(valueCaptor.getValue())); + } + } + } + } private static class TestProperties { diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/TransportConfigurationTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/TransportConfigurationTest.java deleted file mode 100644 index 40296eef..00000000 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/TransportConfigurationTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015 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 org.eclipse.jgit.api.TransportConfigCallback; -import org.junit.Test; -import org.springframework.cloud.config.server.ssh.SshUri; -import org.springframework.cloud.config.server.ssh.SshUriProperties; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.instanceOf; -/** -* @author Ollie Hughes -*/ -public class TransportConfigurationTest { - @Test - public void propertiesBasedSshTransportCallbackCreated() throws Exception { - SshUriProperties ignoreLocalSettings = SshUri.builder() - .uri("user@gitrepo.com:proj/repo") - .ignoreLocalSshSettings(true) - .build(); - TransportConfiguration transportConfiguration = new TransportConfiguration(); - TransportConfigCallback transportConfigCallback = transportConfiguration.propertiesBasedSshTransportCallback(ignoreLocalSettings); - assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class))); - } - - @Test - public void fileBasedSshTransportCallbackCreated() throws Exception { - SshUriProperties dontIgnoreLocalSettings = SshUri.builder() - .uri("user@gitrepo.com:proj/repo") - .ignoreLocalSshSettings(false) - .build(); - - TransportConfiguration transportConfiguration = new TransportConfiguration(); - TransportConfigCallback transportConfigCallback = transportConfiguration.propertiesBasedSshTransportCallback(dontIgnoreLocalSettings); - assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.FileBasedSshTransportConfigCallback.class))); - - } - -} \ No newline at end of file diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/AwsCodeCommitCredentialsProviderTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/AwsCodeCommitCredentialsProviderTests.java index 6eb6018f..9909d491 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/AwsCodeCommitCredentialsProviderTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/AwsCodeCommitCredentialsProviderTests.java @@ -18,16 +18,16 @@ package org.springframework.cloud.config.server.credentials; import java.net.URISyntaxException; +import com.amazonaws.auth.AWSCredentialsProvider; import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.transport.CredentialItem; import org.eclipse.jgit.transport.URIish; import org.junit.Before; import org.junit.Test; + import org.springframework.cloud.config.server.support.AwsCodeCommitCredentialProvider; import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory; -import com.amazonaws.auth.AWSCredentialsProvider; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -56,7 +56,7 @@ public class AwsCodeCommitCredentialsProviderTests { public void init() { GitCredentialsProviderFactory factory = new GitCredentialsProviderFactory(); provider = (AwsCodeCommitCredentialProvider) - factory.createFor(AWS_REPO, USER, PASSWORD, null); + factory.createFor(AWS_REPO, USER, PASSWORD, null, false); } @Test @@ -116,7 +116,7 @@ public class AwsCodeCommitCredentialsProviderTests { public void testUriWithCurlyBracesReturnsTrue() throws UnsupportedCredentialItem, URISyntaxException { GitCredentialsProviderFactory factory = new GitCredentialsProviderFactory(); provider = (AwsCodeCommitCredentialProvider) - factory.createFor(CURLY_BRACES_REPO, USER, PASSWORD, null); + factory.createFor(CURLY_BRACES_REPO, USER, PASSWORD, null, false); CredentialItem[] credentialItems = makeCredentialItems(); assertTrue(provider.get(new URIish(CURLY_BRACES_REPO), credentialItems)); } diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/GitCredentialsProviderFactoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/GitCredentialsProviderFactoryTests.java index 7ed5107e..5718fe01 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/GitCredentialsProviderFactoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/credentials/GitCredentialsProviderFactoryTests.java @@ -20,9 +20,11 @@ import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.junit.Before; import org.junit.Test; + import org.springframework.cloud.config.server.support.AwsCodeCommitCredentialProvider; import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory; import org.springframework.cloud.config.server.support.PassphraseCredentialsProvider; +import org.springframework.cloud.config.server.support.GitSkipSslValidationCredentialsProvider; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -32,13 +34,14 @@ import static org.junit.Assert.assertTrue; /** * @author don laidlaw - * + * @author Gareth Clay */ public class GitCredentialsProviderFactoryTests { private static final String PASSWORD = "secret"; private static final String USER = "test"; private static final String FILE_REPO = "file:///home/user/repo"; - private static final String GIT_REPO = "https://github.com/spring-cloud/spring-cloud-config-test"; + private static final String HTTPS_GIT_REPO = "https://github.com/spring-cloud/spring-cloud-config-test"; + private static final String SSH_GIT_REPO = "git@github.com:spring-cloud/spring-cloud-config-test.git"; private static final String AWS_REPO = "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/test"; private GitCredentialsProviderFactory factory; @@ -50,33 +53,62 @@ public class GitCredentialsProviderFactoryTests { @Test public void testCreateForFileNoUsernameIsNull() { - CredentialsProvider provider = factory.createFor(FILE_REPO, null, null, null); + CredentialsProvider provider = factory.createFor(FILE_REPO, null, null, null, + false); assertNull(provider); } @Test public void testCreateForFileWithUsername() { - CredentialsProvider provider = factory.createFor(FILE_REPO, USER, PASSWORD, null); + CredentialsProvider provider = factory.createFor(FILE_REPO, USER, PASSWORD, null, + false); assertNotNull(provider); assertTrue(provider instanceof UsernamePasswordCredentialsProvider); } @Test public void testCreateForServerNoUsernameIsNull() { - CredentialsProvider provider = factory.createFor(GIT_REPO, null, null, null); + CredentialsProvider provider = factory.createFor(HTTPS_GIT_REPO, null, null, null, + false); assertNull(provider); } @Test public void testCreateForServerWithUsername() { - CredentialsProvider provider = factory.createFor(GIT_REPO, USER, PASSWORD, null); + CredentialsProvider provider = factory.createFor(HTTPS_GIT_REPO, USER, PASSWORD, + null, false); + assertNotNull(provider); + assertTrue(provider instanceof UsernamePasswordCredentialsProvider); + } + + @Test + public void testCreateForHttpsServerWithSkipSslValidation() { + CredentialsProvider provider = factory.createFor(HTTPS_GIT_REPO, USER, PASSWORD, + null, true); + assertNotNull(provider); + assertTrue(provider instanceof GitSkipSslValidationCredentialsProvider); + } + + @Test + public void testCreateForHttpsServerWithoutSpecifyingSkipSslValidation() { + CredentialsProvider provider = factory.createFor(HTTPS_GIT_REPO, USER, PASSWORD, null); + assertNotNull(provider); + assertTrue("deprecated createFor() should not enable ssl validation skipping", + provider instanceof UsernamePasswordCredentialsProvider); + } + + @Test + public void testCreateForSshServerWithSkipSslValidation() { + CredentialsProvider provider = factory.createFor(SSH_GIT_REPO, USER, PASSWORD, + null, true); assertNotNull(provider); assertTrue(provider instanceof UsernamePasswordCredentialsProvider); } @Test public void testCreateForAwsNoUsername() { - CredentialsProvider provider = factory.createFor(AWS_REPO, null, null, null); + CredentialsProvider provider = factory.createFor(AWS_REPO, null, null, null, + false); assertNotNull(provider); assertTrue(provider instanceof AwsCodeCommitCredentialProvider); AwsCodeCommitCredentialProvider aws = (AwsCodeCommitCredentialProvider) provider; @@ -86,7 +118,8 @@ public class GitCredentialsProviderFactoryTests { @Test public void testCreateForAwsWithUsername() { - CredentialsProvider provider = factory.createFor(AWS_REPO, USER, PASSWORD, null); + CredentialsProvider provider = factory.createFor(AWS_REPO, USER, PASSWORD, null, + false); assertNotNull(provider); assertTrue(provider instanceof AwsCodeCommitCredentialProvider); AwsCodeCommitCredentialProvider aws = (AwsCodeCommitCredentialProvider) provider; @@ -97,20 +130,21 @@ public class GitCredentialsProviderFactoryTests { @Test public void testCreateForAwsDisabled() { factory.setAwsCodeCommitEnabled(false); - CredentialsProvider provider = factory.createFor(AWS_REPO, null, null, null); + CredentialsProvider provider = factory.createFor(AWS_REPO, null, null, null, + false); assertNull(provider); - provider = factory.createFor(AWS_REPO, USER, PASSWORD, null); + provider = factory.createFor(AWS_REPO, USER, PASSWORD, null, false); assertNotNull(provider); assertTrue(provider instanceof UsernamePasswordCredentialsProvider); } @Test public void testCreatePassphraseCredentialProvider() { - CredentialsProvider provider = factory.createFor(GIT_REPO, null, null, PASSWORD); + CredentialsProvider provider = factory.createFor(HTTPS_GIT_REPO, null, null, + PASSWORD, false); assertNotNull(provider); assertTrue(provider instanceof PassphraseCredentialsProvider); } - @Test public void testIsAwsCodeCommitEnabled() { diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerIntegrationTests.java index 8aa218ff..0f3c01bc 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerIntegrationTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerIntegrationTests.java @@ -16,16 +16,17 @@ package org.springframework.cloud.config.server.environment; +import java.util.HashMap; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.environment.PropertySource; import org.springframework.cloud.config.server.environment.EnvironmentControllerIntegrationTests.ControllerConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -52,23 +53,26 @@ public class EnvironmentControllerIntegrationTests { private MockMvc mvc; @Autowired private EnvironmentRepository repository; + + private Environment environment = new Environment("foo", "default"); @Before public void init() { Mockito.reset(this.repository); this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + environment.add(new PropertySource("foo", new HashMap<>())); } @Test public void environmentNoLabel() throws Exception { - Mockito.when(this.repository.findOne("foo", "default", null)).thenReturn(new Environment("foo", "default")); + Mockito.when(this.repository.findOne("foo", "default", null)).thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/foo/default")).andExpect(MockMvcResultMatchers.status().isOk()); Mockito.verify(this.repository).findOne("foo", "default", null); } @Test public void propertiesNoLabel() throws Exception { - Mockito.when(this.repository.findOne("foo", "default", null)).thenReturn(new Environment("foo", "default")); + Mockito.when(this.repository.findOne("foo", "default", null)).thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/foo-default.properties")) .andExpect(MockMvcResultMatchers.status().isOk()); Mockito.verify(this.repository).findOne("foo", "default", null); @@ -76,7 +80,7 @@ public class EnvironmentControllerIntegrationTests { @Test public void propertiesLabel() throws Exception { - Mockito.when(this.repository.findOne("foo", "default", "label")).thenReturn(new Environment("foo", "default")); + Mockito.when(this.repository.findOne("foo", "default", "label")).thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/label/foo-default.properties")) .andExpect(MockMvcResultMatchers.status().isOk()); Mockito.verify(this.repository).findOne("foo", "default", "label"); @@ -84,8 +88,10 @@ public class EnvironmentControllerIntegrationTests { @Test public void propertiesLabelWhenApplicationNameContainsHyphen() throws Exception { + Environment environment = new Environment("foo-bar", "default"); + environment.add(new PropertySource("foo", new HashMap<>())); Mockito.when(this.repository.findOne("foo-bar", "default", "label")) - .thenReturn(new Environment("foo-bar", "default")); + .thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/label/foo-bar-default.properties")) .andExpect(MockMvcResultMatchers.status().isOk()); Mockito.verify(this.repository).findOne("foo-bar", "default", "label"); @@ -93,8 +99,9 @@ public class EnvironmentControllerIntegrationTests { @Test public void propertiesLabelWithSlash() throws Exception { + Mockito.when(this.repository.findOne("foo", "default", "label/spam")) - .thenReturn(new Environment("foo", "default")); + .thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/label(_)spam/foo-default.properties")) .andExpect(MockMvcResultMatchers.status().isOk()); Mockito.verify(this.repository).findOne("foo", "default", "label/spam"); @@ -103,7 +110,7 @@ public class EnvironmentControllerIntegrationTests { @Test public void environmentWithLabel() throws Exception { Mockito.when(this.repository.findOne("foo", "default", "awesome")) - .thenReturn(new Environment("foo", "default")); + .thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/foo/default/awesome")) .andExpect(MockMvcResultMatchers.status().isOk()); } @@ -126,7 +133,7 @@ public class EnvironmentControllerIntegrationTests { @Test public void environmentWithLabelContainingPeriod() throws Exception { - Mockito.when(this.repository.findOne("foo", "default", "1.0.0")).thenReturn(new Environment("foo", "default")); + Mockito.when(this.repository.findOne("foo", "default", "1.0.0")).thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/foo/default/1.0.0")) .andExpect(MockMvcResultMatchers.status().isOk()); } @@ -134,7 +141,7 @@ public class EnvironmentControllerIntegrationTests { @Test public void environmentWithLabelContainingSlash() throws Exception { Mockito.when(this.repository.findOne("foo", "default", "feature/puff")) - .thenReturn(new Environment("foo", "default")); + .thenReturn(this.environment); this.mvc.perform(MockMvcRequestBuilders.get("/foo/default/feature(_)puff")) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("\"propertySources\":"))); @@ -142,8 +149,10 @@ public class EnvironmentControllerIntegrationTests { @Test public void environmentWithApplicationContainingSlash() throws Exception { + Environment environment = new Environment("foo/app", "default"); + environment.add(new PropertySource("foo", new HashMap<>())); Mockito.when(this.repository.findOne("foo/app", "default", null)) - .thenReturn(new Environment("foo/app", "default")); + .thenReturn(environment); this.mvc.perform(MockMvcRequestBuilders.get("/foo(_)app/default")) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("\"propertySources\":"))); diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java index 7e1d92e5..d6c8cbbe 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/EnvironmentControllerTests.java @@ -65,6 +65,7 @@ public class EnvironmentControllerTests { @Before public void init() { this.controller = new EnvironmentController(this.repository); + environment.add(new PropertySource("foo", new HashMap<>())); } @After @@ -195,27 +196,47 @@ public class EnvironmentControllerTests { assertThat(environment.getProfiles(), equalTo(new String[] { "master" })); assertThat(environment.getLabel(), equalTo("master")); assertThat(environment.getVersion(), nullValue()); - assertThat(environment.getPropertySources(), hasSize(2)); + assertThat(environment.getPropertySources(), hasSize(3)); assertThat(environment.getPropertySources().get(0).getName(), equalTo("two")); assertThat(environment.getPropertySources().get(0).getSource().entrySet(), hasSize(2)); - assertThat(environment.getPropertySources().get(1).getName(), equalTo("one")); - assertThat(environment.getPropertySources().get(1).getSource().entrySet(), + assertThat(environment.getPropertySources().get(2).getName(), equalTo("one")); + assertThat(environment.getPropertySources().get(2).getSource().entrySet(), hasSize(3)); } @Test public void testNameWithSlash() { - this.controller.labelled("foo(_)spam", "bar", "two"); - - Mockito.verify(this.repository).findOne("foo/spam", "bar", "two"); - } + Mockito.when(this.repository.findOne("foo/spam", "bar", "two")).thenReturn(this.environment); + Environment returnedEnvironment = this.controller.labelled("foo(_)spam", "bar", "two"); + + assertEquals(this.environment.getLabel(), returnedEnvironment.getLabel()); + assertEquals(this.environment.getName(), returnedEnvironment.getName()); + + } + @Test(expected=EnvironmentNotFoundException.class) + public void testEnvironmentNotFound() { + this.controller.setAcceptEmpty(false); + this.controller.labelled("foo", "bar", null); + } + @Test + public void testwithValidEnvironment() { + Mockito.when(this.repository.findOne("foo", "bar",null)) + .thenReturn(this.environment); + Environment environment = this.controller.labelled("foo", "bar", null); + assertThat(environment, not(nullValue())); + + } @Test public void testLabelWithSlash() { - this.controller.labelled("foo", "bar", "two(_)spam"); - Mockito.verify(this.repository).findOne("foo", "bar", "two/spam"); + Mockito.when(this.repository.findOne("foo", "bar", "two/spam")).thenReturn(this.environment); + + Environment returnedEnvironment = this.controller.labelled("foo", "bar", "two(_)spam"); + + assertEquals(this.environment.getLabel(), returnedEnvironment.getLabel()); + assertEquals(this.environment.getName(), returnedEnvironment.getName()); } @Test @@ -491,6 +512,15 @@ public class EnvironmentControllerTests { mvc.perform(MockMvcRequestBuilders.get("/foo/bar/other")) .andExpect(MockMvcResultMatchers.status().isOk()); } + + @Test + public void environmentMissing() throws Exception { + Mockito.when(this.repository.findOne("foo1", "notfound", null)) + .thenThrow(new EnvironmentNotFoundException("Missing Environment")); + MockMvc mvc = MockMvcBuilders.standaloneSetup(this.controller).build(); + mvc.perform(MockMvcRequestBuilders.get("/foo1/notfound")) + .andExpect(MockMvcResultMatchers.status().isNotFound()); + } @Test public void mappingForYaml() throws Exception { diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositorySslTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositorySslTests.java new file mode 100644 index 00000000..cb744124 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositorySslTests.java @@ -0,0 +1,110 @@ +/* + * 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.net.URL; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jgit.internal.storage.file.FileRepository; +import org.eclipse.jgit.junit.http.SimpleHttpServer; +import org.eclipse.jgit.lib.Repository; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +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.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +public class JGitEnvironmentRepositorySslTests { + + private static SimpleHttpServer server; + + @BeforeClass + public static void setup() throws Exception { + URL repoUrl = JGitEnvironmentRepositorySslTests.class + .getResource("/test1-config-repo/git"); + Repository repo = new FileRepository(new File(repoUrl.toURI())); + server = new SimpleHttpServer(repo, true); + server.start(); + } + + @AfterClass + public static void teardown() throws Exception { + server.stop(); + } + + @Test(expected = CertificateException.class) + public void selfSignedCertIsRejected() throws Throwable { + ConfigurableApplicationContext context = new SpringApplicationBuilder( + TestConfiguration.class).properties(configServerProperties()) + .web(WebApplicationType.NONE).run(); + + JGitEnvironmentRepository repository = context + .getBean(JGitEnvironmentRepository.class); + + try { + repository.findOne("bar", "staging", "master"); + } + catch (Throwable e) { + while (e.getCause() != null) { + e = e.getCause(); + if (e instanceof CertificateException) + break; + } + throw e; + } + } + + @Test + public void selfSignedCertWithSkipSslValidationIsAccepted() { + ConfigurableApplicationContext context = new SpringApplicationBuilder( + TestConfiguration.class) + .properties(configServerProperties( + "spring.cloud.config.server.git.skipSslValidation=true")) + .web(WebApplicationType.NONE).run(); + + JGitEnvironmentRepository repository = context + .getBean(JGitEnvironmentRepository.class); + repository.findOne("bar", "staging", "master"); + } + + private static String[] configServerProperties(String... extraProperties) { + List properties = new ArrayList<>(Arrays.asList(extraProperties)); + properties.add("spring.cloud.config.server.git.uri=" + server.getSecureUri()); + properties.add("spring.cloud.config.server.git.username=agitter"); + properties.add("spring.cloud.config.server.git.password=letmein"); + return properties.toArray(new String[0]); + } + + @Configuration + @EnableConfigurationProperties(ConfigServerProperties.class) + @Import({ PropertyPlaceholderAutoConfiguration.class, + EnvironmentRepositoryConfiguration.class }) + static class TestConfiguration { + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryTests.java index 61f65557..f8bd9b50 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryTests.java @@ -38,7 +38,6 @@ import org.eclipse.jgit.api.StatusCommand; import org.eclipse.jgit.api.TransportConfigCallback; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; -import org.eclipse.jgit.api.errors.NoMessageException; import org.eclipse.jgit.api.errors.NotMergedException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.lib.ObjectId; @@ -60,17 +59,17 @@ import org.junit.rules.ExpectedException; import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.config.server.support.AwsCodeCommitCredentialProvider; -import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory; +import org.springframework.cloud.config.server.support.GitSkipSslValidationCredentialsProvider; import org.springframework.cloud.config.server.support.PassphraseCredentialsProvider; import org.springframework.cloud.config.server.test.ConfigServerTestUtils; import org.springframework.core.env.StandardEnvironment; + import static junit.framework.TestCase.assertTrue; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; -import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -82,7 +81,7 @@ import static org.mockito.Mockito.when; /** * @author Dave Syer - * + * @author Gareth Clay */ public class JGitEnvironmentRepositoryTests { @@ -332,6 +331,70 @@ public class JGitEnvironmentRepositoryTests { assertThat("shouldPull was false", shouldPull, is(true)); } + @Test + public void shouldNotRefresh() throws Exception { + Git git = mock(Git.class); + StatusCommand statusCommand = mock(StatusCommand.class); + Status status = mock(Status.class); + Repository repository = mock(Repository.class); + StoredConfig storedConfig = mock(StoredConfig.class); + + when(git.status()).thenReturn(statusCommand); + when(git.getRepository()).thenReturn(repository); + when(repository.getConfig()).thenReturn(storedConfig); + when(storedConfig.getString("remote", "origin", "url")).thenReturn("http://example/git"); + when(statusCommand.call()).thenReturn(status); + when(status.isClean()).thenReturn(true); + + JGitEnvironmentProperties properties = new JGitEnvironmentProperties(); + properties.setRefreshRate(2); + + JGitEnvironmentRepository repo = new JGitEnvironmentRepository(this.environment, properties); + + repo.setLastRefresh(System.currentTimeMillis() - 5000); + + boolean shouldPull = repo.shouldPull(git); + + assertThat("shouldPull was false", shouldPull, is(true)); + + repo.setRefreshRate(30); + + shouldPull = repo.shouldPull(git); + + assertThat("shouldPull was true", shouldPull, is(false)); + } + + @Test + public void shouldUpdateLastRefresh() throws Exception { + Git git = mock(Git.class); + StatusCommand statusCommand = mock(StatusCommand.class); + Status status = mock(Status.class); + Repository repository = mock(Repository.class); + StoredConfig storedConfig = mock(StoredConfig.class); + FetchCommand fetchCommand = mock(FetchCommand.class); + FetchResult fetchResult = mock(FetchResult.class); + + when(git.status()).thenReturn(statusCommand); + when(git.getRepository()).thenReturn(repository); + when(fetchCommand.call()).thenReturn(fetchResult); + when(git.fetch()).thenReturn(fetchCommand); + when(repository.getConfig()).thenReturn(storedConfig); + when(storedConfig.getString("remote", "origin", "url")).thenReturn("http://example/git"); + when(statusCommand.call()).thenReturn(status); + when(status.isClean()).thenReturn(true); + + JGitEnvironmentProperties properties = new JGitEnvironmentProperties(); + properties.setRefreshRate(1000); + JGitEnvironmentRepository repo = new JGitEnvironmentRepository(this.environment, properties); + + repo.setLastRefresh(0); + repo.fetch(git, "master"); + + long timeDiff = System.currentTimeMillis() - repo.getLastRefresh(); + + assertThat("time difference ("+timeDiff+") was longer than 1 second", timeDiff < 1000L, is(true)); + } + @Test public void testFetchException() throws Exception { @@ -617,7 +680,6 @@ public class JGitEnvironmentRepositoryTests { public void gitCredentialsProviderFactoryCreatesPassphraseProvider() throws Exception { final String passphrase = "mypassphrase"; final String gitUri = "git+ssh://git@somegitserver/somegitrepo"; - GitCredentialsProviderFactory credentialsFactory = new GitCredentialsProviderFactory(); Git mockGit = mock(Git.class); MockCloneCommand mockCloneCommand = new MockCloneCommand(mockGit); @@ -626,7 +688,7 @@ public class JGitEnvironmentRepositoryTests { envRepository.setGitFactory(new MockGitFactory(mockGit, mockCloneCommand)); envRepository.setUri(gitUri); envRepository.setBasedir(new File("./mybasedir")); - envRepository.setGitCredentialsProvider(credentialsFactory.createFor(gitUri, null, null, passphrase)); + envRepository.setPassphrase(passphrase); envRepository.setCloneOnStart(true); envRepository.afterPropertiesSet(); @@ -646,7 +708,6 @@ public class JGitEnvironmentRepositoryTests { @Test public void gitCredentialsProviderFactoryCreatesUsernamePasswordProvider() throws Exception { - GitCredentialsProviderFactory credentialsFactory = new GitCredentialsProviderFactory(); Git mockGit = mock(Git.class); MockCloneCommand mockCloneCommand = new MockCloneCommand(mockGit); final String username = "someuser"; @@ -657,8 +718,8 @@ public class JGitEnvironmentRepositoryTests { envRepository.setGitFactory(new MockGitFactory(mockGit, mockCloneCommand)); envRepository.setUri("git+ssh://git@somegitserver/somegitrepo"); envRepository.setBasedir(new File("./mybasedir")); - envRepository.setGitCredentialsProvider( - credentialsFactory.createFor(envRepository.getUri(), username, password, null)); + envRepository.setUsername(username); + envRepository.setPassword(password); envRepository.setCloneOnStart(true); envRepository.afterPropertiesSet(); @@ -678,7 +739,6 @@ public class JGitEnvironmentRepositoryTests { @Test public void gitCredentialsProviderFactoryCreatesAwsCodeCommitProvider() throws Exception { - GitCredentialsProviderFactory credentialsFactory = new GitCredentialsProviderFactory(); Git mockGit = mock(Git.class); MockCloneCommand mockCloneCommand = new MockCloneCommand(mockGit); final String awsUri = "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/test"; @@ -687,12 +747,44 @@ public class JGitEnvironmentRepositoryTests { new JGitEnvironmentProperties()); envRepository.setGitFactory(new MockGitFactory(mockGit, mockCloneCommand)); envRepository.setUri(awsUri); - envRepository.setGitCredentialsProvider(credentialsFactory.createFor(envRepository.getUri(), null, null, null)); envRepository.setCloneOnStart(true); envRepository.afterPropertiesSet(); assertTrue(mockCloneCommand.getCredentialsProvider() instanceof AwsCodeCommitCredentialProvider); + } + @Test + public void gitCredentialsProviderFactoryCreatesSkipSslValidationProvider() + throws Exception { + Git mockGit = mock(Git.class); + MockCloneCommand mockCloneCommand = new MockCloneCommand(mockGit); + final String username = "someuser"; + final String password = "mypassword"; + + JGitEnvironmentRepository envRepository = new JGitEnvironmentRepository( + this.environment, new JGitEnvironmentProperties()); + envRepository.setGitFactory(new MockGitFactory(mockGit, mockCloneCommand)); + envRepository.setUri("https://somegitserver/somegitrepo"); + envRepository.setBasedir(new File("./mybasedir")); + envRepository.setUsername(username); + envRepository.setPassword(password); + envRepository.setSkipSslValidation(true); + envRepository.setCloneOnStart(true); + envRepository.afterPropertiesSet(); + + CredentialsProvider provider = mockCloneCommand.getCredentialsProvider(); + + assertTrue(provider instanceof GitSkipSslValidationCredentialsProvider); + + CredentialItem.Username usernameCredential = new CredentialItem.Username(); + CredentialItem.Password passwordCredential = new CredentialItem.Password(); + assertTrue(provider.supports(usernameCredential)); + assertTrue(provider.supports(passwordCredential)); + + provider.get(new URIish(), usernameCredential); + assertEquals(usernameCredential.getValue(), username); + provider.get(new URIish(), passwordCredential); + assertEquals(String.valueOf(passwordCredential.getValue()), password); } @Test diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryIntegrationTests.java index 30acff54..a81a3d82 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryIntegrationTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryIntegrationTests.java @@ -105,6 +105,26 @@ public class MultipleJGitEnvironmentRepositoryIntegrationTests { assertEquals(2, environment.getPropertySources().size()); } + @Test + public void mappingRepoWithRefreshRate() throws IOException { + String defaultRepoUri = ConfigServerTestUtils.prepareLocalRepo("config-repo"); + String test1RepoUri = ConfigServerTestUtils.prepareLocalRepo("test1-config-repo"); + + Map repoMapping = new LinkedHashMap(); + repoMapping.put("spring.cloud.config.server.git.repos[test1].pattern", "*test1*"); + repoMapping.put("spring.cloud.config.server.git.repos[test1].uri", test1RepoUri); + repoMapping.put("spring.cloud.config.server.git.refresh-rate", "30"); + this.context = new SpringApplicationBuilder(TestConfiguration.class).web(WebApplicationType.NONE) + .properties("spring.cloud.config.server.git.uri:" + defaultRepoUri) + .properties(repoMapping).run(); + EnvironmentRepository repository = this.context + .getBean(EnvironmentRepository.class); + repository.findOne("test1-svc", "staging", "master"); + Environment environment = repository.findOne("test1-svc", "staging", "master"); + assertEquals(2, environment.getPropertySources().size()); + assertEquals(((MultipleJGitEnvironmentRepository) repository).getRepos().get("test1").getRefreshRate(), 30); + } + @Test public void mappingRepoWithProfile() throws IOException { String defaultRepoUri = ConfigServerTestUtils.prepareLocalRepo("config-repo"); diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryTests.java index ea9ccefc..e243340c 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryTests.java @@ -36,6 +36,7 @@ import org.springframework.core.env.StandardEnvironment; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -46,6 +47,7 @@ import static org.mockito.Mockito.when; * @author Andy Chan (iceycake) * @author Dave Syer * @author Spencer Gibb + * @author Gareth Clay * */ public class MultipleJGitEnvironmentRepositoryTests { @@ -204,6 +206,85 @@ public class MultipleJGitEnvironmentRepositoryTests { assertEquals(repo2.getTransportConfigCallback(), mockCallback2); } + @Test + public void setCorrectCredentials() throws Exception { + final String repo1Username = "repo1-username"; + final String repo1Password = "repo1-password"; + final String repo2Passphrase = "repo2-passphrase"; + final String multiRepoUsername = "multi-repo-username"; + final String multiRepoPassword = "multi-repo-password"; + final String multiRepoPassphrase = "multi-repo-passphrase"; + PatternMatchingJGitEnvironmentRepository repo1 = createRepository("test1", + "*test1*", "test1Uri"); + repo1.setUsername(repo1Username); + repo1.setPassword(repo1Password); + PatternMatchingJGitEnvironmentRepository repo2 = createRepository("test2", + "*test2*", "test2Uri"); + repo2.setPassphrase(repo2Passphrase); + + Map repos = new HashMap<>(); + repos.put("test1", repo1); + repos.put("test2", repo2); + + this.repository.setRepos(repos); + this.repository.setUsername(multiRepoUsername); + this.repository.setPassword(multiRepoPassword); + this.repository.setPassphrase(multiRepoPassphrase); + this.repository.afterPropertiesSet(); + + assertEquals("Repo1 has its own username which should not be overwritten", + repo1.getUsername(), repo1Username); + assertEquals("Repo1 has its own password which should not be overwritten", + repo1.getPassword(), repo1Password); + assertEquals( + "Repo1 did not specify a passphrase so this should have been copied from the multi repo", + repo1.getPassphrase(), multiRepoPassphrase); + assertEquals( + "Repo2 did not specify a username so this should have been copied from the multi repo", + repo2.getUsername(), multiRepoUsername); + assertEquals( + "Repo2 did not specify a username so this should have been copied from the multi repo", + repo2.getPassword(), multiRepoPassword); + assertEquals( + "Repo2 has its own passphrase which should not have been overwritten", + repo2.getPassphrase(), repo2Passphrase); + } + + @Test + public void setSkipSslValidation() throws Exception { + final boolean repo1SkipSslValidation = false; + final boolean repo2SkipSslValidation = true; + PatternMatchingJGitEnvironmentRepository repo1 = createRepository("test1", + "*test1*", "test1Uri"); + repo1.setSkipSslValidation(repo1SkipSslValidation); + PatternMatchingJGitEnvironmentRepository repo2 = createRepository("test2", + "*test2*", "test2Uri"); + repo2.setSkipSslValidation(repo2SkipSslValidation); + + Map repos = new HashMap<>(); + repos.put("test1", repo1); + repos.put("test2", repo2); + + this.repository.setRepos(repos); + this.repository.setSkipSslValidation(false); + this.repository.afterPropertiesSet(); + assertFalse( + "If skip SSL validation is false at multi-repo level, then per-repo settings take priority", + repo1.isSkipSslValidation()); + assertTrue( + "If skip SSL validation is false at multi-repo level, then per-repo settings take priority", + repo2.isSkipSslValidation()); + + this.repository.setSkipSslValidation(true); + this.repository.afterPropertiesSet(); + assertTrue( + "If explicitly set to skip SSL validation at the multi-repo level, then apply same setting to sub-repos", + repo1.isSkipSslValidation()); + assertTrue( + "If explicitly set to skip SSL validation at the multi-repo level, then apply same setting to sub-repos", + repo2.isSkipSslValidation()); + } + @Test // test for gh-700 public void basedirCreatedIfNotExists() throws Exception { diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java index c5887f84..703e1ec1 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java @@ -1,14 +1,16 @@ package org.springframework.cloud.config.server.environment; -import java.io.IOException; import java.util.HashMap; import java.util.Map; +import javax.servlet.http.HttpServletRequest; + import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.VaultResponse; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -17,6 +19,10 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.client.RestTemplate; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * @author Spencer Gibb @@ -27,133 +33,170 @@ public class VaultEnvironmentRepositoryTests { @Before public void init() {} - @Test - public void testFindOneNoDefaultKey() throws IOException { - MockHttpServletRequest configRequest = new MockHttpServletRequest(); - configRequest.addHeader("X-CONFIG-TOKEN", "mytoken"); - RestTemplate rest = Mockito.mock(RestTemplate.class); - - ResponseEntity myAppResp = Mockito.mock(ResponseEntity.class); - Mockito.when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); - VaultEnvironmentRepository.VaultResponse myAppVaultResp = Mockito.mock(VaultEnvironmentRepository.VaultResponse.class); - Mockito.when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); - Mockito.when(myAppResp.getBody()).thenReturn(myAppVaultResp); - Mockito.when(rest.exchange(Mockito.eq("http://127.0.0.1:8200/v1/{backend}/{key}"), - Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(VaultEnvironmentRepository.VaultResponse.class), - Mockito.eq("secret"), Mockito.eq("myapp"))).thenReturn(myAppResp); - - ResponseEntity appResp = Mockito.mock(ResponseEntity.class); - Mockito.when(appResp.getStatusCode()).thenReturn(HttpStatus.OK); - VaultEnvironmentRepository.VaultResponse appVaultResp = Mockito.mock(VaultEnvironmentRepository.VaultResponse.class); - Mockito.when(appVaultResp.getData()).thenReturn("{\"def-foo\":\"def-bar\"}"); - Mockito.when(appResp.getBody()).thenReturn(appVaultResp); - Mockito.when(rest.exchange(Mockito.eq("http://127.0.0.1:8200/v1/{backend}/{key}"), - Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(VaultEnvironmentRepository.VaultResponse.class), - Mockito.eq("secret"), Mockito.eq("application"))).thenReturn(appResp); - - VaultEnvironmentRepository repo = new VaultEnvironmentRepository(configRequest, new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); - - Environment e = repo.findOne("myapp", null, null); - assertEquals("Name should be the same as the application argument", "myapp", e.getName()); - assertEquals("Properties for specified application and default application with key 'application' should be returned", 2, e.getPropertySources().size()); - Map firstResult = new HashMap(); - firstResult.put("foo", "bar"); - assertEquals("Properties for specified application should be returned in priority position", firstResult, e.getPropertySources().get(0).getSource()); - - Map secondResult = new HashMap(); - secondResult.put("def-foo", "def-bar"); - assertEquals("Properties for default application with key 'application' should be returned in second position", secondResult, e.getPropertySources().get(1).getSource()); + @SuppressWarnings("unchecked") + private ObjectProvider mockProvide(HttpServletRequest request) { + ObjectProvider objectProvider = mock(ObjectProvider.class); + when(objectProvider.getIfAvailable()).thenReturn(request); + return objectProvider; } @Test - public void testFindOneDefaultKeySetAndDifferentToApplication() throws IOException { + @SuppressWarnings("unchecked") + public void testFindOneNoDefaultKey() { MockHttpServletRequest configRequest = new MockHttpServletRequest(); configRequest.addHeader("X-CONFIG-TOKEN", "mytoken"); - RestTemplate rest = Mockito.mock(RestTemplate.class); - ResponseEntity myAppResp = Mockito.mock(ResponseEntity.class); - Mockito.when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); - VaultEnvironmentRepository.VaultResponse myAppVaultResp = Mockito.mock(VaultEnvironmentRepository.VaultResponse.class); - Mockito.when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); - Mockito.when(myAppResp.getBody()).thenReturn(myAppVaultResp); - Mockito.when(rest.exchange(Mockito.eq("http://127.0.0.1:8200/v1/{backend}/{key}"), - Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(VaultEnvironmentRepository.VaultResponse.class), - Mockito.eq("secret"), Mockito.eq("myapp"))).thenReturn(myAppResp); + RestTemplate rest = mock(RestTemplate.class); - ResponseEntity myDefaultKeyResp = Mockito.mock(ResponseEntity.class); - Mockito.when(myDefaultKeyResp.getStatusCode()).thenReturn(HttpStatus.OK); - VaultEnvironmentRepository.VaultResponse myDefaultKeyVaultResp = Mockito.mock(VaultEnvironmentRepository.VaultResponse.class); - Mockito.when(myDefaultKeyVaultResp.getData()).thenReturn("{\"def-foo\":\"def-bar\"}"); - Mockito.when(myDefaultKeyResp.getBody()).thenReturn(myDefaultKeyVaultResp); - Mockito.when(rest.exchange(Mockito.eq("http://127.0.0.1:8200/v1/{backend}/{key}"), - Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(VaultEnvironmentRepository.VaultResponse.class), - Mockito.eq("secret"), Mockito.eq("mydefaultkey"))).thenReturn(myDefaultKeyResp); + ResponseEntity myAppResp = mock(ResponseEntity.class); + when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse myAppVaultResp = mock(VaultResponse.class); + when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); + when(myAppResp.getBody()).thenReturn(myAppVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("myapp"))).thenReturn(myAppResp); - VaultEnvironmentRepository repo = new VaultEnvironmentRepository(configRequest, new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); + ResponseEntity appResp = mock(ResponseEntity.class); + when(appResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse appVaultResp = mock(VaultResponse.class); + when(appVaultResp.getData()).thenReturn("{\"def-foo\":\"def-bar\"}"); + when(appResp.getBody()).thenReturn(appVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("application"))).thenReturn(appResp); + + VaultEnvironmentRepository repo = new VaultEnvironmentRepository(mockProvide(configRequest), + new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); + + Environment e = repo.findOne("myapp", null, null); + assertEquals("Name should be the same as the application argument", "myapp", e.getName()); + assertEquals("Properties for specified application and default application with key 'application' should be returned", + 2, e.getPropertySources().size()); + Map firstResult = new HashMap(); + firstResult.put("foo", "bar"); + assertEquals("Properties for specified application should be returned in priority position", + firstResult, e.getPropertySources().get(0).getSource()); + + Map secondResult = new HashMap(); + secondResult.put("def-foo", "def-bar"); + assertEquals("Properties for default application with key 'application' should be returned in second position", + secondResult, e.getPropertySources().get(1).getSource()); + } + + @Test + @SuppressWarnings("unchecked") + public void testFindOneDefaultKeySetAndDifferentToApplication() { + MockHttpServletRequest configRequest = new MockHttpServletRequest(); + configRequest.addHeader("X-CONFIG-TOKEN", "mytoken"); + RestTemplate rest = mock(RestTemplate.class); + + ResponseEntity myAppResp = mock(ResponseEntity.class); + when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse myAppVaultResp = mock(VaultResponse.class); + when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); + when(myAppResp.getBody()).thenReturn(myAppVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("myapp"))).thenReturn(myAppResp); + + ResponseEntity myDefaultKeyResp = mock(ResponseEntity.class); + when(myDefaultKeyResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse myDefaultKeyVaultResp = mock(VaultResponse.class); + when(myDefaultKeyVaultResp.getData()).thenReturn("{\"def-foo\":\"def-bar\"}"); + when(myDefaultKeyResp.getBody()).thenReturn(myDefaultKeyVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("mydefaultkey"))).thenReturn(myDefaultKeyResp); + + VaultEnvironmentRepository repo = new VaultEnvironmentRepository(mockProvide(configRequest), + new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); repo.setDefaultKey("mydefaultkey"); Environment e = repo.findOne("myapp", null, null); assertEquals("Name should be the same as the application argument", "myapp", e.getName()); - assertEquals("Properties for specified application and default application with key 'mydefaultkey' should be returned", 2, e.getPropertySources().size()); + assertEquals("Properties for specified application and default application with key 'mydefaultkey' should be returned", + 2, e.getPropertySources().size()); Map firstResult = new HashMap(); firstResult.put("foo", "bar"); - assertEquals("Properties for specified application should be returned in priority position", firstResult, e.getPropertySources().get(0).getSource()); + assertEquals("Properties for specified application should be returned in priority position", + firstResult, e.getPropertySources().get(0).getSource()); Map secondResult = new HashMap(); secondResult.put("def-foo", "def-bar"); - assertEquals("Properties for default application with key 'mydefaultkey' should be returned in second position", secondResult, e.getPropertySources().get(1).getSource()); + assertEquals("Properties for default application with key 'mydefaultkey' should be returned in second position", + secondResult, e.getPropertySources().get(1).getSource()); } @Test - public void testFindOneDefaultKeySetAndEqualToApplication() throws IOException { + @SuppressWarnings("unchecked") + public void testFindOneDefaultKeySetAndEqualToApplication() { MockHttpServletRequest configRequest = new MockHttpServletRequest(); configRequest.addHeader("X-CONFIG-TOKEN", "mytoken"); - RestTemplate rest = Mockito.mock(RestTemplate.class); + RestTemplate rest = mock(RestTemplate.class); - ResponseEntity myAppResp = Mockito.mock(ResponseEntity.class); - Mockito.when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); - VaultEnvironmentRepository.VaultResponse myAppVaultResp = Mockito.mock(VaultEnvironmentRepository.VaultResponse.class); - Mockito.when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); - Mockito.when(myAppResp.getBody()).thenReturn(myAppVaultResp); - Mockito.when(rest.exchange(Mockito.eq("http://127.0.0.1:8200/v1/{backend}/{key}"), - Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(VaultEnvironmentRepository.VaultResponse.class), - Mockito.eq("secret"), Mockito.eq("myapp"))).thenReturn(myAppResp); + ResponseEntity myAppResp = mock(ResponseEntity.class); + when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse myAppVaultResp = mock(VaultResponse.class); + when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); + when(myAppResp.getBody()).thenReturn(myAppVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("myapp"))).thenReturn(myAppResp); - ResponseEntity appResp = Mockito.mock(ResponseEntity.class); - Mockito.when(appResp.getStatusCode()).thenReturn(HttpStatus.OK); - VaultEnvironmentRepository.VaultResponse appVaultResp = Mockito.mock(VaultEnvironmentRepository.VaultResponse.class); - Mockito.when(appVaultResp.getData()).thenReturn("{\"def-foo\":\"def-bar\"}"); - Mockito.when(appResp.getBody()).thenReturn(appVaultResp); - Mockito.when(rest.exchange(Mockito.eq("http://127.0.0.1:8200/v1/{backend}/{key}"), - Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(VaultEnvironmentRepository.VaultResponse.class), - Mockito.eq("secret"), Mockito.eq("application"))).thenReturn(appResp); + ResponseEntity appResp = mock(ResponseEntity.class); + when(appResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse appVaultResp = mock(VaultResponse.class); + when(appVaultResp.getData()).thenReturn("{\"def-foo\":\"def-bar\"}"); + when(appResp.getBody()).thenReturn(appVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("application"))).thenReturn(appResp); - VaultEnvironmentRepository repo = new VaultEnvironmentRepository(configRequest, new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); + VaultEnvironmentRepository repo = new VaultEnvironmentRepository(mockProvide(configRequest), + new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); repo.setDefaultKey("myapp"); Environment e = repo.findOne("myapp", null, null); - assertEquals("Name should be the same as the application argument", "myapp", e.getName()); - assertEquals("Only properties for specified application should be returned", 1, e.getPropertySources().size()); + assertEquals("Name should be the same as the application argument", + "myapp", e.getName()); + assertEquals("Only properties for specified application should be returned", + 1, e.getPropertySources().size()); Map result = new HashMap(); result.put("foo", "bar"); - assertEquals("Properties should be returned for specified application", result, e.getPropertySources().get(0).getSource()); + assertEquals("Properties should be returned for specified application", + result, e.getPropertySources().get(0).getSource()); } @Test(expected = IllegalArgumentException.class) - public void missingConfigToken() throws IOException { + @SuppressWarnings("unchecked") + public void missingConfigToken() { MockHttpServletRequest configRequest = new MockHttpServletRequest(); - RestTemplate rest = Mockito.mock(RestTemplate.class); - ResponseEntity myAppResp = Mockito.mock(ResponseEntity.class); - Mockito.when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); - VaultEnvironmentRepository.VaultResponse myAppVaultResp = Mockito.mock(VaultEnvironmentRepository.VaultResponse.class); - Mockito.when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); - Mockito.when(myAppResp.getBody()).thenReturn(myAppVaultResp); - Mockito.when(rest.exchange(Mockito.eq("http://127.0.0.1:8200/v1/{backend}/{key}"), - Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(VaultEnvironmentRepository.VaultResponse.class), - Mockito.eq("secret"), Mockito.eq("myapp"))).thenReturn(myAppResp); - VaultEnvironmentRepository repo = new VaultEnvironmentRepository(configRequest, new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); + RestTemplate rest = mock(RestTemplate.class); + ResponseEntity myAppResp = mock(ResponseEntity.class); + when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse myAppVaultResp = mock(VaultResponse.class); + when(myAppVaultResp.getData()).thenReturn("{\"foo\":\"bar\"}"); + when(myAppResp.getBody()).thenReturn(myAppVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("myapp"))).thenReturn(myAppResp); + VaultEnvironmentRepository repo = new VaultEnvironmentRepository(mockProvide(configRequest), + new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); + repo.findOne("myapp", null, null); + } + + @Test(expected = IllegalStateException.class) + @SuppressWarnings("unchecked") + public void missingHttpRequest() { + ObjectProvider objectProvider = mock(ObjectProvider.class); + when(objectProvider.getIfAvailable()).thenReturn(null); + + RestTemplate rest = mock(RestTemplate.class); + VaultEnvironmentRepository repo = new VaultEnvironmentRepository(objectProvider, + new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); repo.findOne("myapp", null, null); } } diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java index a8eeeb43..0a00e1d5 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 the original author or authors. + * Copyright 2015 - 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. @@ -22,6 +22,10 @@ import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.HostKeyRepository; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; import org.eclipse.jgit.transport.OpenSshConfig.Host; import org.junit.Assert; import org.junit.Test; @@ -29,19 +33,11 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; -import com.jcraft.jsch.HostKey; -import com.jcraft.jsch.HostKeyRepository; -import com.jcraft.jsch.JSch; -import com.jcraft.jsch.Session; - -import static org.mockito.Matchers.isNull; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -70,10 +66,9 @@ public class PropertyBasedSshSessionFactoryTest { @Test public void strictHostKeyCheckingIsOptional() { - SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder() - .uri("ssh://gitlab.example.local:3322/somerepo.git") - .privateKey(PRIVATE_KEY) - .build(); + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git"); + sshKey.setPrivateKey(PRIVATE_KEY); setupSessionFactory(sshKey); factory.configure(hc, session); @@ -84,11 +79,10 @@ public class PropertyBasedSshSessionFactoryTest { @Test public void strictHostKeyCheckingIsUsed() { - SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder() - .uri("ssh://gitlab.example.local:3322/somerepo.git") - .hostKey(HOST_KEY) - .privateKey(PRIVATE_KEY) - .build(); + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git"); + sshKey.setHostKey(HOST_KEY); + sshKey.setPrivateKey(PRIVATE_KEY); setupSessionFactory(sshKey); factory.configure(hc, session); @@ -99,12 +93,11 @@ public class PropertyBasedSshSessionFactoryTest { @Test public void hostKeyAlgorithmIsSpecified() { - SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder() - .uri("ssh://gitlab.example.local:3322/somerepo.git") - .hostKeyAlgorithm(HOST_KEY_ALGORITHM) - .hostKey(HOST_KEY) - .privateKey(PRIVATE_KEY) - .build(); + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git"); + sshKey.setHostKeyAlgorithm(HOST_KEY_ALGORITHM); + sshKey.setHostKey(HOST_KEY); + sshKey.setPrivateKey(PRIVATE_KEY); setupSessionFactory(sshKey); factory.configure(hc, session); @@ -115,10 +108,9 @@ public class PropertyBasedSshSessionFactoryTest { @Test public void privateKeyIsUsed() throws Exception { - SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder() - .uri("git@gitlab.example.local:someorg/somerepo.git") - .privateKey(PRIVATE_KEY) - .build(); + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git"); + sshKey.setPrivateKey(PRIVATE_KEY); setupSessionFactory(sshKey); factory.createSession(hc, null, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 22, null); @@ -127,11 +119,10 @@ public class PropertyBasedSshSessionFactoryTest { @Test public void hostKeyIsUsed() throws Exception { - SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder() - .uri("git@gitlab.example.local:someorg/somerepo.git") - .hostKey(HOST_KEY) - .privateKey(PRIVATE_KEY) - .build(); + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git"); + sshKey.setHostKey(HOST_KEY); + sshKey.setPrivateKey(PRIVATE_KEY); setupSessionFactory(sshKey); factory.createSession(hc, null, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 22, null); @@ -144,11 +135,10 @@ public class PropertyBasedSshSessionFactoryTest { @Test public void preferredAuthenticationsIsSpecified() { - SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder() - .uri("ssh://gitlab.example.local:3322/somerepo.git") - .privateKey(PRIVATE_KEY) - .preferredAuthentications("password,keyboard-interactive") - .build(); + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git"); + sshKey.setPrivateKey(PRIVATE_KEY); + sshKey.setPreferredAuthentications("password,keyboard-interactive"); setupSessionFactory(sshKey); factory.configure(hc, session); @@ -159,11 +149,10 @@ public class PropertyBasedSshSessionFactoryTest { @Test public void customKnownHostsFileIsUsed() throws Exception { - SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder() - .uri("git@gitlab.example.local:someorg/somerepo.git") - .privateKey(PRIVATE_KEY) - .knownHostsFile("/ssh/known_hosts") - .build(); + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git"); + sshKey.setPrivateKey(PRIVATE_KEY); + sshKey.setKnownHostsFile("/ssh/known_hosts"); setupSessionFactory(sshKey); factory.createSession(hc, null, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 22, null); @@ -173,8 +162,8 @@ public class PropertyBasedSshSessionFactoryTest { Assert.assertEquals("/ssh/known_hosts", captor.getValue()); } - private void setupSessionFactory(SshUri sshKey) { - Map sshKeysByHostname = new HashMap<>(); + private void setupSessionFactory(JGitEnvironmentProperties sshKey) { + Map sshKeysByHostname = new HashMap<>(); sshKeysByHostname.put(SshUriPropertyProcessor.getHostname(sshKey.getUri()), sshKey); factory = new PropertyBasedSshSessionFactory(sshKeysByHostname, jSch) ; when(hc.getHostName()).thenReturn(SshUriPropertyProcessor.getHostname(sshKey.getUri())); diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java index 942f96ec..921067aa 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java @@ -16,14 +16,16 @@ package org.springframework.cloud.config.server.ssh; -import org.junit.BeforeClass; -import org.junit.Test; - +import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; -import java.util.Set; + +import org.junit.BeforeClass; +import org.junit.Test; + +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; @@ -76,119 +78,102 @@ public class SshPropertyValidatorTest { @Test public void supportedParametersSuccesful() throws Exception { - SshUriProperties validSettings = SshUri.builder() - .uri(SSH_URI) - .ignoreLocalSshSettings(true) - .privateKey(VALID_PRIVATE_KEY) - .hostKey(VALID_HOST_KEY) - .hostKeyAlgorithm("ssh-rsa") - .build(); + MultipleJGitEnvironmentProperties validSettings = new MultipleJGitEnvironmentProperties(); + validSettings.setUri(SSH_URI); + validSettings.setIgnoreLocalSshSettings(true); + validSettings.setPrivateKey(VALID_PRIVATE_KEY); + validSettings.setHostKey(VALID_HOST_KEY); + validSettings.setHostKeyAlgorithm("ssh-rsa"); - Set> constraintViolations = validator.validate(validSettings); + Set> constraintViolations = validator.validate(validSettings); assertThat(constraintViolations, hasSize(0)); - } @Test public void invalidPrivateKeyFails() throws Exception { + MultipleJGitEnvironmentProperties invalidKey = new MultipleJGitEnvironmentProperties(); + invalidKey.setUri(SSH_URI); + invalidKey.setIgnoreLocalSshSettings(true); + invalidKey.setPrivateKey("invalid_key"); - SshUriProperties invalidKey = SshUri.builder() - .uri(SSH_URI) - .ignoreLocalSshSettings(true) - .privateKey("invalid_key") - .build(); - - Set> constraintViolations = validator.validate(invalidKey); + Set> constraintViolations = validator.validate(invalidKey); assertThat(constraintViolations, hasSize(1)); - } @Test public void missingPrivateKeyFails() throws Exception { + MultipleJGitEnvironmentProperties missingKey = new MultipleJGitEnvironmentProperties(); + missingKey.setUri(SSH_URI); + missingKey.setIgnoreLocalSshSettings(true); - SshUriProperties missingKey = SshUri.builder() - .uri(SSH_URI) - .ignoreLocalSshSettings(true) - .build(); - - Set> constraintViolations = validator.validate(missingKey); + Set> constraintViolations = validator.validate(missingKey); assertThat(constraintViolations, hasSize(1)); } @Test public void hostKeyWithMissingAlgoFails() throws Exception { + MultipleJGitEnvironmentProperties missingAlgo = new MultipleJGitEnvironmentProperties(); + missingAlgo.setUri(SSH_URI); + missingAlgo.setIgnoreLocalSshSettings(true); + missingAlgo.setPrivateKey(VALID_PRIVATE_KEY); + missingAlgo.setHostKey("some_host"); - SshUriProperties missingAlgo = SshUri.builder() - .uri(SSH_URI) - .ignoreLocalSshSettings(true) - .privateKey(VALID_PRIVATE_KEY) - .hostKey("some_host") - .build(); - - Set> constraintViolations = validator.validate(missingAlgo); + Set> constraintViolations = validator.validate(missingAlgo); assertThat(constraintViolations, hasSize(1)); } @Test public void algoWithMissingHostKeyFails() throws Exception { + MultipleJGitEnvironmentProperties missingHostKey = new MultipleJGitEnvironmentProperties(); + missingHostKey.setUri(SSH_URI); + missingHostKey.setIgnoreLocalSshSettings(true); + missingHostKey.setPrivateKey(VALID_PRIVATE_KEY); + missingHostKey.setHostKeyAlgorithm("ssh-rsa"); - SshUriProperties missingHostKey = SshUri.builder() - .uri(SSH_URI) - .ignoreLocalSshSettings(true) - .privateKey(VALID_PRIVATE_KEY) - .hostKeyAlgorithm("ssh-rsa") - .build(); - - Set> constraintViolations = validator.validate(missingHostKey); + Set> constraintViolations = validator.validate(missingHostKey); assertThat(constraintViolations, hasSize(1)); } @Test public void unsupportedAlgoFails() throws Exception { + MultipleJGitEnvironmentProperties unsupportedAlgo = new MultipleJGitEnvironmentProperties(); + unsupportedAlgo.setUri(SSH_URI); + unsupportedAlgo.setIgnoreLocalSshSettings(true); + unsupportedAlgo.setPrivateKey(VALID_PRIVATE_KEY); + unsupportedAlgo.setHostKey("some_host_key"); + unsupportedAlgo.setHostKeyAlgorithm("unsupported"); - SshUriProperties unsupportedAlgo = SshUri.builder() - .uri(SSH_URI) - .ignoreLocalSshSettings(true) - .privateKey(VALID_PRIVATE_KEY) - .hostKey("some_host_key") - .hostKeyAlgorithm("unsupported") - .build(); - - Set> constraintViolations = validator.validate(unsupportedAlgo); + Set> constraintViolations = validator.validate(unsupportedAlgo); assertThat(constraintViolations, hasSize(1)); } @Test public void validatorNotRunIfIgnoreLocalSettingsFalse() throws Exception { + MultipleJGitEnvironmentProperties useLocal = new MultipleJGitEnvironmentProperties(); + useLocal.setUri(SSH_URI); + useLocal.setIgnoreLocalSshSettings(false); + useLocal.setPrivateKey("invalid_key"); - SshUriProperties useLocal = (SshUri.builder() - .uri(SSH_URI) - .ignoreLocalSshSettings(false) - .privateKey("invalid_key") - .build()); - - Set> constraintViolations = validator.validate(useLocal); + Set> constraintViolations = validator.validate(useLocal); assertThat(constraintViolations, hasSize(0)); } @Test public void validatorNotRunIfHttpsUri() throws Exception { + MultipleJGitEnvironmentProperties httpsUri = new MultipleJGitEnvironmentProperties(); + httpsUri.setUri("https://somerepo.com/team/project.git"); + httpsUri.setIgnoreLocalSshSettings(true); + httpsUri.setPrivateKey("invalid_key"); - SshUriProperties httpsUri = (SshUri.builder() - .uri("https://somerepo.com/team/project.git") - .ignoreLocalSshSettings(true) - .privateKey("invalid_key") - .build()); - - Set> constraintViolations = validator.validate(httpsUri); + Set> constraintViolations = validator.validate(httpsUri); assertThat(constraintViolations, hasSize(0)); } @Test public void preferredAuthenticationsIsValidated() throws Exception { - SshUriProperties sshUriProperties = new SshUriProperties(); + MultipleJGitEnvironmentProperties sshUriProperties = new MultipleJGitEnvironmentProperties(); assertThat(validator.validate(sshUriProperties), hasSize(0)); sshUriProperties.setPreferredAuthentications("keyboard-interactive, public-key ,kerberos"); @@ -200,7 +185,7 @@ public class SshPropertyValidatorTest { @Test public void knowHostsFileIsValidated() throws Exception { - SshUriProperties sshUriProperties = new SshUriProperties(); + MultipleJGitEnvironmentProperties sshUriProperties = new MultipleJGitEnvironmentProperties(); assertThat(validator.validate(sshUriProperties), hasSize(0)); sshUriProperties.setKnownHostsFile("non-existing.file"); diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessorTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessorTest.java index 8f7fd49a..c1cb8e22 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessorTest.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshUriPropertyProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015 - 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. @@ -18,13 +18,19 @@ package org.springframework.cloud.config.server.ssh; import java.util.Map; + import org.eclipse.jgit.transport.SshSessionFactory; import org.junit.After; import org.junit.Test; -import org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriNestedRepoProperties; +import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties; +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; /** @@ -54,43 +60,45 @@ public class SshUriPropertyProcessorTest { @Test public void testSingleSshUriProperties() { SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(mainRepoPropertiesFixture()); - Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); + Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); assertThat(sshKeysByHostname.values(), hasSize(1)); - SshUri sshKey = sshKeysByHostname.get(HOST1); + JGitEnvironmentProperties sshKey = sshKeysByHostname.get(HOST1); assertMainRepo(sshKey); } @Test public void testMultipleSshUriPropertiess() { - SshUriProperties sshUriProperties = mainRepoPropertiesFixture(); - addRepoProperties(sshUriProperties, SshUri.builder() - .uri(URI2) - .privateKey(PRIVATE_KEY2) - .buildAsNestedRepo(), "repo2"); - addRepoProperties(sshUriProperties, SshUri.builder() - .uri(URI3) - .privateKey(PRIVATE_KEY3) - .buildAsNestedRepo(), "repo3"); + MultipleJGitEnvironmentProperties sshUriProperties = mainRepoPropertiesFixture(); + MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties nestedSshUriProperties1 = + new MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties(); + nestedSshUriProperties1.setUri(URI2); + nestedSshUriProperties1.setPrivateKey(PRIVATE_KEY2); + MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties nestedSshUriProperties2 = + new MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties(); + nestedSshUriProperties2.setUri(URI3); + nestedSshUriProperties2.setPrivateKey(PRIVATE_KEY3); + addRepoProperties(sshUriProperties, nestedSshUriProperties1, "repo2"); + addRepoProperties(sshUriProperties, nestedSshUriProperties2, "repo3"); SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties); - Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); + Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); assertThat(sshKeysByHostname.values(), hasSize(3)); - SshUri sshKey1 = sshKeysByHostname.get(HOST1); + JGitEnvironmentProperties sshKey1 = sshKeysByHostname.get(HOST1); assertMainRepo(sshKey1); - SshUri sshKey2 = sshKeysByHostname.get(HOST2); + JGitEnvironmentProperties sshKey2 = sshKeysByHostname.get(HOST2); assertThat(SshUriPropertyProcessor.getHostname(sshKey2.getUri()), is(equalTo(HOST2))); assertThat(sshKey2.getHostKeyAlgorithm(), is(nullValue())); assertThat(sshKey2.getHostKey(), is(nullValue())); assertThat(sshKey2.getPrivateKey(), is(equalTo(PRIVATE_KEY2))); - SshUri sshKey3 = sshKeysByHostname.get(HOST3); + JGitEnvironmentProperties sshKey3 = sshKeysByHostname.get(HOST3); assertThat(SshUriPropertyProcessor.getHostname(sshKey3.getUri()), is(equalTo(HOST3))); assertThat(sshKey3.getHostKeyAlgorithm(), is(nullValue())); @@ -100,58 +108,62 @@ public class SshUriPropertyProcessorTest { @Test public void testSameHostnameDifferentKeysFirstOneWins() { - SshUriProperties sshUriProperties = mainRepoPropertiesFixture(); - addRepoProperties(sshUriProperties, SshUri.builder().uri(URI1) - .privateKey(PRIVATE_KEY1) - .hostKey(HOST_KEY1) - .hostKeyAlgorithm(ALGO1) - .buildAsNestedRepo(), "repo2"); + MultipleJGitEnvironmentProperties sshUriProperties = mainRepoPropertiesFixture(); + MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties nestedSshUriProperties = new MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties(); + nestedSshUriProperties.setUri(URI1); + nestedSshUriProperties.setPrivateKey(PRIVATE_KEY1); + nestedSshUriProperties.setHostKey(HOST_KEY1); + nestedSshUriProperties.setHostKeyAlgorithm(ALGO1); + addRepoProperties(sshUriProperties, nestedSshUriProperties, "repo2"); SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties); - Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); + Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); assertThat(sshKeysByHostname.values(), hasSize(1)); - SshUri sshKey = sshKeysByHostname.get(HOST1); + JGitEnvironmentProperties sshKey = sshKeysByHostname.get(HOST1); assertMainRepo(sshKey); } @Test public void testNoSshUriProperties() { - SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(new SshUriProperties()); - Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); + SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(new MultipleJGitEnvironmentProperties()); + Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); assertThat(sshKeysByHostname.values(), hasSize(0)); } @Test public void testInvalidUriDoesNotAddEntry() { - SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(SshUri.builder().uri("invalid_uri").build()); - Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); + MultipleJGitEnvironmentProperties sshUriProperties = new MultipleJGitEnvironmentProperties(); + sshUriProperties.setUri("invalid_uri"); + SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties); + Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); assertThat(sshKeysByHostname.values(), hasSize(0)); } @Test public void testHttpsUriDoesNotAddEntry() { - SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(SshUri.builder().uri("https://user@github.com/proj/repo.git").build()); - Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); + MultipleJGitEnvironmentProperties sshUriProperties = new MultipleJGitEnvironmentProperties(); + sshUriProperties.setUri("https://user@github.com/proj/repo.git"); + SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties); + Map sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname(); assertThat(sshKeysByHostname.values(), hasSize(0)); } - private SshUriProperties mainRepoPropertiesFixture() { - - return SshUri.builder() - .uri(URI1) - .hostKeyAlgorithm(ALGO1) - .hostKey(HOST_KEY1) - .privateKey(PRIVATE_KEY1) - .build(); + private MultipleJGitEnvironmentProperties mainRepoPropertiesFixture() { + MultipleJGitEnvironmentProperties result = new MultipleJGitEnvironmentProperties(); + result.setUri(URI1); + result.setHostKeyAlgorithm(ALGO1); + result.setHostKey(HOST_KEY1); + result.setPrivateKey(PRIVATE_KEY1); + return result; } - private void addRepoProperties(SshUriProperties mainRepoProperties, SshUriNestedRepoProperties repoProperties, String repoName) { - mainRepoProperties.addRepo(repoName, repoProperties); + private void addRepoProperties(MultipleJGitEnvironmentProperties mainRepoProperties, MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties repoProperties, String repoName) { + mainRepoProperties.getRepos().put(repoName, repoProperties); } - private void assertMainRepo(SshUri sshKey) { + private void assertMainRepo(JGitEnvironmentProperties sshKey) { assertThat(sshKey, is(notNullValue())); assertThat(SshUriPropertyProcessor.getHostname(sshKey.getUri()), is(equalTo(HOST1))); assertThat(sshKey.getHostKeyAlgorithm(), is(equalTo(ALGO1))); diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/support/GitSkipSslValidationCredentialsProviderTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/support/GitSkipSslValidationCredentialsProviderTest.java new file mode 100644 index 00000000..148a51e9 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/support/GitSkipSslValidationCredentialsProviderTest.java @@ -0,0 +1,280 @@ +/* + * 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.net.URISyntaxException; +import java.text.MessageFormat; + +import org.eclipse.jgit.errors.UnsupportedCredentialItem; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.URIish; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Gareth Clay + */ +@RunWith(MockitoJUnitRunner.class) +public class GitSkipSslValidationCredentialsProviderTest { + + @Mock + private CredentialsProvider mockDelegateCredentialsProvider; + private GitSkipSslValidationCredentialsProvider skipSslValidationCredentialsProvider; + + @Before + public void setup() { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + null); + } + + @Test + public void testCanHandle() { + assertTrue("GitSkipSslValidationCredentialsProvider only handles HTTPS uris", + GitSkipSslValidationCredentialsProvider + .canHandle("https://github.com/org/repo")); + assertFalse("GitSkipSslValidationCredentialsProvider only handles HTTPS uris", + GitSkipSslValidationCredentialsProvider + .canHandle("git@github.com:org/repo")); + } + + @Test + public void testIsInteractive() { + assertFalse( + "GitSkipSslValidationCredentialsProvider with no delegate requires no user interaction", + skipSslValidationCredentialsProvider.isInteractive()); + } + + @Test + public void testIsInteractiveWithDelegate() { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + + when(mockDelegateCredentialsProvider.isInteractive()).thenReturn(true); + + assertTrue( + "With a delegate provider, isInteractive value depends on the delegate", + skipSslValidationCredentialsProvider.isInteractive()); + } + + @Test + public void testSupportsSslFailureInformationalMessage() { + CredentialItem informationalMessage = new CredentialItem.InformationalMessage( + "text " + JGitText.get().sslFailureTrustExplanation + " more text"); + assertTrue( + "GitSkipSslValidationCredentialsProvider should always support SSL failure InformationalMessage", + skipSslValidationCredentialsProvider.supports(informationalMessage)); + + informationalMessage = new CredentialItem.InformationalMessage("unrelated"); + assertFalse( + "GitSkipSslValidationCredentialsProvider should not support unrelated InformationalMessage items", + skipSslValidationCredentialsProvider.supports(informationalMessage)); + } + + @Test + public void testSupportsSslFailureInformationalMessageWithDelegate() { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + + testSupportsSslFailureInformationalMessage(); + } + + @Test + public void testSupportsSslValidationYesNoTypes() { + CredentialItem yesNoType = new CredentialItem.YesNoType( + JGitText.get().sslTrustNow); + assertTrue( + "GitSkipSslValidationCredentialsProvider should always support the trust now YesNoType item", + skipSslValidationCredentialsProvider.supports(yesNoType)); + + yesNoType = new CredentialItem.YesNoType( + MessageFormat.format(JGitText.get().sslTrustForRepo, "/a/path.git")); + assertTrue( + "GitSkipSslValidationCredentialsProvider should always support the trust repo YesNoType item", + skipSslValidationCredentialsProvider.supports(yesNoType)); + + yesNoType = new CredentialItem.YesNoType(JGitText.get().sslTrustAlways); + assertTrue( + "GitSkipSslValidationCredentialsProvider should always support the trust always YesNoType item", + skipSslValidationCredentialsProvider.supports(yesNoType)); + + yesNoType = new CredentialItem.YesNoType("unrelated"); + assertFalse( + "GitSkipSslValidationCredentialsProvider should not support unrelated YesNoType items", + skipSslValidationCredentialsProvider.supports(yesNoType)); + } + + @Test + public void testSupportsYesNoTypeWithDelegate() { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + + testSupportsSslValidationYesNoTypes(); + } + + @Test + public void testSupportsUnrelatedCredentialItemTypes() { + CredentialItem usernameCredentialItem = new CredentialItem.Username(); + + boolean supportsItems = skipSslValidationCredentialsProvider + .supports(usernameCredentialItem); + + assertFalse( + "Credential item types not related to SSL validation skipping should not be supported", + supportsItems); + } + + @Test + public void testSupportsUnrelatedCredentialItemTypesWithDelegate() { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + CredentialItem usernameCredentialItem = new CredentialItem.Username(); + + when(mockDelegateCredentialsProvider.supports(usernameCredentialItem)) + .thenReturn(true); + + boolean supportsItems = skipSslValidationCredentialsProvider + .supports(usernameCredentialItem); + + assertTrue( + "GitSkipSslValidationCredentialsProvider must support the types supported by its delegate CredentialsProvider", + supportsItems); + } + + @Test(expected = UnsupportedCredentialItem.class) + public void testGetUnrelatedCredentialItemTypes() throws URISyntaxException { + URIish uri = new URIish("https://example.com/repo.git"); + CredentialItem usernameCredentialItem = new CredentialItem.Username(); + CredentialItem passwordCredentialItem = new CredentialItem.Password(); + + skipSslValidationCredentialsProvider.get(uri, usernameCredentialItem, + passwordCredentialItem); + } + + @Test + public void testGetUnrelatedCredentialItemTypesWithDelegate() + throws URISyntaxException { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + URIish uri = new URIish("https://example.com/repo.git"); + CredentialItem usernameCredentialItem = new CredentialItem.Username(); + CredentialItem passwordCredentialItem = new CredentialItem.Password(); + + when(mockDelegateCredentialsProvider.get(uri, usernameCredentialItem, + passwordCredentialItem)).thenReturn(true); + + boolean getSuccessful = skipSslValidationCredentialsProvider.get(uri, + usernameCredentialItem, passwordCredentialItem); + + assertTrue( + "GitSkipSslValidationCredentialsProvider must successfully get the types supported by its delegate CredentialsProvider", + getSuccessful); + } + + @Test + public void testGetSslTrustItems() throws URISyntaxException { + URIish uri = new URIish("https://example.com/repo.git"); + CredentialItem message = new CredentialItem.InformationalMessage( + JGitText.get().sslFailureTrustExplanation); + CredentialItem.YesNoType trustNow = new CredentialItem.YesNoType( + JGitText.get().sslTrustNow); + CredentialItem.YesNoType trustAlways = new CredentialItem.YesNoType( + JGitText.get().sslTrustAlways); + + boolean getSuccessful = skipSslValidationCredentialsProvider.get(uri, message, + trustNow, trustAlways); + + assertTrue( + "SkipSSlValidationCredentialsProvider must successfully get the types required for SSL validation skipping", + getSuccessful); + assertTrue( + "SkipSSlValidationCredentialsProvider should trust the current repo operation", + trustNow.getValue()); + assertFalse("We should not globally skip all SSL validation", + trustAlways.getValue()); + } + + @Test + public void testGetSslTrustItemsWithDelegate() throws URISyntaxException { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + + testGetSslTrustItems(); + } + + @Test + public void testGetSslTrustItemsWithLocalRepo() throws URISyntaxException { + URIish uri = new URIish("https://example.com/repo.git"); + CredentialItem message = new CredentialItem.InformationalMessage( + JGitText.get().sslFailureTrustExplanation); + CredentialItem.YesNoType trustNow = new CredentialItem.YesNoType( + JGitText.get().sslTrustNow); + CredentialItem.YesNoType trustForRepo = new CredentialItem.YesNoType( + JGitText.get().sslTrustForRepo); + CredentialItem.YesNoType trustAlways = new CredentialItem.YesNoType( + JGitText.get().sslTrustAlways); + + boolean getSuccessful = skipSslValidationCredentialsProvider.get(uri, message, + trustNow, trustForRepo, trustAlways); + + assertTrue( + "SkipSSlValidationCredentialsProvider must successfully get the types required for SSL validation skipping", + getSuccessful); + assertTrue( + "SkipSSlValidationCredentialsProvider should trust the current repo operation", + trustNow.getValue()); + assertTrue("Future operations on this repository should also be trusted", + trustForRepo.getValue()); + assertFalse("We should not globally skip all SSL validation", + trustAlways.getValue()); + } + + @Test + public void testGetSslTrustItemsWithLocalRepoAndDelegate() throws URISyntaxException { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + + testGetSslTrustItemsWithLocalRepo(); + } + + @Test + public void testReset() throws URISyntaxException { + URIish uri = new URIish("https://example.com/repo.git"); + + skipSslValidationCredentialsProvider.reset(uri); + } + + @Test + public void testResetWithDelegate() throws URISyntaxException { + this.skipSslValidationCredentialsProvider = new GitSkipSslValidationCredentialsProvider( + mockDelegateCredentialsProvider); + URIish uri = new URIish("https://example.com/repo.git"); + + skipSslValidationCredentialsProvider.reset(uri); + + verify(mockDelegateCredentialsProvider).reset(uri); + } +} diff --git a/spring-cloud-config-server/src/test/resources/ssh/ssh-nested-settings-list.yml b/spring-cloud-config-server/src/test/resources/ssh/ssh-nested-settings-list.yml new file mode 100644 index 00000000..c265f489 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/ssh/ssh-nested-settings-list.yml @@ -0,0 +1,42 @@ +spring: + cloud: + config: + server: + composite: + - type: git + uri: git@gitserver.com:team/repo1.git + ignoreLocalSshSettings: true + privateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI\nyyxp38YcuZG9cUDBAxby+K2TKmwHaC1R61QTwbPuCRdIPrDwRz+FLoegm3iDLCmn\nuP6rjZDneYsqfU1KSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km\nayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq\nTOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir\n+diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk\njfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt\nlDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI\nxdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V\n4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG\n7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu\n3reKgm0CgYEA03Sx8JoF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8\nq4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq\nmRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa\nlKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M\nZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv\nZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit\nBT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO\nzf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw\nuC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF\n54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS\nE1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx\n4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr\n/syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq\n2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==\n-----END RSA PRIVATE KEY-----" + repos: + repo1: + uri: git@gitserver.com:team/repo2.git + hostKey: someHostKey + hostKeyAlgorithm: ssh-rsa + privateKey: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX + IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF + ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud + 1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i + oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W + DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd + fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b + BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG + EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj + 5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8 + +AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe + pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG + ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ + xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW + dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi + PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX + VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z + FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R + gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4 + VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV + cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee + KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N + CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs + q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J + 69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT + -----END RSA PRIVATE KEY----- diff --git a/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block-list.yml b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block-list.yml new file mode 100644 index 00000000..90bb2009 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block-list.yml @@ -0,0 +1,36 @@ +spring: + cloud: + config: + server: + composite: + - type: git + uri: git@gitserver.com:team/repo.git + ignoreLocalSshSettings: true + privateKey: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI + yyxp38YcuZG9cUDBAxby+K2TKmwHaC1Wf1QTwbPuCRdIPrDwRz+FLoegm3iDLCmn + uP6rjZDneYsqfU1sSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km + ayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq + TOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir + +diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk + jfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt + lDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI + xdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V + 4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG + 7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu + 3reKgm0CgYEA03Sx8JgF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8 + q4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq + mRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa + lKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M + ZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv + ZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit + BT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO + zf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw + uC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF + 54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS + E1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx + 4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr + /syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq + 2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ== + -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-newline-list.yml b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-newline-list.yml new file mode 100644 index 00000000..2fe26da9 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-newline-list.yml @@ -0,0 +1,11 @@ +spring: + cloud: + config: + server: + composite: + - type: git + uri: git@gitserver.com:team/repo.git + ignoreLocalSshSettings: true + privateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI\nyyxp38YcuZG9cUDBAxby+K2TKmwHaC1R61QTwbPuCRdIPrDwRz+FLoegm3iDLCmn\nuP6rjZDneYsqfU1KSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km\nayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq\nTOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir\n+diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk\njfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt\nlDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI\nxdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V\n4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG\n7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu\n3reKgm0CgYEA03Sx8JoF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8\nq4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq\nmRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa\nlKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M\nZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv\nZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit\nBT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO\nzf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw\nuC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF\n54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS\nE1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx\n4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr\n/syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq\n2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==\n-----END RSA PRIVATE KEY-----" + hostKey: somekey + hostKeyAlgorithm: ssh-rsa diff --git a/spring-cloud-config-server/src/test/resources/test1-config-repo/git/info/refs b/spring-cloud-config-server/src/test/resources/test1-config-repo/git/info/refs new file mode 100644 index 00000000..c2763f61 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/test1-config-repo/git/info/refs @@ -0,0 +1 @@ +4e1f25bbdca5765e279b436b5b02d05b1654515c refs/heads/master