Skip to content

Commit

Permalink
Implement application default credentials (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Wayne Parrott authored Oct 19, 2016
1 parent 714fee0 commit aadb3de
Show file tree
Hide file tree
Showing 10 changed files with 863 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/reference/google.auth.environment_vars.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
google.auth.environment_vars module
===================================

.. automodule:: google.auth.environment_vars
:members:
:inherited-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/reference/google.auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Submodules

google.auth.credentials
google.auth.crypt
google.auth.environment_vars
google.auth.exceptions
google.auth.jwt

7 changes: 7 additions & 0 deletions google/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

import logging

from google.auth._default import default


__all__ = [
'default',
]


# Set default logging handler to avoid "No handler found" warnings.
logging.getLogger(__name__).addHandler(logging.NullHandler())
135 changes: 135 additions & 0 deletions google/auth/_cloud_sdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Copyright 2015 Google Inc.
#
# 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.

"""Helpers for reading the Google Cloud SDK's configuration."""

import os

import six
from six.moves import configparser

from google.auth import environment_vars
import google.oauth2.credentials

# The Google OAuth 2.0 token endpoint. Used for authorized user credentials.
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = 'https://accounts.google.com/o/oauth2/token'

# The ~/.config subdirectory containing gcloud credentials.
_CONFIG_DIRECTORY = 'gcloud'
# Windows systems store config at %APPDATA%\gcloud
_WINDOWS_CONFIG_ROOT_ENV_VAR = 'APPDATA'
# The name of the file in the Cloud SDK config that contains default
# credentials.
_CREDENTIALS_FILENAME = 'application_default_credentials.json'
# The name of the file in the Cloud SDK config that contains the
# active configuration.
_ACTIVE_CONFIG_FILENAME = os.path.join(
'configurations', 'config_default')
# The config section and key for the project ID in the cloud SDK config.
_PROJECT_CONFIG_SECTION = 'core'
_PROJECT_CONFIG_KEY = 'project'


def get_config_path():
"""Returns the absolute path the the Cloud SDK's configuration directory.
Returns:
str: The Cloud SDK config path.
"""
# If the path is explicitly set, return that.
try:
return os.environ[environment_vars.CLOUD_SDK_CONFIG_DIR]
except KeyError:
pass

# Non-windows systems store this at ~/.config/gcloud
if os.name != 'nt':
return os.path.join(
os.path.expanduser('~'), '.config', _CONFIG_DIRECTORY)
# Windows systems store config at %APPDATA%\gcloud
else:
try:
return os.path.join(
os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR],
_CONFIG_DIRECTORY)
except KeyError:
# This should never happen unless someone is really
# messing with things, but we'll cover the case anyway.
drive = os.environ.get('SystemDrive', 'C:')
return os.path.join(
drive, '\\', _CONFIG_DIRECTORY)


def get_application_default_credentials_path():
"""Gets the path to the application default credentials file.
The path may or may not exist.
Returns:
str: The full path to application default credentials.
"""
config_path = get_config_path()
return os.path.join(config_path, _CREDENTIALS_FILENAME)


def get_project_id():
"""Gets the project ID from the Cloud SDK's configuration.
Returns:
Optional[str]: The project ID.
"""
config_path = get_config_path()
config_file = os.path.join(config_path, _ACTIVE_CONFIG_FILENAME)

if not os.path.isfile(config_file):
return None

config = configparser.RawConfigParser()

try:
config.read(config_file)
except configparser.Error:
return None

if config.has_section(_PROJECT_CONFIG_SECTION):
return config.get(
_PROJECT_CONFIG_SECTION, _PROJECT_CONFIG_KEY)


def load_authorized_user_credentials(info):
"""Loads an authorized user credential.
Args:
info (Mapping[str, str]): The loaded file's data.
Returns:
google.oauth2.credentials.Credentials: The constructed credentials.
Raises:
ValueError: if the info is in the wrong format or missing data.
"""
keys_needed = set(('refresh_token', 'client_id', 'client_secret'))
missing = keys_needed.difference(six.iterkeys(info))

if missing:
raise ValueError(
'Authorized user info was not in the expected format, missing '
'fields {}.'.format(', '.join(missing)))

return google.oauth2.credentials.Credentials(
None, # No access token, must be refreshed.
refresh_token=info['refresh_token'],
token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT,
client_id=info['client_id'],
client_secret=info['client_secret'])
Loading

0 comments on commit aadb3de

Please sign in to comment.