| // Copyright 2022 The Bazel Authors. All rights reserved. | |
| // | |
| // 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 com.google.devtools.build.lib.authandtls; | |
| import static com.google.common.truth.Truth.assertThat; | |
| import static com.google.common.truth.Truth8.assertThat; | |
| import static java.nio.charset.StandardCharsets.UTF_8; | |
| import com.google.auth.Credentials; | |
| import com.google.common.base.Preconditions; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.collect.Iterables; | |
| import com.google.common.eventbus.EventBus; | |
| import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment; | |
| import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperProvider; | |
| import com.google.devtools.build.lib.events.Reporter; | |
| import com.google.devtools.build.lib.runtime.CommandLinePathFactory; | |
| import com.google.devtools.build.lib.testutil.Scratch; | |
| import com.google.devtools.build.lib.vfs.DigestHashFunction; | |
| import com.google.devtools.build.lib.vfs.FileSystem; | |
| import com.google.devtools.build.lib.vfs.Path; | |
| import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; | |
| import com.google.devtools.common.options.OptionsParsingException; | |
| import java.io.IOException; | |
| import java.io.OutputStream; | |
| import java.net.URI; | |
| import java.time.Duration; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.Optional; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| @RunWith(JUnit4.class) | |
| public class GoogleAuthUtilsTest { | |
| @Test | |
| public void testNetrc_emptyEnv_shouldIgnore() throws Exception { | |
| ImmutableMap<String, String> clientEnv = ImmutableMap.of(); | |
| FileSystem fileSystem = new InMemoryFileSystem(DigestHashFunction.SHA256); | |
| assertThat(GoogleAuthUtils.newCredentialsFromNetrc(clientEnv, fileSystem)).isEmpty(); | |
| } | |
| @Test | |
| public void testNetrc_netrcNotExist_shouldIgnore() throws Exception { | |
| String home = "/home/foo"; | |
| ImmutableMap<String, String> clientEnv = ImmutableMap.of("HOME", home); | |
| FileSystem fileSystem = new InMemoryFileSystem(DigestHashFunction.SHA256); | |
| assertThat(GoogleAuthUtils.newCredentialsFromNetrc(clientEnv, fileSystem)).isEmpty(); | |
| } | |
| @Test | |
| public void testNetrc_netrcExist_shouldUse() throws Exception { | |
| String home = "/home/foo"; | |
| ImmutableMap<String, String> clientEnv = ImmutableMap.of("HOME", home); | |
| FileSystem fileSystem = new InMemoryFileSystem(DigestHashFunction.SHA256); | |
| Scratch scratch = new Scratch(fileSystem); | |
| scratch.file(home + "/.netrc", "machine foo.example.org login foouser password foopass"); | |
| Optional<Credentials> credentials = | |
| GoogleAuthUtils.newCredentialsFromNetrc(clientEnv, fileSystem); | |
| assertThat(credentials).isPresent(); | |
| assertRequestMetadata( | |
| credentials.get().getRequestMetadata(URI.create("https://foo.example.org")), | |
| "foouser", | |
| "foopass"); | |
| } | |
| @Test | |
| public void testNetrc_netrcFromNetrcEnvExist_shouldUse() throws Exception { | |
| String home = "/home/foo"; | |
| String netrc = "/.netrc"; | |
| ImmutableMap<String, String> clientEnv = ImmutableMap.of("HOME", home, "NETRC", netrc); | |
| FileSystem fileSystem = new InMemoryFileSystem(DigestHashFunction.SHA256); | |
| Scratch scratch = new Scratch(fileSystem); | |
| scratch.file(home + "/.netrc", "machine foo.example.org login foouser password foopass"); | |
| scratch.file(netrc, "machine foo.example.org login baruser password barpass"); | |
| Optional<Credentials> credentials = | |
| GoogleAuthUtils.newCredentialsFromNetrc(clientEnv, fileSystem); | |
| assertThat(credentials).isPresent(); | |
| assertRequestMetadata( | |
| credentials.get().getRequestMetadata(URI.create("https://foo.example.org")), | |
| "baruser", | |
| "barpass"); | |
| } | |
| @Test | |
| public void testNetrc_netrcFromNetrcEnvNotExist_shouldIgnore() throws Exception { | |
| String home = "/home/foo"; | |
| String netrc = "/.netrc"; | |
| ImmutableMap<String, String> clientEnv = ImmutableMap.of("HOME", home, "NETRC", netrc); | |
| FileSystem fileSystem = new InMemoryFileSystem(DigestHashFunction.SHA256); | |
| Scratch scratch = new Scratch(fileSystem); | |
| scratch.file(home + "/.netrc", "machine foo.example.org login foouser password foopass"); | |
| assertThat(GoogleAuthUtils.newCredentialsFromNetrc(clientEnv, fileSystem)).isEmpty(); | |
| } | |
| @Test | |
| public void testCredentialHelperProvider() throws Exception { | |
| FileSystem fileSystem = new InMemoryFileSystem(DigestHashFunction.SHA256); | |
| Path workspace = fileSystem.getPath("/workspace"); | |
| Path pathValue = fileSystem.getPath("/usr/local/bin"); | |
| pathValue.createDirectoryAndParents(); | |
| CredentialHelperEnvironment credentialHelperEnvironment = | |
| CredentialHelperEnvironment.newBuilder() | |
| .setEventReporter(new Reporter(new EventBus())) | |
| .setWorkspacePath(workspace) | |
| .setClientEnvironment(ImmutableMap.of("PATH", pathValue.getPathString())) | |
| .setHelperExecutionTimeout(Duration.ZERO) | |
| .build(); | |
| CommandLinePathFactory commandLinePathFactory = | |
| new CommandLinePathFactory(fileSystem, ImmutableMap.of("workspace", workspace)); | |
| Path unusedHelper = createExecutable(fileSystem, "/unused/helper"); | |
| Path defaultHelper = createExecutable(fileSystem, "/default/helper"); | |
| Path exampleComHelper = createExecutable(fileSystem, "/example/com/helper"); | |
| Path fooExampleComHelper = createExecutable(fileSystem, "/foo/example/com/helper"); | |
| Path exampleComWildcardHelper = createExecutable(fileSystem, "/example/com/wildcard/helper"); | |
| Path exampleOrgHelper = createExecutable(workspace.getRelative("helpers/example-org")); | |
| // No helpers. | |
| CredentialHelperProvider credentialHelperProvider1 = | |
| newCredentialHelperProvider( | |
| credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of()); | |
| assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://example.com"))) | |
| .isEmpty(); | |
| assertThat( | |
| credentialHelperProvider1.findCredentialHelper(URI.create("https://foo.example.com"))) | |
| .isEmpty(); | |
| // Default helper only. | |
| CredentialHelperProvider credentialHelperProvider2 = | |
| newCredentialHelperProvider( | |
| credentialHelperEnvironment, | |
| commandLinePathFactory, | |
| ImmutableList.of(defaultHelper.getPathString())); | |
| assertThat( | |
| credentialHelperProvider2 | |
| .findCredentialHelper(URI.create("https://example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(defaultHelper); | |
| assertThat( | |
| credentialHelperProvider2 | |
| .findCredentialHelper(URI.create("https://foo.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(defaultHelper); | |
| // Default and exact match. | |
| CredentialHelperProvider credentialHelperProvider3 = | |
| newCredentialHelperProvider( | |
| credentialHelperEnvironment, | |
| commandLinePathFactory, | |
| ImmutableList.of( | |
| defaultHelper.getPathString(), "example.com=" + exampleComHelper.getPathString())); | |
| assertThat( | |
| credentialHelperProvider3 | |
| .findCredentialHelper(URI.create("https://example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleComHelper); | |
| assertThat( | |
| credentialHelperProvider3 | |
| .findCredentialHelper(URI.create("https://foo.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(defaultHelper); | |
| // Exact match without default. | |
| CredentialHelperProvider credentialHelperProvider4 = | |
| newCredentialHelperProvider( | |
| credentialHelperEnvironment, | |
| commandLinePathFactory, | |
| ImmutableList.of("example.com=" + exampleComHelper.getPathString())); | |
| assertThat( | |
| credentialHelperProvider4 | |
| .findCredentialHelper(URI.create("https://example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleComHelper); | |
| assertThat( | |
| credentialHelperProvider4.findCredentialHelper(URI.create("https://foo.example.com"))) | |
| .isEmpty(); | |
| // Multiple scoped helpers with default. | |
| CredentialHelperProvider credentialHelperProvider5 = | |
| newCredentialHelperProvider( | |
| credentialHelperEnvironment, | |
| commandLinePathFactory, | |
| ImmutableList.of( | |
| defaultHelper.getPathString(), | |
| "example.com=" + exampleComHelper.getPathString(), | |
| "*.foo.example.com=" + fooExampleComHelper.getPathString(), | |
| "*.example.com=" + exampleComWildcardHelper.getPathString(), | |
| "example.org=%workspace%/helpers/example-org")); | |
| assertThat( | |
| credentialHelperProvider5 | |
| .findCredentialHelper(URI.create("https://anotherdomain.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(defaultHelper); | |
| assertThat( | |
| credentialHelperProvider5 | |
| .findCredentialHelper(URI.create("https://example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleComHelper); | |
| assertThat( | |
| credentialHelperProvider5 | |
| .findCredentialHelper(URI.create("https://foo.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(fooExampleComHelper); | |
| assertThat( | |
| credentialHelperProvider5 | |
| .findCredentialHelper(URI.create("https://abc.foo.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(fooExampleComHelper); | |
| assertThat( | |
| credentialHelperProvider5 | |
| .findCredentialHelper(URI.create("https://bar.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleComWildcardHelper); | |
| assertThat( | |
| credentialHelperProvider5 | |
| .findCredentialHelper(URI.create("https://abc.bar.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleComWildcardHelper); | |
| assertThat( | |
| credentialHelperProvider5 | |
| .findCredentialHelper(URI.create("https://example.org")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleOrgHelper); | |
| // Helpers override. | |
| CredentialHelperProvider credentialHelperProvider6 = | |
| newCredentialHelperProvider( | |
| credentialHelperEnvironment, | |
| commandLinePathFactory, | |
| ImmutableList.of( | |
| // <system .bazelrc> | |
| unusedHelper.getPathString(), | |
| // <user .bazelrc> | |
| defaultHelper.getPathString(), | |
| "example.com=" + unusedHelper.getPathString(), | |
| "*.example.com=" + unusedHelper.getPathString(), | |
| "example.org=" + unusedHelper.getPathString(), | |
| "*.example.org=" + exampleOrgHelper.getPathString(), | |
| // <workspace .bazelrc> | |
| "*.example.com=" + exampleComWildcardHelper.getPathString(), | |
| "example.org=" + exampleOrgHelper.getPathString(), | |
| "*.foo.example.com=" + unusedHelper.getPathString(), | |
| // <command-line> | |
| "example.com=" + exampleComHelper.getPathString(), | |
| "*.foo.example.com=" + fooExampleComHelper.getPathString())); | |
| assertThat( | |
| credentialHelperProvider6 | |
| .findCredentialHelper(URI.create("https://anotherdomain.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(defaultHelper); | |
| assertThat( | |
| credentialHelperProvider6 | |
| .findCredentialHelper(URI.create("https://example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleComHelper); | |
| assertThat( | |
| credentialHelperProvider6 | |
| .findCredentialHelper(URI.create("https://foo.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(fooExampleComHelper); | |
| assertThat( | |
| credentialHelperProvider6 | |
| .findCredentialHelper(URI.create("https://bar.example.com")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleComWildcardHelper); | |
| assertThat( | |
| credentialHelperProvider6 | |
| .findCredentialHelper(URI.create("https://example.org")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleOrgHelper); | |
| assertThat( | |
| credentialHelperProvider6 | |
| .findCredentialHelper(URI.create("https://foo.example.org")) | |
| .get() | |
| .getPath()) | |
| .isEqualTo(exampleOrgHelper); | |
| } | |
| private static Path createExecutable(FileSystem fileSystem, String path) throws IOException { | |
| Preconditions.checkNotNull(fileSystem); | |
| Preconditions.checkNotNull(path); | |
| return createExecutable(fileSystem.getPath(path)); | |
| } | |
| private static Path createExecutable(Path path) throws IOException { | |
| Preconditions.checkNotNull(path); | |
| path.getParentDirectory().createDirectoryAndParents(); | |
| try (OutputStream unused = path.getOutputStream()) { | |
| // Nothing to do. | |
| } | |
| path.setExecutable(true); | |
| return path; | |
| } | |
| private static void assertRequestMetadata( | |
| Map<String, List<String>> requestMetadata, String username, String password) { | |
| assertThat(requestMetadata.keySet()).containsExactly("Authorization"); | |
| assertThat(Iterables.getOnlyElement(requestMetadata.values())) | |
| .containsExactly(BasicHttpAuthenticationEncoder.encode(username, password, UTF_8)); | |
| } | |
| private static CredentialHelperProvider newCredentialHelperProvider( | |
| CredentialHelperEnvironment credentialHelperEnvironment, | |
| CommandLinePathFactory commandLinePathFactory, | |
| ImmutableList<String> inputs) | |
| throws Exception { | |
| Preconditions.checkNotNull(credentialHelperEnvironment); | |
| Preconditions.checkNotNull(commandLinePathFactory); | |
| Preconditions.checkNotNull(inputs); | |
| return GoogleAuthUtils.newCredentialHelperProvider( | |
| credentialHelperEnvironment, | |
| commandLinePathFactory, | |
| ImmutableList.copyOf( | |
| Iterables.transform(inputs, s -> createUnresolvedScopedCredentialHelper(s)))); | |
| } | |
| private static AuthAndTLSOptions.UnresolvedScopedCredentialHelper | |
| createUnresolvedScopedCredentialHelper(String input) { | |
| Preconditions.checkNotNull(input); | |
| try { | |
| return AuthAndTLSOptions.UnresolvedScopedCredentialHelperConverter.INSTANCE.convert(input); | |
| } catch (OptionsParsingException e) { | |
| throw new IllegalStateException(e); | |
| } | |
| } | |
| } |