| // 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); |
| } |
| } |
| } |