From 589d95bfceba20898ed842592c41a8a4388218ba Mon Sep 17 00:00:00 2001 From: Yannic Bonenberger Date: Mon, 18 Jul 2022 16:02:45 +0200 Subject: [PATCH 1/3] [credentialhelper] Add parser for flag syntax Progress on #15856 --- .../credentialhelper/CredentialHelper.java | 2 +- .../CredentialHelperProvider.java | 17 ++ .../google/devtools/build/lib/remote/BUILD | 2 + .../build/lib/remote/RemoteModule.java | 53 ++++ .../google/devtools/build/lib/remote/BUILD | 1 + .../build/lib/remote/RemoteModuleTest.java | 230 +++++++++++++++++- 6 files changed, 303 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelper.java b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelper.java index c82417b0034383..e410c0b52bc771 100644 --- a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelper.java +++ b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelper.java @@ -49,7 +49,7 @@ public final class CredentialHelper { } @VisibleForTesting - Path getPath() { + public Path getPath() { return path; } diff --git a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java index ded7e5eab3c0d0..33dcc3dac11214 100644 --- a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java +++ b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java @@ -129,6 +129,23 @@ private void checkHelper(Path path) throws IOException { path.isExecutable(), "Credential helper %s is not executable", path); } + /** + * Adds a credential helper to use for all {@link URI}s matching the provided pattern, or as + * default credential helper if {@code pattern} is empty. + * + *

See {@link #add(String, Path)} for the syntax of {@code pattern}. + */ + public Builder add(Optional pattern, Path helper) throws IOException { + Preconditions.checkNotNull(pattern); + Preconditions.checkNotNull(helper); + + if (pattern.isPresent()) { + return add(pattern.get(), helper); + } else { + return add(helper); + } + } + /** * Adds a default credential helper to use for all {@link URI}s that don't specify a more * specific credential helper. diff --git a/src/main/java/com/google/devtools/build/lib/remote/BUILD b/src/main/java/com/google/devtools/build/lib/remote/BUILD index ed33a59565c956..1ed01f28639a1b 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/BUILD +++ b/src/main/java/com/google/devtools/build/lib/remote/BUILD @@ -59,6 +59,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/analysis:top_level_artifact_context", "//src/main/java/com/google/devtools/build/lib/analysis/platform:platform_utils", "//src/main/java/com/google/devtools/build/lib/authandtls", + "//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper", "//src/main/java/com/google/devtools/build/lib/bazel/repository/downloader", "//src/main/java/com/google/devtools/build/lib/buildeventstream", "//src/main/java/com/google/devtools/build/lib/clock", @@ -102,6 +103,7 @@ java_library( "//src/main/java/com/google/devtools/common/options", "//src/main/protobuf:failure_details_java_proto", "//third_party:auth", + "//third_party:auto_value", "//third_party:caffeine", "//third_party:flogger", "//third_party:guava", diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java index 19a2b7d0a85ecc..c5f59d87114c4e 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java +++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java @@ -19,6 +19,7 @@ import build.bazel.remote.execution.v2.DigestFunction; import build.bazel.remote.execution.v2.ServerCapabilities; import com.google.auth.Credentials; +import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Ascii; import com.google.common.base.Preconditions; @@ -50,6 +51,8 @@ import com.google.devtools.build.lib.authandtls.Netrc; import com.google.devtools.build.lib.authandtls.NetrcCredentials; import com.google.devtools.build.lib.authandtls.NetrcParser; +import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment; +import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperProvider; import com.google.devtools.build.lib.bazel.repository.downloader.Downloader; import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader; import com.google.devtools.build.lib.buildeventstream.LocalFilesArtifactUploader; @@ -78,6 +81,7 @@ import com.google.devtools.build.lib.runtime.BuildEventArtifactUploaderFactory; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; +import com.google.devtools.build.lib.runtime.CommandLinePathFactory; import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor; import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutorFactory; import com.google.devtools.build.lib.runtime.ServerBuilder; @@ -1130,4 +1134,53 @@ static Credentials newCredentials( return creds; } + + @VisibleForTesting + static CredentialHelperProvider newCredentialHelperProvider( + CredentialHelperEnvironment environment, + CommandLinePathFactory pathFactory, + List inputs) + throws IOException { + Preconditions.checkNotNull(environment); + Preconditions.checkNotNull(pathFactory); + Preconditions.checkNotNull(inputs); + + CredentialHelperProvider.Builder builder = CredentialHelperProvider.builder(); + for (String input : inputs) { + ScopedCredentialHelper helper = parseCredentialHelperFlag(environment, pathFactory, input); + builder.add(helper.getScope(), helper.getPath()); + } + return builder.build(); + } + + @VisibleForTesting + static ScopedCredentialHelper parseCredentialHelperFlag( + CredentialHelperEnvironment environment, CommandLinePathFactory pathFactory, String input) + throws IOException { + Preconditions.checkNotNull(environment); + Preconditions.checkNotNull(pathFactory); + Preconditions.checkNotNull(input); + + int pos = input.indexOf('='); + if (pos > 0) { + String scope = input.substring(0, pos); + String path = input.substring(pos + 1); + return new AutoValue_RemoteModule_ScopedCredentialHelper( + Optional.of(scope), pathFactory.create(environment.getClientEnvironment(), path)); + } + + // `input` does not specify a scope. + return new AutoValue_RemoteModule_ScopedCredentialHelper( + Optional.empty(), pathFactory.create(environment.getClientEnvironment(), input)); + } + + @VisibleForTesting + @AutoValue + static abstract class ScopedCredentialHelper { + /** Returns the scope of the credential helper (if any). */ + public abstract Optional getScope(); + + /** Returns the path of the credential helper. */ + public abstract Path getPath(); + } } diff --git a/src/test/java/com/google/devtools/build/lib/remote/BUILD b/src/test/java/com/google/devtools/build/lib/remote/BUILD index 44df625842371a..0102233157caee 100644 --- a/src/test/java/com/google/devtools/build/lib/remote/BUILD +++ b/src/test/java/com/google/devtools/build/lib/remote/BUILD @@ -56,6 +56,7 @@ java_test( "//src/main/java/com/google/devtools/build/lib/analysis:server_directories", "//src/main/java/com/google/devtools/build/lib/analysis/platform:platform_utils", "//src/main/java/com/google/devtools/build/lib/authandtls", + "//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper", "//src/main/java/com/google/devtools/build/lib/buildeventstream", "//src/main/java/com/google/devtools/build/lib/clock", "//src/main/java/com/google/devtools/build/lib/collect/nestedset", diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java index bfc0171b6b84f6..027287570c17f8 100644 --- a/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java +++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java @@ -27,6 +27,7 @@ import build.bazel.remote.execution.v2.GetCapabilitiesRequest; import build.bazel.remote.execution.v2.ServerCapabilities; 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; @@ -36,6 +37,8 @@ import com.google.devtools.build.lib.analysis.config.CoreOptions; import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions; import com.google.devtools.build.lib.authandtls.BasicHttpAuthenticationEncoder; +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.exec.BinTools; import com.google.devtools.build.lib.exec.ExecutionOptions; @@ -47,12 +50,14 @@ import com.google.devtools.build.lib.runtime.ClientOptions; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; +import com.google.devtools.build.lib.runtime.CommandLinePathFactory; import com.google.devtools.build.lib.runtime.CommonCommandOptions; import com.google.devtools.build.lib.runtime.commands.BuildCommand; import com.google.devtools.build.lib.testutil.Scratch; import com.google.devtools.build.lib.util.AbruptExitException; 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.Options; import com.google.devtools.common.options.OptionsParser; @@ -65,7 +70,9 @@ import io.grpc.stub.StreamObserver; import io.grpc.util.MutableHandlerRegistry; import java.io.IOException; +import java.io.OutputStream; import java.net.URI; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -576,4 +583,225 @@ private static void assertRequestMetadata( assertThat(Iterables.getOnlyElement(requestMetadata.values())) .containsExactly(BasicHttpAuthenticationEncoder.encode(username, password, UTF_8)); } -} + + @Test + public void parseCredentialHelperFlag() 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.builder() + .put("workspace", workspace) + .build()); + + // Absolute paths. + { + RemoteModule.ScopedCredentialHelper helper1 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "/absolute/path"); + assertThat(helper1.getScope().isPresent()).isFalse(); + assertThat(helper1.getPath()).isEqualTo(fileSystem.getPath("/absolute/path")); + + RemoteModule.ScopedCredentialHelper helper2 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "example.com=/absolute/path"); + assertThat(helper2.getScope().get()).isEqualTo("example.com"); + assertThat(helper2.getPath()).isEqualTo(fileSystem.getPath("/absolute/path")); + + RemoteModule.ScopedCredentialHelper helper3 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "*.example.com=/absolute/path"); + assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); + assertThat(helper3.getPath()).isEqualTo(fileSystem.getPath("/absolute/path")); + } + + // Root-relative paths. + { + RemoteModule.ScopedCredentialHelper helper1 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "%workspace%/path"); + assertThat(helper1.getScope().isPresent()).isFalse(); + assertThat(helper1.getPath()).isEqualTo(workspace.getRelative("path")); + + RemoteModule.ScopedCredentialHelper helper2 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "example.com=%workspace%/path"); + assertThat(helper2.getScope().get()).isEqualTo("example.com"); + assertThat(helper2.getPath()).isEqualTo(workspace.getRelative("path")); + + RemoteModule.ScopedCredentialHelper helper3 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "*.example.com=%workspace%/path"); + assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); + assertThat(helper3.getPath()).isEqualTo(workspace.getRelative("path")); + } + + // PATH lookup. + { + Path helper = createExecutable(pathValue.getRelative("foo")); + + RemoteModule.ScopedCredentialHelper helper1 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "foo"); + assertThat(helper1.getScope().isPresent()).isFalse(); + assertThat(helper1.getPath()).isEqualTo(pathValue.getRelative("foo")); + + RemoteModule.ScopedCredentialHelper helper2 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "example.com=foo"); + assertThat(helper2.getScope().get()).isEqualTo("example.com"); + assertThat(helper2.getPath()).isEqualTo(pathValue.getRelative("foo")); + + RemoteModule.ScopedCredentialHelper helper3 = + RemoteModule.parseCredentialHelperFlag( + credentialHelperEnvironment, commandLinePathFactory, "*.example.com=foo"); + assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); + assertThat(helper3.getPath()).isEqualTo(pathValue.getRelative("foo")); + } + } + + @Test + public void newCredentialHelperProvider() 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.builder() + .put("workspace", workspace) + .build()); + + 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 = RemoteModule.newCredentialHelperProvider( + credentialHelperEnvironment, + commandLinePathFactory, + ImmutableList.of()); + assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://example.com")).isPresent()).isFalse(); + assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://foo.example.com")).isPresent()).isFalse(); + + // Default helper only. + CredentialHelperProvider credentialHelperProvider2 = RemoteModule.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 = RemoteModule.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 = RemoteModule.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")).isPresent()).isFalse(); + + // Multiple scoped helpers with default. + CredentialHelperProvider credentialHelperProvider5 = RemoteModule.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 = RemoteModule.newCredentialHelperProvider( + credentialHelperEnvironment, + commandLinePathFactory, + ImmutableList.of( + // + unusedHelper.getPathString(), + + // + defaultHelper.getPathString(), + "example.com=" + unusedHelper.getPathString(), + "*.example.com=" + unusedHelper.getPathString(), + "example.org=" + unusedHelper.getPathString(), + "*.example.org=" + exampleOrgHelper.getPathString(), + + // + "*.example.com=" + exampleComWildcardHelper.getPathString(), + "example.org=" + exampleOrgHelper.getPathString(), + "*.foo.example.com=" + unusedHelper.getPathString(), + + // + "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; + } +} \ No newline at end of file From df8a9e262ea75e1227e44887a556fc5f2dcd03fd Mon Sep 17 00:00:00 2001 From: Yannic Bonenberger Date: Mon, 18 Jul 2022 16:42:46 +0200 Subject: [PATCH 2/3] Address most comments --- .../CredentialHelperProvider.java | 17 --------------- .../build/lib/remote/RemoteModule.java | 7 ++++++- .../build/lib/remote/RemoteModuleTest.java | 21 ++++++++----------- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java index 33dcc3dac11214..ded7e5eab3c0d0 100644 --- a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java +++ b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperProvider.java @@ -129,23 +129,6 @@ private void checkHelper(Path path) throws IOException { path.isExecutable(), "Credential helper %s is not executable", path); } - /** - * Adds a credential helper to use for all {@link URI}s matching the provided pattern, or as - * default credential helper if {@code pattern} is empty. - * - *

See {@link #add(String, Path)} for the syntax of {@code pattern}. - */ - public Builder add(Optional pattern, Path helper) throws IOException { - Preconditions.checkNotNull(pattern); - Preconditions.checkNotNull(helper); - - if (pattern.isPresent()) { - return add(pattern.get(), helper); - } else { - return add(helper); - } - } - /** * Adds a default credential helper to use for all {@link URI}s that don't specify a more * specific credential helper. diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java index c5f59d87114c4e..968d99cc82e7e6 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java +++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java @@ -1148,7 +1148,12 @@ static CredentialHelperProvider newCredentialHelperProvider( CredentialHelperProvider.Builder builder = CredentialHelperProvider.builder(); for (String input : inputs) { ScopedCredentialHelper helper = parseCredentialHelperFlag(environment, pathFactory, input); - builder.add(helper.getScope(), helper.getPath()); + Optional scope = helper.getScope(); + if (scope.isPresent()) { + builder.add(scope.get(), helper.getPath()); + } else { + builder.add(helper.getPath()); + } } return builder.build(); } diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java index 027287570c17f8..e25ce51495a083 100644 --- a/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java +++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.remote; 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 static org.junit.Assert.assertThrows; import static org.mockito.Mockito.mock; @@ -602,16 +603,14 @@ public void parseCredentialHelperFlag() throws Exception { CommandLinePathFactory commandLinePathFactory = new CommandLinePathFactory( fileSystem, - ImmutableMap.builder() - .put("workspace", workspace) - .build()); + ImmutableMap.of("workspace", workspace)); // Absolute paths. { RemoteModule.ScopedCredentialHelper helper1 = RemoteModule.parseCredentialHelperFlag( credentialHelperEnvironment, commandLinePathFactory, "/absolute/path"); - assertThat(helper1.getScope().isPresent()).isFalse(); + assertThat(helper1.getScope()).isEmpty(); assertThat(helper1.getPath()).isEqualTo(fileSystem.getPath("/absolute/path")); RemoteModule.ScopedCredentialHelper helper2 = @@ -632,7 +631,7 @@ public void parseCredentialHelperFlag() throws Exception { RemoteModule.ScopedCredentialHelper helper1 = RemoteModule.parseCredentialHelperFlag( credentialHelperEnvironment, commandLinePathFactory, "%workspace%/path"); - assertThat(helper1.getScope().isPresent()).isFalse(); + assertThat(helper1.getScope()).isEmpty(); assertThat(helper1.getPath()).isEqualTo(workspace.getRelative("path")); RemoteModule.ScopedCredentialHelper helper2 = @@ -655,7 +654,7 @@ public void parseCredentialHelperFlag() throws Exception { RemoteModule.ScopedCredentialHelper helper1 = RemoteModule.parseCredentialHelperFlag( credentialHelperEnvironment, commandLinePathFactory, "foo"); - assertThat(helper1.getScope().isPresent()).isFalse(); + assertThat(helper1.getScope()).isEmpty(); assertThat(helper1.getPath()).isEqualTo(pathValue.getRelative("foo")); RemoteModule.ScopedCredentialHelper helper2 = @@ -690,9 +689,7 @@ public void newCredentialHelperProvider() throws Exception { CommandLinePathFactory commandLinePathFactory = new CommandLinePathFactory( fileSystem, - ImmutableMap.builder() - .put("workspace", workspace) - .build()); + ImmutableMap.of("workspace", workspace)); Path unusedHelper = createExecutable(fileSystem, "/unused/helper"); @@ -708,8 +705,8 @@ public void newCredentialHelperProvider() throws Exception { credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of()); - assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://example.com")).isPresent()).isFalse(); - assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://foo.example.com")).isPresent()).isFalse(); + assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://example.com"))).isEmpty(); + assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://foo.example.com"))).isEmpty(); // Default helper only. CredentialHelperProvider credentialHelperProvider2 = RemoteModule.newCredentialHelperProvider( @@ -735,7 +732,7 @@ public void newCredentialHelperProvider() throws Exception { 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")).isPresent()).isFalse(); + assertThat(credentialHelperProvider4.findCredentialHelper(URI.create("https://foo.example.com"))).isEmpty(); // Multiple scoped helpers with default. CredentialHelperProvider credentialHelperProvider5 = RemoteModule.newCredentialHelperProvider( From a71b0ea468eb0bd2761ef493b147fbd163395695 Mon Sep 17 00:00:00 2001 From: Yannic Bonenberger Date: Mon, 18 Jul 2022 17:40:42 +0200 Subject: [PATCH 3/3] More code review --- .../lib/authandtls/AuthAndTLSOptions.java | 56 ++++++++ .../build/lib/remote/RemoteModule.java | 35 ++--- .../devtools/build/lib/authandtls/BUILD | 1 + ...edScopedCredentialHelperConverterTest.java | 83 ++++++++++++ .../build/lib/remote/RemoteModuleTest.java | 126 +++++------------- 5 files changed, 182 insertions(+), 119 deletions(-) create mode 100644 src/test/java/com/google/devtools/build/lib/authandtls/UnresolvedScopedCredentialHelperConverterTest.java diff --git a/src/main/java/com/google/devtools/build/lib/authandtls/AuthAndTLSOptions.java b/src/main/java/com/google/devtools/build/lib/authandtls/AuthAndTLSOptions.java index 2a54e3d7f96297..81f12d418d1551 100644 --- a/src/main/java/com/google/devtools/build/lib/authandtls/AuthAndTLSOptions.java +++ b/src/main/java/com/google/devtools/build/lib/authandtls/AuthAndTLSOptions.java @@ -14,6 +14,10 @@ package com.google.devtools.build.lib.authandtls; +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; import com.google.devtools.common.options.Converters.DurationConverter; import com.google.devtools.common.options.Option; @@ -21,8 +25,12 @@ import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionMetadataTag; import com.google.devtools.common.options.OptionsBase; +import com.google.devtools.common.options.OptionsParsingException; import java.time.Duration; import java.util.List; +import java.util.Locale; +import java.util.Optional; +import javax.annotation.Nullable; /** * Common options for authentication and TLS. @@ -131,4 +139,52 @@ public class AuthAndTLSOptions extends OptionsBase { + "granularity; it is an error to set a value less than one second. If keep-alive " + "pings are disabled, then this setting is ignored.") public Duration grpcKeepaliveTimeout; + + // TODO(yannic): Use this for `--credential_helper`. + @AutoValue + public static abstract class UnresolvedScopedCredentialHelper { + /** Returns the scope of the credential helper (if any). */ + public abstract Optional getScope(); + + /** Returns the (unparsed) path of the credential helper. */ + public abstract String getPath(); + } + + public static final class UnresolvedScopedCredentialHelperConverter + extends Converter.Contextless { + public static final UnresolvedScopedCredentialHelperConverter INSTANCE = + new UnresolvedScopedCredentialHelperConverter(); + + @Override + public String getTypeDescription() { + return "An (unresolved) path to a credential helper for a scope."; + } + + @Override + public UnresolvedScopedCredentialHelper convert(String input) throws OptionsParsingException { + Preconditions.checkNotNull(input); + + int pos = input.indexOf('='); + if (pos >= 0) { + String scope = input.substring(0, pos); + if (Strings.isNullOrEmpty(scope)) { + throw new OptionsParsingException("Scope of credential helper must not be empty"); + } + String path = checkPath(input.substring(pos + 1)); + return new AutoValue_AuthAndTLSOptions_UnresolvedScopedCredentialHelper( + Optional.of(scope), path); + } + + // `input` does not specify a scope. + return new AutoValue_AuthAndTLSOptions_UnresolvedScopedCredentialHelper( + Optional.empty(), checkPath(input)); + } + + private String checkPath(@Nullable String input) throws OptionsParsingException { + if (Strings.isNullOrEmpty(input)) { + throw new OptionsParsingException("Path to credential helper must not be empty"); + } + return input; + } + } } diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java index 968d99cc82e7e6..c202ab0cd18ae9 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java +++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java @@ -46,6 +46,7 @@ import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; import com.google.devtools.build.lib.analysis.test.TestProvider; import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions; +import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions.UnresolvedScopedCredentialHelper; import com.google.devtools.build.lib.authandtls.CallCredentialsProvider; import com.google.devtools.build.lib.authandtls.GoogleAuthUtils; import com.google.devtools.build.lib.authandtls.Netrc; @@ -99,6 +100,7 @@ import com.google.devtools.build.lib.vfs.OutputService; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.common.options.OptionsBase; +import com.google.devtools.common.options.OptionsParsingException; import com.google.devtools.common.options.OptionsParsingResult; import io.grpc.CallCredentials; import io.grpc.Channel; @@ -1139,46 +1141,25 @@ static Credentials newCredentials( static CredentialHelperProvider newCredentialHelperProvider( CredentialHelperEnvironment environment, CommandLinePathFactory pathFactory, - List inputs) + List helpers) throws IOException { Preconditions.checkNotNull(environment); Preconditions.checkNotNull(pathFactory); - Preconditions.checkNotNull(inputs); + Preconditions.checkNotNull(helpers); CredentialHelperProvider.Builder builder = CredentialHelperProvider.builder(); - for (String input : inputs) { - ScopedCredentialHelper helper = parseCredentialHelperFlag(environment, pathFactory, input); + for (UnresolvedScopedCredentialHelper helper : helpers) { Optional scope = helper.getScope(); + Path path = pathFactory.create(environment.getClientEnvironment(), helper.getPath()); if (scope.isPresent()) { - builder.add(scope.get(), helper.getPath()); + builder.add(scope.get(), path); } else { - builder.add(helper.getPath()); + builder.add(path); } } return builder.build(); } - @VisibleForTesting - static ScopedCredentialHelper parseCredentialHelperFlag( - CredentialHelperEnvironment environment, CommandLinePathFactory pathFactory, String input) - throws IOException { - Preconditions.checkNotNull(environment); - Preconditions.checkNotNull(pathFactory); - Preconditions.checkNotNull(input); - - int pos = input.indexOf('='); - if (pos > 0) { - String scope = input.substring(0, pos); - String path = input.substring(pos + 1); - return new AutoValue_RemoteModule_ScopedCredentialHelper( - Optional.of(scope), pathFactory.create(environment.getClientEnvironment(), path)); - } - - // `input` does not specify a scope. - return new AutoValue_RemoteModule_ScopedCredentialHelper( - Optional.empty(), pathFactory.create(environment.getClientEnvironment(), input)); - } - @VisibleForTesting @AutoValue static abstract class ScopedCredentialHelper { diff --git a/src/test/java/com/google/devtools/build/lib/authandtls/BUILD b/src/test/java/com/google/devtools/build/lib/authandtls/BUILD index f9125da2d92771..6dd0b2f276559d 100644 --- a/src/test/java/com/google/devtools/build/lib/authandtls/BUILD +++ b/src/test/java/com/google/devtools/build/lib/authandtls/BUILD @@ -25,6 +25,7 @@ java_library( ), deps = [ "//src/main/java/com/google/devtools/build/lib/authandtls", + "//src/main/java/com/google/devtools/common/options:options", "//third_party:guava", "//third_party:junit4", "//third_party:truth", diff --git a/src/test/java/com/google/devtools/build/lib/authandtls/UnresolvedScopedCredentialHelperConverterTest.java b/src/test/java/com/google/devtools/build/lib/authandtls/UnresolvedScopedCredentialHelperConverterTest.java new file mode 100644 index 00000000000000..150ece1177d9fd --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/authandtls/UnresolvedScopedCredentialHelperConverterTest.java @@ -0,0 +1,83 @@ +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 org.junit.Assert.assertThrows; + +import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions.UnresolvedScopedCredentialHelper; +import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions.UnresolvedScopedCredentialHelperConverter; +import com.google.devtools.common.options.OptionsParsingException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link UnresolvedScopedCredentialHelperConverter}. */ +@RunWith(JUnit4.class) +public class UnresolvedScopedCredentialHelperConverterTest { + @Test + public void convertAbsolutePath() throws Exception { + UnresolvedScopedCredentialHelper helper1 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("/absolute/path"); + assertThat(helper1.getScope()).isEmpty(); + assertThat(helper1.getPath()).isEqualTo("/absolute/path"); + + UnresolvedScopedCredentialHelper helper2 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("example.com=/absolute/path"); + assertThat(helper2.getScope().get()).isEqualTo("example.com"); + assertThat(helper2.getPath()).isEqualTo("/absolute/path"); + + UnresolvedScopedCredentialHelper helper3 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("*.example.com=/absolute/path"); + assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); + assertThat(helper3.getPath()).isEqualTo("/absolute/path"); + } + + @Test + public void convertRootRelativePath() throws Exception { + UnresolvedScopedCredentialHelper helper1 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("%workspace%/path"); + assertThat(helper1.getScope()).isEmpty(); + assertThat(helper1.getPath()).isEqualTo("%workspace%/path"); + + UnresolvedScopedCredentialHelper helper2 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("example.com=%workspace%/path"); + assertThat(helper2.getScope().get()).isEqualTo("example.com"); + assertThat(helper2.getPath()).isEqualTo("%workspace%/path"); + + UnresolvedScopedCredentialHelper helper3 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("*.example.com=%workspace%/path"); + assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); + assertThat(helper3.getPath()).isEqualTo("%workspace%/path"); + } + + @Test + public void convertPathLookup() throws Exception { + UnresolvedScopedCredentialHelper helper1 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("foo"); + assertThat(helper1.getScope()).isEmpty(); + assertThat(helper1.getPath()).isEqualTo("foo"); + + UnresolvedScopedCredentialHelper helper2 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("example.com=foo"); + assertThat(helper2.getScope().get()).isEqualTo("example.com"); + assertThat(helper2.getPath()).isEqualTo("foo"); + + UnresolvedScopedCredentialHelper helper3 = + UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("*.example.com=foo"); + assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); + assertThat(helper3.getPath()).isEqualTo("foo"); + } + + @Test + public void emptyPath() { + assertThrows(OptionsParsingException.class, () -> UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("")); + assertThrows(OptionsParsingException.class, () -> UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("foo=")); + assertThrows(OptionsParsingException.class, () -> UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("=")); + } + + @Test + public void emptyScope() { + assertThrows(OptionsParsingException.class, () -> UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("=/foo")); + assertThrows(OptionsParsingException.class, () -> UnresolvedScopedCredentialHelperConverter.INSTANCE.convert("=")); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java index e25ce51495a083..f15ec92500e10b 100644 --- a/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java +++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteModuleTest.java @@ -37,6 +37,8 @@ import com.google.devtools.build.lib.analysis.ServerDirectories; import com.google.devtools.build.lib.analysis.config.CoreOptions; import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions; +import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions.UnresolvedScopedCredentialHelper; +import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions.UnresolvedScopedCredentialHelperConverter; import com.google.devtools.build.lib.authandtls.BasicHttpAuthenticationEncoder; import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment; import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperProvider; @@ -62,6 +64,7 @@ import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.common.options.Options; import com.google.devtools.common.options.OptionsParser; +import com.google.devtools.common.options.OptionsParsingException; import com.google.devtools.common.options.OptionsParsingResult; import io.grpc.BindableService; import io.grpc.Server; @@ -585,92 +588,6 @@ private static void assertRequestMetadata( .containsExactly(BasicHttpAuthenticationEncoder.encode(username, password, UTF_8)); } - @Test - public void parseCredentialHelperFlag() 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)); - - // Absolute paths. - { - RemoteModule.ScopedCredentialHelper helper1 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "/absolute/path"); - assertThat(helper1.getScope()).isEmpty(); - assertThat(helper1.getPath()).isEqualTo(fileSystem.getPath("/absolute/path")); - - RemoteModule.ScopedCredentialHelper helper2 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "example.com=/absolute/path"); - assertThat(helper2.getScope().get()).isEqualTo("example.com"); - assertThat(helper2.getPath()).isEqualTo(fileSystem.getPath("/absolute/path")); - - RemoteModule.ScopedCredentialHelper helper3 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "*.example.com=/absolute/path"); - assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); - assertThat(helper3.getPath()).isEqualTo(fileSystem.getPath("/absolute/path")); - } - - // Root-relative paths. - { - RemoteModule.ScopedCredentialHelper helper1 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "%workspace%/path"); - assertThat(helper1.getScope()).isEmpty(); - assertThat(helper1.getPath()).isEqualTo(workspace.getRelative("path")); - - RemoteModule.ScopedCredentialHelper helper2 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "example.com=%workspace%/path"); - assertThat(helper2.getScope().get()).isEqualTo("example.com"); - assertThat(helper2.getPath()).isEqualTo(workspace.getRelative("path")); - - RemoteModule.ScopedCredentialHelper helper3 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "*.example.com=%workspace%/path"); - assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); - assertThat(helper3.getPath()).isEqualTo(workspace.getRelative("path")); - } - - // PATH lookup. - { - Path helper = createExecutable(pathValue.getRelative("foo")); - - RemoteModule.ScopedCredentialHelper helper1 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "foo"); - assertThat(helper1.getScope()).isEmpty(); - assertThat(helper1.getPath()).isEqualTo(pathValue.getRelative("foo")); - - RemoteModule.ScopedCredentialHelper helper2 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "example.com=foo"); - assertThat(helper2.getScope().get()).isEqualTo("example.com"); - assertThat(helper2.getPath()).isEqualTo(pathValue.getRelative("foo")); - - RemoteModule.ScopedCredentialHelper helper3 = - RemoteModule.parseCredentialHelperFlag( - credentialHelperEnvironment, commandLinePathFactory, "*.example.com=foo"); - assertThat(helper3.getScope().get()).isEqualTo("*.example.com"); - assertThat(helper3.getPath()).isEqualTo(pathValue.getRelative("foo")); - } - } - @Test public void newCredentialHelperProvider() throws Exception { FileSystem fileSystem = new InMemoryFileSystem(DigestHashFunction.SHA256); @@ -701,7 +618,7 @@ public void newCredentialHelperProvider() throws Exception { Path exampleOrgHelper = createExecutable(workspace.getRelative("helpers/example-org")); // No helpers. - CredentialHelperProvider credentialHelperProvider1 = RemoteModule.newCredentialHelperProvider( + CredentialHelperProvider credentialHelperProvider1 = newCredentialHelperProvider( credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of()); @@ -709,7 +626,7 @@ public void newCredentialHelperProvider() throws Exception { assertThat(credentialHelperProvider1.findCredentialHelper(URI.create("https://foo.example.com"))).isEmpty(); // Default helper only. - CredentialHelperProvider credentialHelperProvider2 = RemoteModule.newCredentialHelperProvider( + CredentialHelperProvider credentialHelperProvider2 = newCredentialHelperProvider( credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of(defaultHelper.getPathString())); @@ -717,7 +634,7 @@ public void newCredentialHelperProvider() throws Exception { assertThat(credentialHelperProvider2.findCredentialHelper(URI.create("https://foo.example.com")).get().getPath()).isEqualTo(defaultHelper); // Default and exact match. - CredentialHelperProvider credentialHelperProvider3 = RemoteModule.newCredentialHelperProvider( + CredentialHelperProvider credentialHelperProvider3 = newCredentialHelperProvider( credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of( @@ -727,7 +644,7 @@ public void newCredentialHelperProvider() throws Exception { assertThat(credentialHelperProvider3.findCredentialHelper(URI.create("https://foo.example.com")).get().getPath()).isEqualTo(defaultHelper); // Exact match without default. - CredentialHelperProvider credentialHelperProvider4 = RemoteModule.newCredentialHelperProvider( + CredentialHelperProvider credentialHelperProvider4 = newCredentialHelperProvider( credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of("example.com=" + exampleComHelper.getPathString())); @@ -735,7 +652,7 @@ public void newCredentialHelperProvider() throws Exception { assertThat(credentialHelperProvider4.findCredentialHelper(URI.create("https://foo.example.com"))).isEmpty(); // Multiple scoped helpers with default. - CredentialHelperProvider credentialHelperProvider5 = RemoteModule.newCredentialHelperProvider( + CredentialHelperProvider credentialHelperProvider5 = newCredentialHelperProvider( credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of( @@ -753,7 +670,7 @@ public void newCredentialHelperProvider() throws Exception { assertThat(credentialHelperProvider5.findCredentialHelper(URI.create("https://example.org")).get().getPath()).isEqualTo(exampleOrgHelper); // Helpers override. - CredentialHelperProvider credentialHelperProvider6 = RemoteModule.newCredentialHelperProvider( + CredentialHelperProvider credentialHelperProvider6 = newCredentialHelperProvider( credentialHelperEnvironment, commandLinePathFactory, ImmutableList.of( @@ -801,4 +718,29 @@ private static Path createExecutable(Path path) throws IOException { return path; } + + private static CredentialHelperProvider newCredentialHelperProvider( + CredentialHelperEnvironment credentialHelperEnvironment, + CommandLinePathFactory commandLinePathFactory, + ImmutableList inputs) + throws Exception { + Preconditions.checkNotNull(credentialHelperEnvironment); + Preconditions.checkNotNull(commandLinePathFactory); + Preconditions.checkNotNull(inputs); + + return RemoteModule.newCredentialHelperProvider( + credentialHelperEnvironment, + commandLinePathFactory, + ImmutableList.copyOf(Iterables.transform(inputs, s -> createUnresolvedScopedCredentialHelper(s)))); + } + + private static UnresolvedScopedCredentialHelper createUnresolvedScopedCredentialHelper(String input) { + Preconditions.checkNotNull(input); + + try { + return AuthAndTLSOptions.UnresolvedScopedCredentialHelperConverter.INSTANCE.convert(input); + } catch (OptionsParsingException e) { + throw new IllegalStateException(e); + } + } } \ No newline at end of file