1# Copyright 2015 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Helpers for reading the Google Cloud SDK's configuration.""" 16 17import json 18import os 19import subprocess 20 21import six 22 23from google.auth import environment_vars 24from google.auth import exceptions 25 26 27# The ~/.config subdirectory containing gcloud credentials. 28_CONFIG_DIRECTORY = "gcloud" 29# Windows systems store config at %APPDATA%\gcloud 30_WINDOWS_CONFIG_ROOT_ENV_VAR = "APPDATA" 31# The name of the file in the Cloud SDK config that contains default 32# credentials. 33_CREDENTIALS_FILENAME = "application_default_credentials.json" 34# The name of the Cloud SDK shell script 35_CLOUD_SDK_POSIX_COMMAND = "gcloud" 36_CLOUD_SDK_WINDOWS_COMMAND = "gcloud.cmd" 37# The command to get the Cloud SDK configuration 38_CLOUD_SDK_CONFIG_COMMAND = ("config", "config-helper", "--format", "json") 39# The command to get google user access token 40_CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND = ("auth", "print-access-token") 41# Cloud SDK's application-default client ID 42CLOUD_SDK_CLIENT_ID = ( 43 "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com" 44) 45 46 47def get_config_path(): 48 """Returns the absolute path the the Cloud SDK's configuration directory. 49 50 Returns: 51 str: The Cloud SDK config path. 52 """ 53 # If the path is explicitly set, return that. 54 try: 55 return os.environ[environment_vars.CLOUD_SDK_CONFIG_DIR] 56 except KeyError: 57 pass 58 59 # Non-windows systems store this at ~/.config/gcloud 60 if os.name != "nt": 61 return os.path.join(os.path.expanduser("~"), ".config", _CONFIG_DIRECTORY) 62 # Windows systems store config at %APPDATA%\gcloud 63 else: 64 try: 65 return os.path.join( 66 os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], _CONFIG_DIRECTORY 67 ) 68 except KeyError: 69 # This should never happen unless someone is really 70 # messing with things, but we'll cover the case anyway. 71 drive = os.environ.get("SystemDrive", "C:") 72 return os.path.join(drive, "\\", _CONFIG_DIRECTORY) 73 74 75def get_application_default_credentials_path(): 76 """Gets the path to the application default credentials file. 77 78 The path may or may not exist. 79 80 Returns: 81 str: The full path to application default credentials. 82 """ 83 config_path = get_config_path() 84 return os.path.join(config_path, _CREDENTIALS_FILENAME) 85 86 87def _run_subprocess_ignore_stderr(command): 88 """ Return subprocess.check_output with the given command and ignores stderr.""" 89 with open(os.devnull, "w") as devnull: 90 output = subprocess.check_output(command, stderr=devnull) 91 return output 92 93 94def get_project_id(): 95 """Gets the project ID from the Cloud SDK. 96 97 Returns: 98 Optional[str]: The project ID. 99 """ 100 if os.name == "nt": 101 command = _CLOUD_SDK_WINDOWS_COMMAND 102 else: 103 command = _CLOUD_SDK_POSIX_COMMAND 104 105 try: 106 # Ignore the stderr coming from gcloud, so it won't be mixed into the output. 107 # https://github.com/googleapis/google-auth-library-python/issues/673 108 output = _run_subprocess_ignore_stderr((command,) + _CLOUD_SDK_CONFIG_COMMAND) 109 except (subprocess.CalledProcessError, OSError, IOError): 110 return None 111 112 try: 113 configuration = json.loads(output.decode("utf-8")) 114 except ValueError: 115 return None 116 117 try: 118 return configuration["configuration"]["properties"]["core"]["project"] 119 except KeyError: 120 return None 121 122 123def get_auth_access_token(account=None): 124 """Load user access token with the ``gcloud auth print-access-token`` command. 125 126 Args: 127 account (Optional[str]): Account to get the access token for. If not 128 specified, the current active account will be used. 129 130 Returns: 131 str: The user access token. 132 133 Raises: 134 google.auth.exceptions.UserAccessTokenError: if failed to get access 135 token from gcloud. 136 """ 137 if os.name == "nt": 138 command = _CLOUD_SDK_WINDOWS_COMMAND 139 else: 140 command = _CLOUD_SDK_POSIX_COMMAND 141 142 try: 143 if account: 144 command = ( 145 (command,) 146 + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND 147 + ("--account=" + account,) 148 ) 149 else: 150 command = (command,) + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND 151 152 access_token = subprocess.check_output(command, stderr=subprocess.STDOUT) 153 # remove the trailing "\n" 154 return access_token.decode("utf-8").strip() 155 except (subprocess.CalledProcessError, OSError, IOError) as caught_exc: 156 new_exc = exceptions.UserAccessTokenError( 157 "Failed to obtain access token", caught_exc 158 ) 159 six.raise_from(new_exc, caught_exc) 160