diff --git a/docs/src/main/asciidoc/spring-cloud-config.adoc b/docs/src/main/asciidoc/spring-cloud-config.adoc
index fff80204..cd49f417 100644
--- a/docs/src/main/asciidoc/spring-cloud-config.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-config.adoc
@@ -334,6 +334,17 @@ AWS EC2 instances may use https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ia
NOTE: The `aws-java-sdk-core` jar is an optional dependency.
If the `aws-java-sdk-core` jar is not on your classpath, the AWS Code Commit credential provider is not created, regardless of the git server URI.
+===== Authentication with Google Cloud Source
+
+Spring Cloud Config Server also supports authenticating against https://cloud.google.com/source-repositories/[Google Cloud Source] repositories.
+
+If your Git URI uses the `http` or `https` protocol and the domain name is `source.developers.google.com`, the Google Cloud Source credentials provider will be used. A Google Cloud Source repository URI has the format `https://source.developers.google.com/p/${GCP_PROJECT}/r/${REPO}`. To obtain the URI for your repository, click on "Clone" in the Google Cloud Source UI, and select "Manually generated credentials". Do not generate any credentials, simply copy the displayed URI.
+
+The Google Cloud Source credentials provider will use Google Cloud Platform application default credentials. See https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login[Google Cloud SDK documentation] on how to create application default credentials for a system. This approach will work for user accounts in dev environments and for service accounts in production environments.
+
+NOTE: `com.google.auth:google-auth-library-oauth2-http` is an optional dependency.
+If the `google-auth-library-oauth2-http` jar is not on your classpath, the Google Cloud Source credential provider is not created, regardless of the git server URI.
+
===== Git SSH configuration using properties
By default, the JGit library used by Spring Cloud Config Server uses SSH configuration files such as `~/.ssh/known_hosts` and `/etc/ssh/ssh_config` when connecting to Git repositories by using an SSH URI.
diff --git a/pom.xml b/pom.xml
index f6310a15..e1547743 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,11 @@
aws-java-sdk-core
1.11.52
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ 0.15.0
+
diff --git a/spring-cloud-config-server/pom.xml b/spring-cloud-config-server/pom.xml
index ec51019a..3b6ad2ea 100644
--- a/spring-cloud-config-server/pom.xml
+++ b/spring-cloud-config-server/pom.xml
@@ -85,6 +85,11 @@
spring-boot-autoconfigure-processor
true
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ true
+
com.h2database
h2
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 8d05bd74..6b8a9fee 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
@@ -64,6 +64,8 @@ import org.springframework.cloud.config.server.environment.SvnKitEnvironmentRepo
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
import org.springframework.cloud.config.server.environment.VaultEnvironmentRepository;
import org.springframework.cloud.config.server.environment.VaultEnvironmentRepositoryFactory;
+import org.springframework.cloud.config.server.support.GoogleCloudSourceSupport;
+import org.springframework.cloud.config.server.support.TransportConfigCallbackFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -91,7 +93,8 @@ import org.springframework.jdbc.core.JdbcTemplate;
VaultRepositoryConfiguration.class, CredhubConfiguration.class,
CredhubRepositoryConfiguration.class, SvnRepositoryConfiguration.class,
NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class,
- RedisRepositoryConfiguration.class, DefaultRepositoryConfiguration.class })
+ RedisRepositoryConfiguration.class, GoogleCloudSourceConfiguration.class,
+ DefaultRepositoryConfiguration.class })
public class EnvironmentRepositoryConfiguration {
@Bean
@@ -137,9 +140,13 @@ public class EnvironmentRepositoryConfiguration {
public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory(
ConfigurableEnvironment environment, ConfigServerProperties server,
Optional jgitHttpConnectionFactory,
- Optional customTransportConfigCallback) {
+ Optional customTransportConfigCallback,
+ Optional googleCloudSourceSupport) {
+ final TransportConfigCallbackFactory transportConfigCallbackFactory = new TransportConfigCallbackFactory(
+ customTransportConfigCallback.orElse(null),
+ googleCloudSourceSupport.orElse(null));
return new MultipleJGitEnvironmentRepositoryFactory(environment, server,
- jgitHttpConnectionFactory, customTransportConfigCallback);
+ jgitHttpConnectionFactory, transportConfigCallbackFactory);
}
}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/GoogleCloudSourceConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/GoogleCloudSourceConfiguration.java
new file mode 100644
index 00000000..5c579e43
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/GoogleCloudSourceConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013-2019 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
+ *
+ * https://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.google.auth.oauth2.GoogleCredentials;
+import org.eclipse.jgit.api.TransportConfigCallback;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.cloud.config.server.support.GoogleCloudSourceSupport;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Adds Google Cloud Source OAuth2 support, if
+ * com.google.auth:google-auth-library-oauth2-http library is on classpath.
+ *
+ * @author Eduard Wirch
+ */
+@Configuration
+@ConditionalOnClass({ GoogleCredentials.class, TransportConfigCallback.class })
+public class GoogleCloudSourceConfiguration {
+
+ @Bean
+ public GoogleCloudSourceSupport createGoogleCloudSourceSupport() {
+ return new GoogleCloudSourceSupport();
+ }
+
+}
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 cfbd6e17..5996e3c8 100644
--- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryFactory.java
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepositoryFactory.java
@@ -18,12 +18,10 @@ package org.springframework.cloud.config.server.environment;
import java.util.Optional;
-import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.transport.HttpTransport;
import org.springframework.cloud.config.server.config.ConfigServerProperties;
-import org.springframework.cloud.config.server.ssh.FileBasedSshTransportConfigCallback;
-import org.springframework.cloud.config.server.ssh.PropertiesBasedSshTransportConfigCallback;
+import org.springframework.cloud.config.server.support.TransportConfigCallbackFactory;
import org.springframework.core.env.ConfigurableEnvironment;
/**
@@ -38,23 +36,23 @@ public class MultipleJGitEnvironmentRepositoryFactory implements
private Optional connectionFactory;
- private Optional customTransportConfigCallback;
+ private final TransportConfigCallbackFactory transportConfigCallbackFactory;
@Deprecated
public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment,
ConfigServerProperties server,
- Optional customTransportConfigCallback) {
- this(environment, server, Optional.empty(), customTransportConfigCallback);
+ TransportConfigCallbackFactory transportConfigCallbackFactory) {
+ this(environment, server, Optional.empty(), transportConfigCallbackFactory);
}
public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment,
ConfigServerProperties server,
Optional connectionFactory,
- Optional customTransportConfigCallback) {
+ TransportConfigCallbackFactory transportConfigCallbackFactory) {
this.environment = environment;
this.server = server;
this.connectionFactory = connectionFactory;
- this.customTransportConfigCallback = customTransportConfigCallback;
+ this.transportConfigCallbackFactory = transportConfigCallbackFactory;
}
@Override
@@ -67,21 +65,12 @@ public class MultipleJGitEnvironmentRepositoryFactory implements
MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(
this.environment, environmentProperties);
- repository.setTransportConfigCallback(this.customTransportConfigCallback
- .orElse(buildTransportConfigCallback(environmentProperties)));
+ repository.setTransportConfigCallback(
+ transportConfigCallbackFactory.build(environmentProperties));
if (this.server.getDefaultLabel() != null) {
repository.setDefaultLabel(this.server.getDefaultLabel());
}
return repository;
}
- private TransportConfigCallback buildTransportConfigCallback(
- MultipleJGitEnvironmentProperties gitEnvironmentProperties) {
- if (gitEnvironmentProperties.isIgnoreLocalSshSettings()) {
- return new PropertiesBasedSshTransportConfigCallback(
- gitEnvironmentProperties);
- }
- return new FileBasedSshTransportConfigCallback(gitEnvironmentProperties);
- }
-
}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GoogleCloudSourceSupport.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GoogleCloudSourceSupport.java
new file mode 100644
index 00000000..fe3f6809
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/GoogleCloudSourceSupport.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013-2019 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
+ *
+ * https://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.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+
+import com.google.auth.oauth2.GoogleCredentials;
+import org.eclipse.jgit.api.TransportConfigCallback;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.TransportHttp;
+import org.eclipse.jgit.transport.URIish;
+
+import static java.util.stream.Collectors.toMap;
+
+/**
+ * Provides credentials for Google Cloud Source repositories by adding a
+ * {@code Authenticate} http header.
+ *
+ * It does so by acting as a transport configurer. If a transport instance targets a
+ * Google Cloud Source repository, this implementation retrieves Google Cloud application
+ * default credentials and adds them as a http header.
+ *
+ * @author Eduard Wirch
+ * @see gcloud
+ * auth application-default login
+ */
+public final class GoogleCloudSourceSupport {
+
+ boolean canHandle(String uri) {
+ try {
+ return GCSTransportConfigCallback.canHandle(new URIish(uri));
+ }
+ catch (URISyntaxException e) {
+ return false;
+ }
+ }
+
+ // This detour via GCSTransportConfigCallback was necessary because:
+ // - we want the Google Cloud credentials provider to be a bean, so we can use
+ // @ConditionalOnClass to conditionally disable support, if required classes
+ // are not on the class path.
+ // - We cannot make a class implementing TransportConfigCallback a bean,
+ // because Spring would populate customTransportConfigCallback with this bean
+ // (see JGitFactoryConfig.gitEnvironmentRepositoryFactory()), and report a
+ // conflict whenever there is a real "custom" TransportConfigCallback
+ // implementation in the Spring context.
+ // This is why GoogleCloudSourceSupport is the optional bean, which can provide
+ // the TransportConfigCallback on request.
+ TransportConfigCallback createTransportConfigCallback() {
+ return new GCSTransportConfigCallback(
+ new ApplicationDefaultCredentialsProvider());
+ }
+
+ TransportConfigCallback createTransportConfigCallback(
+ CredentialsProvider credentialsProvider) {
+ return new GCSTransportConfigCallback(credentialsProvider);
+ }
+
+ private static final class GCSTransportConfigCallback
+ implements TransportConfigCallback {
+
+ private static final String GOOGLE_CLOUD_SOURCE_HOST = "source.developers.google.com";
+
+ private final CredentialsProvider credentialsProvider;
+
+ private GCSTransportConfigCallback(CredentialsProvider credentialsProvider) {
+ this.credentialsProvider = credentialsProvider;
+ }
+
+ @Override
+ public void configure(Transport transport) {
+ if (transport instanceof TransportHttp && canHandle(transport.getURI())) {
+ addHeaders((TransportHttp) transport,
+ credentialsProvider.getAuthorizationHeaders());
+ }
+ }
+
+ private static boolean canHandle(URIish uri) {
+ return isHttpScheme(uri) && isGoogleCloudSourceHost(uri);
+ }
+
+ private static boolean isHttpScheme(URIish uri) {
+ final String scheme = uri.getScheme();
+ return Objects.equals(scheme, "http") || Objects.equals(scheme, "https");
+ }
+
+ private static boolean isGoogleCloudSourceHost(URIish uri) {
+ return Objects.equals(uri.getHost(), GOOGLE_CLOUD_SOURCE_HOST);
+ }
+
+ private void addHeaders(TransportHttp transport, Map headers) {
+ transport.setAdditionalHeaders(headers);
+ }
+
+ }
+
+ interface CredentialsProvider {
+
+ Map getAuthorizationHeaders();
+
+ }
+
+ private static class ApplicationDefaultCredentialsProvider
+ implements CredentialsProvider {
+
+ @Override
+ public Map getAuthorizationHeaders() {
+ try {
+ return GoogleCredentials.getApplicationDefault().getRequestMetadata()
+ .entrySet().stream()
+ .collect(toMap(Entry::getKey, this::joinValues));
+ }
+ catch (IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ private String joinValues(Entry, List> entry) {
+ return String.join(", ", entry.getValue());
+ }
+
+ }
+
+}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/TransportConfigCallbackFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/TransportConfigCallbackFactory.java
new file mode 100644
index 00000000..3fe4f6c7
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/support/TransportConfigCallbackFactory.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2013-2019 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
+ *
+ * https://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 javax.annotation.Nullable;
+
+import org.eclipse.jgit.api.TransportConfigCallback;
+
+import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties;
+import org.springframework.cloud.config.server.ssh.FileBasedSshTransportConfigCallback;
+import org.springframework.cloud.config.server.ssh.PropertiesBasedSshTransportConfigCallback;
+
+public class TransportConfigCallbackFactory {
+
+ @Nullable
+ private final TransportConfigCallback customTransportConfigCallback;
+
+ @Nullable
+ private final GoogleCloudSourceSupport googleCloudSourceSupport;
+
+ public TransportConfigCallbackFactory(
+ TransportConfigCallback customTransportConfigCallback,
+ GoogleCloudSourceSupport googleCloudSourceSupport) {
+
+ this.customTransportConfigCallback = customTransportConfigCallback;
+ this.googleCloudSourceSupport = googleCloudSourceSupport;
+ }
+
+ public TransportConfigCallback build(
+ MultipleJGitEnvironmentProperties environmentProperties) {
+
+ // customTransportConfigCallback has the highest priority. If someone put
+ // a TransportConfigCallback bean in to the Spring context, we use it for
+ // all repositories.
+ if (customTransportConfigCallback != null) {
+ return customTransportConfigCallback;
+ }
+
+ // If the currently configured repository is a Google Cloud Source repository
+ // we use GoogleCloudSourceSupport.
+ if (googleCloudSourceSupport != null) {
+ final String uri = environmentProperties.getUri();
+ if (googleCloudSourceSupport.canHandle(uri)) {
+ return googleCloudSourceSupport.createTransportConfigCallback();
+ }
+ }
+
+ // Otherwise - legacy behaviour - use SshTransportConfigCallback for all
+ // repositories.
+ return buildSshTransportConfigCallback(environmentProperties);
+ }
+
+ private TransportConfigCallback buildSshTransportConfigCallback(
+ MultipleJGitEnvironmentProperties gitEnvironmentProperties) {
+
+ if (gitEnvironmentProperties.isIgnoreLocalSshSettings()) {
+ return new PropertiesBasedSshTransportConfigCallback(
+ gitEnvironmentProperties);
+ }
+ return new FileBasedSshTransportConfigCallback(gitEnvironmentProperties);
+ }
+
+}
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java
index b00a01c0..a033faf8 100644
--- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java
@@ -114,4 +114,23 @@ public class CompositeClasspathTests {
}
+ @RunWith(ModifiedClassPathRunner.class)
+ @ClassPathExclusions("google-auth-library-oauth2-http-*.jar")
+ public static class GoogleAuthTests {
+
+ @Test
+ public void contextLoads() {
+ new WebApplicationContextRunner()
+ .withUserConfiguration(ConfigServerApplication.class)
+ .withPropertyValues("spring.profiles.active:test,composite",
+ "spring.jmx.enabled=false", "spring.config.name:configserver",
+ "spring.cloud.config.server.composite[0].uri:https://source.developers.google.com",
+ "spring.cloud.config.server.composite[0].type:git")
+ .run(context -> {
+ CompositeUtils.getCompositeTypeList(context.getEnvironment());
+ });
+ }
+
+ }
+
}
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/support/GoogleCloudSourceSupportTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/support/GoogleCloudSourceSupportTests.java
new file mode 100644
index 00000000..2899cb07
--- /dev/null
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/support/GoogleCloudSourceSupportTests.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2013-2019 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
+ *
+ * https://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.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.api.TransportConfigCallback;
+import org.eclipse.jgit.transport.SshTransport;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.TransportHttp;
+import org.eclipse.jgit.transport.URIish;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import org.springframework.cloud.config.server.support.GoogleCloudSourceSupport.CredentialsProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author Eduard Wirch
+ */
+public class GoogleCloudSourceSupportTests {
+
+ private static final String HTTPS_GOOGLE_CLOUD_SOURCE_REPO = "https://source.developers.google.com/r/somerepo";
+
+ private static final String HTTP_GOOGLE_CLOUD_SOURCE_REPO = "http://source.developers.google.com/r/somerepo";
+
+ private static final String SSH_GOOGLE_CLOUD_SOURCE_REPO = "ssh://source.developers.google.com/r/somerepo";
+
+ private static final String HTTPS_OTHER_REPO = "https://somehub.com/r/somerepo";
+
+ @Test
+ public void verifySetsAuthHeadersForHttpsGCSRepo() throws URISyntaxException {
+ Map authHeaders = createAuthHeaders();
+ TransportConfigCallback callback = transportConfigCallbackWith(authHeaders);
+
+ TransportHttp transport = mockTransportHttp(HTTPS_GOOGLE_CLOUD_SOURCE_REPO);
+ Map actualHeaders = recordSetHeaders(transport);
+
+ callback.configure(transport);
+
+ assertThat(actualHeaders).containsAllEntriesOf(authHeaders);
+ }
+
+ @Test
+ public void verifySetsAuthHeadersForHttpGCSRepo() throws URISyntaxException {
+ Map authHeaders = createAuthHeaders();
+ TransportConfigCallback callback = transportConfigCallbackWith(authHeaders);
+
+ TransportHttp transport = mockTransportHttp(HTTP_GOOGLE_CLOUD_SOURCE_REPO);
+ Map actualHeaders = recordSetHeaders(transport);
+
+ callback.configure(transport);
+
+ assertThat(actualHeaders).containsAllEntriesOf(authHeaders);
+ }
+
+ @Test
+ public void verifyDoesNothingForSshGCSRepo() throws URISyntaxException {
+ TransportConfigCallback callback = transportConfigCallbackWith(
+ createAuthHeaders());
+ TransportHttp transport = mockTransportHttp(SSH_GOOGLE_CLOUD_SOURCE_REPO);
+
+ callback.configure(transport);
+
+ verifyOnlyValidInteraction(transport);
+ }
+
+ @Test
+ public void verifyDoesNothingForHttpsOtherRepo() throws URISyntaxException {
+ TransportConfigCallback callback = transportConfigCallbackWith(
+ createAuthHeaders());
+ TransportHttp transport = mockTransportHttp(HTTPS_OTHER_REPO);
+
+ callback.configure(transport);
+
+ verifyOnlyValidInteraction(transport);
+ }
+
+ @Test
+ public void verifyDoesNothingForNonHttpTransports() throws URISyntaxException {
+ TransportConfigCallback callback = transportConfigCallbackWith(
+ createAuthHeaders());
+ Transport transport = mockSshTransport(SSH_GOOGLE_CLOUD_SOURCE_REPO);
+
+ callback.configure(transport);
+
+ verifyOnlyValidInteraction(transport);
+ }
+
+ private void verifyOnlyValidInteraction(Transport transport) {
+ // Actually, we don't care how often getURI() was invoked, simply "allow"
+ // invocation of getURI(), so verifyNoMoreInteractions() won't complain
+ // about getURI().
+ verify(transport, atMost(10000)).getURI();
+
+ verifyNoMoreInteractions(transport);
+ }
+
+ private Map createAuthHeaders() {
+ Map headers = new HashMap<>();
+ headers.put("WWW-Authorization", "user:password");
+ return headers;
+ }
+
+ private Map recordSetHeaders(TransportHttp transport) {
+ Map headers = new HashMap<>();
+ doAnswer(invocation -> {
+ headers.putAll(invocation.getArgument(0));
+ return null;
+ }).when(transport).setAdditionalHeaders(anyMap());
+ return headers;
+ }
+
+ private TransportHttp mockTransportHttp(String uri) throws URISyntaxException {
+ TransportHttp transport = Mockito.mock(TransportHttp.class);
+
+ when(transport.getURI()).thenReturn(new URIish(uri));
+
+ return transport;
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ private Transport mockSshTransport(String uri) throws URISyntaxException {
+ Transport transport = Mockito.mock(SshTransport.class);
+
+ when(transport.getURI()).thenReturn(new URIish(uri));
+
+ return transport;
+ }
+
+ private TransportConfigCallback transportConfigCallbackWith(
+ Map authHeaders) {
+ CredentialsProvider credentialsProvider = () -> authHeaders;
+ return new GoogleCloudSourceSupport()
+ .createTransportConfigCallback(credentialsProvider);
+ }
+
+}