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 8641d1c1f8c23e..f648038182e026 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
@@ -139,6 +139,44 @@ public class AuthAndTLSOptions extends OptionsBase {
+ "pings are disabled, then this setting is ignored.")
public Duration grpcKeepaliveTimeout;
+ @Option(
+ name = "experimental_credential_helper",
+ defaultValue = "null",
+ allowMultiple = true,
+ converter = UnresolvedScopedCredentialHelperConverter.class,
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Configures Credential Helpers to use for retrieving credentials for the provided scope"
+ + " (domain).\n\n"
+ + "Credentials from Credential Helpers take precedence over credentials from"
+ + " --google_default_credentials
, `--google_credentials`, or"
+ + " .netrc
.\n\n"
+ + "See https://github.com/bazelbuild/proposals/blob/main/designs/2022-06-07-bazel-credential-helpers.md"
+ + " for details.")
+ public List credentialHelpers;
+
+ @Option(
+ name = "experimental_credential_helper_timeout",
+ defaultValue = "5s",
+ converter = DurationConverter.class,
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Configures the timeout for the Credential Helper.\n\n"
+ + "Credential Helpers failing to respond within this timeout will fail the"
+ + " invocation.")
+ public Duration credentialHelperTimeout;
+
+ @Option(
+ name = "experimental_credential_helper_cache_duration",
+ defaultValue = "30m",
+ converter = DurationConverter.class,
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Configures the duration for which credentials from Credential Helpers are cached.")
+ public Duration credentialHelperCacheTimeout;
+
/** One of the values of the `--credential_helper` flag. */
@AutoValue
public abstract static class UnresolvedScopedCredentialHelper {
diff --git a/src/main/java/com/google/devtools/build/lib/authandtls/GoogleAuthUtils.java b/src/main/java/com/google/devtools/build/lib/authandtls/GoogleAuthUtils.java
index 3d3ecac5bac70c..93c80f5396576c 100644
--- a/src/main/java/com/google/devtools/build/lib/authandtls/GoogleAuthUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/authandtls/GoogleAuthUtils.java
@@ -19,10 +19,10 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
+import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperCredentials;
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.Event;
-import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.runtime.CommandLinePathFactory;
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.Path;
@@ -222,36 +222,49 @@ public static CallCredentialsProvider newCallCredentialsProvider(@Nullable Crede
}
/**
- * Create a new {@link Credentials} with following order:
+ * Create a new {@link Credentials} retrieving call credentials in the following order:
*
*
- * - If authentication enabled by flags, use it to create credentials
- *
- Use .netrc to provide credentials if exists
- *
- Otherwise, return {@code null}
+ *
- If a Credential Helper is configured for the scope, use the credentials provided by the
+ * helper.
+ *
- If (Google) authentication is enabled by flags, use it to create credentials.
+ *
- Use {@code .netrc} to provide credentials if exists.
*
*
* @throws IOException in case the credentials can't be constructed.
*/
- @Nullable
public static Credentials newCredentials(
- Reporter reporter,
- Map clientEnv,
+ CredentialHelperEnvironment credentialHelperEnvironment,
+ CommandLinePathFactory commandLinePathFactory,
FileSystem fileSystem,
AuthAndTLSOptions authAndTlsOptions)
throws IOException {
+ Preconditions.checkNotNull(credentialHelperEnvironment);
+ Preconditions.checkNotNull(commandLinePathFactory);
+ Preconditions.checkNotNull(fileSystem);
+ Preconditions.checkNotNull(authAndTlsOptions);
+
Optional credentials = newGoogleCredentials(authAndTlsOptions);
if (credentials.isEmpty()) {
// Fallback to .netrc if it exists.
try {
- credentials = newCredentialsFromNetrc(clientEnv, fileSystem);
+ credentials =
+ newCredentialsFromNetrc(credentialHelperEnvironment.getClientEnvironment(), fileSystem);
} catch (IOException e) {
// TODO(yannic): Make this fail the build.
- reporter.handle(Event.warn(e.getMessage()));
+ credentialHelperEnvironment.getEventReporter().handle(Event.warn(e.getMessage()));
}
}
- return credentials.orElse(null);
+ return new CredentialHelperCredentials(
+ newCredentialHelperProvider(
+ credentialHelperEnvironment,
+ commandLinePathFactory,
+ authAndTlsOptions.credentialHelpers),
+ credentialHelperEnvironment,
+ credentials,
+ authAndTlsOptions.credentialHelperCacheTimeout);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/BUILD b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/BUILD
index 8b11bce467a160..20500030d42356 100644
--- a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/BUILD
@@ -17,9 +17,12 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/events",
"//src/main/java/com/google/devtools/build/lib/shell",
"//src/main/java/com/google/devtools/build/lib/vfs",
+ "//third_party:auth",
"//third_party:auto_value",
+ "//third_party:caffeine",
"//third_party:error_prone_annotations",
"//third_party:gson",
"//third_party:guava",
+ "//third_party:jsr305",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperCredentials.java b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperCredentials.java
new file mode 100644
index 00000000000000..ecc40e15707d8d
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/CredentialHelperCredentials.java
@@ -0,0 +1,141 @@
+// 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.credentialhelper;
+
+import com.github.benmanes.caffeine.cache.CacheLoader;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import com.google.auth.Credentials;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.net.URI;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.Nullable;
+
+/**
+ * Implementation of {@link Credentials} which fetches credentials by invoking a {@code credential
+ * helper} as subprocess, falling back to another {@link Credentials} if no suitable helper exists.
+ */
+public class CredentialHelperCredentials extends Credentials {
+ private final Optional fallbackCredentials;
+
+ private final LoadingCache credentialCache;
+
+ public CredentialHelperCredentials(
+ CredentialHelperProvider credentialHelperProvider,
+ CredentialHelperEnvironment credentialHelperEnvironment,
+ Optional fallbackCredentials,
+ Duration cacheTimeout) {
+ Preconditions.checkNotNull(credentialHelperProvider);
+ Preconditions.checkNotNull(credentialHelperEnvironment);
+ this.fallbackCredentials = Preconditions.checkNotNull(fallbackCredentials);
+ Preconditions.checkNotNull(cacheTimeout);
+ Preconditions.checkArgument(
+ !cacheTimeout.isNegative() && !cacheTimeout.isZero(),
+ "Cache timeout must be greater than 0");
+
+ credentialCache =
+ Caffeine.newBuilder()
+ .expireAfterWrite(cacheTimeout)
+ .build(
+ new CredentialHelperCacheLoader(
+ credentialHelperProvider, credentialHelperEnvironment));
+ }
+
+ @Override
+ public String getAuthenticationType() {
+ if (fallbackCredentials.isPresent()) {
+ return "credential-helper-with-fallback-" + fallbackCredentials.get().getAuthenticationType();
+ }
+
+ return "credential-helper";
+ }
+
+ @Override
+ public Map> getRequestMetadata(URI uri) throws IOException {
+ Preconditions.checkNotNull(uri);
+
+ Optional