com.rt.storage.auth.oauth2.DefaultCredentialsProvider Maven / Gradle / Ivy
package com.rt.storage.auth.oauth2;
import com.rt.storage.auth.http.HttpTransportFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessControlException;
import java.util.Collections;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Provides the Application Default Credential from the environment.
*
* An instance represents the per-process state used to get and cache the credential and allows
* overriding the state and environment for testing purposes.
*/
class DefaultCredentialsProvider {
static final DefaultCredentialsProvider DEFAULT = new DefaultCredentialsProvider();
static final String CREDENTIAL_ENV_VAR = "GOOGLE_APPLICATION_CREDENTIALS";
static final String WELL_KNOWN_CREDENTIALS_FILE = "application_default_credentials.json";
static final String CLOUDSDK_CONFIG_DIRECTORY = "gcloud";
static final String HELP_PERMALINK =
"https://developers.google.com/accounts/docs/application-default-credentials";
static final String APP_ENGINE_SIGNAL_CLASS = "com.google.appengine.api.utils.SystemProperty";
static final String CLOUD_SHELL_ENV_VAR = "DEVSHELL_CLIENT_PORT";
static final String SKIP_APP_ENGINE_ENV_VAR = "GOOGLE_APPLICATION_CREDENTIALS_SKIP_APP_ENGINE";
static final String SPECIFICATION_VERSION = System.getProperty("java.specification.version");
static final String GAE_RUNTIME_VERSION =
System.getProperty("com.google.appengine.runtime.version");
static final String RUNTIME_JETTY_LOGGER = System.getProperty("org.eclipse.jetty.util.log.class");
static final Logger LOGGER = Logger.getLogger(DefaultCredentialsProvider.class.getName());
static final String NO_GCE_CHECK_ENV_VAR = "NO_GCE_CHECK";
static final String GCE_METADATA_HOST_ENV_VAR = "GCE_METADATA_HOST";
static final String CLOUDSDK_CLIENT_ID =
"764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com";
static final String CLOUDSDK_CREDENTIALS_WARNING =
"Your application has authenticated using end user credentials from Google "
+ "Cloud SDK. We recommend that most server applications use service accounts "
+ "instead. If your application continues to use end user credentials from Cloud "
+ "SDK, you might receive a \"quota exceeded\" or \"API not enabled\" error. For "
+ "more information about service accounts, see "
+ "https://cloud.google.com/docs/authentication/.";
public static final String SUPPRESS_GCLOUD_CREDS_WARNING_ENV_VAR =
"SUPPRESS_GCLOUD_CREDS_WARNING";
// These variables should only be accessed inside a synchronized block
private RtStorageCredentials cachedCredentials = null;
private boolean checkedAppEngine = false;
private boolean checkedComputeEngine = false;
DefaultCredentialsProvider() {}
/**
* Returns the Application Default Credentials.
*
*
Returns the Application Default Credentials which are used to identify and authorize the
* whole application. The following are searched (in order) to find the Application Default
* Credentials:
*
*
* @param transportFactory HTTP transport factory, creates the transport used to get access
* tokens.
* @return the credentials instance.
* @throws IOException if the credentials cannot be created in the current environment.
*/
final RtStorageCredentials getDefaultCredentials(HttpTransportFactory transportFactory)
throws IOException {
synchronized (this) {
if (cachedCredentials == null) {
cachedCredentials = getDefaultCredentialsUnsynchronized(transportFactory);
}
if (cachedCredentials != null) {
return cachedCredentials;
}
}
throw new IOException(
String.format(
"The Application Default Credentials are not available. They are available if running"
+ " in Google Compute Engine. Otherwise, the environment variable %s must be defined"
+ " pointing to a file defining the credentials. See %s for more information.",
CREDENTIAL_ENV_VAR, HELP_PERMALINK));
}
private final RtStorageCredentials getDefaultCredentialsUnsynchronized(
HttpTransportFactory transportFactory) throws IOException {
// First try the environment variable
RtStorageCredentials credentials = null;
String credentialsPath = getEnv(CREDENTIAL_ENV_VAR);
if (credentialsPath != null && credentialsPath.length() > 0) {
InputStream credentialsStream = null;
try {
File credentialsFile = new File(credentialsPath);
if (!isFile(credentialsFile)) {
// Path will be put in the message from the catch block below
throw new IOException("File does not exist.");
}
credentialsStream = readStream(credentialsFile);
credentials = RtStorageCredentials.fromStream(credentialsStream, transportFactory);
} catch (IOException e) {
// Although it is also the cause, the message of the caught exception can have very
// important information for diagnosing errors, so include its message in the
// outer exception message also.
throw new IOException(
String.format(
"Error reading credential file from environment variable %s, value '%s': %s",
CREDENTIAL_ENV_VAR, credentialsPath, e.getMessage()),
e);
} catch (AccessControlException expected) {
// Exception querying file system is expected on App-Engine
} finally {
if (credentialsStream != null) {
credentialsStream.close();
}
}
}
// Then try the well-known file
if (credentials == null) {
File wellKnownFileLocation = getWellKnownCredentialsFile();
InputStream credentialsStream = null;
try {
if (isFile(wellKnownFileLocation)) {
credentialsStream = readStream(wellKnownFileLocation);
credentials = RtStorageCredentials.fromStream(credentialsStream, transportFactory);
}
} catch (IOException e) {
throw new IOException(
String.format(
"Error reading credential file from location %s: %s",
wellKnownFileLocation, e.getMessage()));
} catch (AccessControlException expected) {
// Exception querying file system is expected on App-Engine
} finally {
if (credentialsStream != null) {
credentialsStream.close();
}
}
warnAboutProblematicCredentials(credentials);
}
return credentials;
}
private void warnAboutProblematicCredentials(RtStorageCredentials credentials) {
if (credentials instanceof UserCredentials
&& ((UserCredentials) credentials).getClientId().equals(CLOUDSDK_CLIENT_ID)
&& !Boolean.parseBoolean(getEnv(SUPPRESS_GCLOUD_CREDS_WARNING_ENV_VAR))) {
LOGGER.log(Level.WARNING, CLOUDSDK_CREDENTIALS_WARNING);
}
}
private final File getWellKnownCredentialsFile() {
File cloudConfigPath;
String os = getProperty("os.name", "").toLowerCase(Locale.US);
String envPath = getEnv("CLOUDSDK_CONFIG");
if (envPath != null) {
cloudConfigPath = new File(envPath);
} else if (os.indexOf("windows") >= 0) {
File appDataPath = new File(getEnv("APPDATA"));
cloudConfigPath = new File(appDataPath, CLOUDSDK_CONFIG_DIRECTORY);
} else {
File configPath = new File(getProperty("user.home", ""), ".config");
cloudConfigPath = new File(configPath, CLOUDSDK_CONFIG_DIRECTORY);
}
return new File(cloudConfigPath, WELL_KNOWN_CREDENTIALS_FILE);
}
private boolean runningOnAppEngine() {
Class> systemPropertyClass = null;
try {
systemPropertyClass = forName(APP_ENGINE_SIGNAL_CLASS);
} catch (ClassNotFoundException expected) {
// SystemProperty will always be present on App Engine.
return false;
}
Exception cause;
Field environmentField;
try {
environmentField = systemPropertyClass.getField("environment");
Object environmentValue = environmentField.get(null);
Class> environmentType = environmentField.getType();
Method valueMethod = environmentType.getMethod("value");
Object environmentValueValue = valueMethod.invoke(environmentValue);
return (environmentValueValue != null);
} catch (NoSuchFieldException
| SecurityException
| IllegalArgumentException
| IllegalAccessException
| NoSuchMethodException
| InvocationTargetException exception) {
cause = exception;
}
throw new RuntimeException(
String.format(
"Unexpected error trying to determine if runnning on Google App Engine: %s",
cause.getMessage()),
cause);
}
// Skip app engine check if environment variable
private boolean skipAppEngineCredentialsCheck() {
boolean skip = false; // do not skip by default
String value = getEnv(SKIP_APP_ENGINE_ENV_VAR);
if (value != null) {
skip = value.equalsIgnoreCase("true") || value.equals("1");
}
return skip;
}
protected boolean isOnGAEStandard7() {
return GAE_RUNTIME_VERSION != null
&& (SPECIFICATION_VERSION.equals("1.7") || RUNTIME_JETTY_LOGGER == null);
}
/*
* Start of methods to allow overriding in the test code to isolate from the environment.
*/
Class> forName(String className) throws ClassNotFoundException {
return Class.forName(className);
}
String getEnv(String name) {
return System.getenv(name);
}
String getProperty(String property, String def) {
return System.getProperty(property, def);
}
boolean isFile(File file) {
return file.isFile();
}
InputStream readStream(File file) throws FileNotFoundException {
return new FileInputStream(file);
}
/*
* End of methods to allow overriding in the test code to isolate from the environment.
*/
}