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 os 
    18import subprocess 
    19 
    20from google.auth import _helpers 
    21from google.auth import environment_vars 
    22from google.auth import exceptions 
    23 
    24 
    25# The ~/.config subdirectory containing gcloud credentials. 
    26_CONFIG_DIRECTORY = "gcloud" 
    27# Windows systems store config at %APPDATA%\gcloud 
    28_WINDOWS_CONFIG_ROOT_ENV_VAR = "APPDATA" 
    29# The name of the file in the Cloud SDK config that contains default 
    30# credentials. 
    31_CREDENTIALS_FILENAME = "application_default_credentials.json" 
    32# The name of the Cloud SDK shell script 
    33_CLOUD_SDK_POSIX_COMMAND = "gcloud" 
    34_CLOUD_SDK_WINDOWS_COMMAND = "gcloud.cmd" 
    35# The command to get the Cloud SDK configuration 
    36_CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND = ("config", "get", "project") 
    37# The command to get google user access token 
    38_CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND = ("auth", "print-access-token") 
    39# Cloud SDK's application-default client ID 
    40CLOUD_SDK_CLIENT_ID = ( 
    41    "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com" 
    42) 
    43 
    44 
    45def get_config_path(): 
    46    """Returns the absolute path the the Cloud SDK's configuration directory. 
    47 
    48    Returns: 
    49        str: The Cloud SDK config path. 
    50    """ 
    51    # If the path is explicitly set, return that. 
    52    try: 
    53        return os.environ[environment_vars.CLOUD_SDK_CONFIG_DIR] 
    54    except KeyError: 
    55        pass 
    56 
    57    # Non-windows systems store this at ~/.config/gcloud 
    58    if os.name != "nt": 
    59        return os.path.join(os.path.expanduser("~"), ".config", _CONFIG_DIRECTORY) 
    60    # Windows systems store config at %APPDATA%\gcloud 
    61    else: 
    62        try: 
    63            return os.path.join( 
    64                os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], _CONFIG_DIRECTORY 
    65            ) 
    66        except KeyError: 
    67            # This should never happen unless someone is really 
    68            # messing with things, but we'll cover the case anyway. 
    69            drive = os.environ.get("SystemDrive", "C:") 
    70            return os.path.join(drive, "\\", _CONFIG_DIRECTORY) 
    71 
    72 
    73def get_application_default_credentials_path(): 
    74    """Gets the path to the application default credentials file. 
    75 
    76    The path may or may not exist. 
    77 
    78    Returns: 
    79        str: The full path to application default credentials. 
    80    """ 
    81    config_path = get_config_path() 
    82    return os.path.join(config_path, _CREDENTIALS_FILENAME) 
    83 
    84 
    85def _run_subprocess_ignore_stderr(command): 
    86    """ Return subprocess.check_output with the given command and ignores stderr.""" 
    87    with open(os.devnull, "w") as devnull: 
    88        output = subprocess.check_output(command, stderr=devnull) 
    89    return output 
    90 
    91 
    92def get_project_id(): 
    93    """Gets the project ID from the Cloud SDK. 
    94 
    95    Returns: 
    96        Optional[str]: The project ID. 
    97    """ 
    98    if os.name == "nt": 
    99        command = _CLOUD_SDK_WINDOWS_COMMAND 
    100    else: 
    101        command = _CLOUD_SDK_POSIX_COMMAND 
    102 
    103    try: 
    104        # Ignore the stderr coming from gcloud, so it won't be mixed into the output. 
    105        # https://github.com/googleapis/google-auth-library-python/issues/673 
    106        project = _run_subprocess_ignore_stderr( 
    107            (command,) + _CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND 
    108        ) 
    109 
    110        # Turn bytes into a string and remove "\n" 
    111        project = _helpers.from_bytes(project).strip() 
    112        return project if project else None 
    113    except (subprocess.CalledProcessError, OSError, IOError): 
    114        return None 
    115 
    116 
    117def get_auth_access_token(account=None): 
    118    """Load user access token with the ``gcloud auth print-access-token`` command. 
    119 
    120    Args: 
    121        account (Optional[str]): Account to get the access token for. If not 
    122            specified, the current active account will be used. 
    123 
    124    Returns: 
    125        str: The user access token. 
    126 
    127    Raises: 
    128        google.auth.exceptions.UserAccessTokenError: if failed to get access 
    129            token from gcloud. 
    130    """ 
    131    if os.name == "nt": 
    132        command = _CLOUD_SDK_WINDOWS_COMMAND 
    133    else: 
    134        command = _CLOUD_SDK_POSIX_COMMAND 
    135 
    136    try: 
    137        if account: 
    138            command = ( 
    139                (command,) 
    140                + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND 
    141                + ("--account=" + account,) 
    142            ) 
    143        else: 
    144            command = (command,) + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND 
    145 
    146        access_token = subprocess.check_output(command, stderr=subprocess.STDOUT) 
    147        # remove the trailing "\n" 
    148        return access_token.decode("utf-8").strip() 
    149    except (subprocess.CalledProcessError, OSError, IOError) as caught_exc: 
    150        new_exc = exceptions.UserAccessTokenError( 
    151            "Failed to obtain access token", caught_exc 
    152        ) 
    153        raise new_exc from caught_exc