Skip to content
This repository was archived by the owner on Jan 18, 2025. It is now read-only.

Introduce the 'application default credentials' concept. #24

Merged
merged 1 commit into from
Jul 11, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 76 additions & 64 deletions oauth2client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@
# The value representing service account credentials.
SERVICE_ACCOUNT = 'service_account'

# The environment variable pointing the file with local Default Credentials.
GOOGLE_CREDENTIALS_DEFAULT = 'GOOGLE_CREDENTIALS_DEFAULT'
# The environment variable pointing the file with local
# Application Default Credentials.
GOOGLE_APPLICATION_CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS'

# The access token along with the seconds in which it expires.
AccessTokenInfo = namedtuple('AccessTokenInfo', ['access_token', 'expires_in'])
Expand Down Expand Up @@ -111,8 +112,8 @@ class NonAsciiHeaderError(Error):
"""Header names and values must be ASCII strings."""


class DefaultCredentialsError(Error):
"""Error retrieving the Default Credentials."""
class ApplicationDefaultCredentialsError(Error):
"""Error retrieving the Application Default Credentials."""


def _abstract():
Expand Down Expand Up @@ -903,14 +904,15 @@ def _get_environment(urllib2_urlopen=None):


class GoogleCredentials(OAuth2Credentials):
"""Default credentials for use in calling Google APIs.
"""Application Default Credentials for use in calling Google APIs.

The Default Credentials are being constructed as a function of the environment
where the code is being run. More details can be found on this page:
https://developers.google.com/accounts/docs/default-credentials
The Application Default Credentials are being constructed as a function of
the environment where the code is being run.
More details can be found on this page:
https://developers.google.com/accounts/docs/application-default-credentials

Here is an example of how to use the Default Credentials for a service that
requires authentication:
Here is an example of how to use the Application Default Credentials for a
service that requires authentication:

<code>
from googleapiclient.discovery import build
Expand All @@ -919,7 +921,8 @@ class GoogleCredentials(OAuth2Credentials):
PROJECT = 'bamboo-machine-422' # replace this with one of your projects
ZONE = 'us-central1-a' # replace this with the zone you care about

service = build('compute', 'v1', credentials=GoogleCredentials.get_default())
credentials = GoogleCredentials.get_application_default()
service = build('compute', 'v1', credentials=credentials)

request = service.instances().list(project=PROJECT, zone=ZONE)
response = request.execute()
Expand Down Expand Up @@ -949,7 +952,8 @@ def __init__(self, access_token, client_id, client_secret, refresh_token,

This constructor is not usually called by the user, instead
GoogleCredentials objects are instantiated by
GoogleCredentials.from_stream() or GoogleCredentials.get_default().
GoogleCredentials.from_stream() or
GoogleCredentials.get_application_default().

Args:
access_token: string, access token.
Expand Down Expand Up @@ -982,53 +986,53 @@ def create_scoped(self, scopes):
return self

@staticmethod
def get_default():
"""Get the Default Credentials for the current environment.
def get_application_default():
"""Get the Application Default Credentials for the current environment.

Exceptions:
DefaultCredentialsError: raised when the credentials fail to be retrieved.
ApplicationDefaultCredentialsError: raised when the credentials fail
to be retrieved.
"""

_env_name = _get_environment()

if _env_name in ('GAE_PRODUCTION', 'GAE_LOCAL'):
# if we are running inside Google App Engine
# there is no need to look for credentials in local files
default_credential_filename = None
application_default_credential_filename = None
well_known_file = None
else:
default_credential_filename = _get_environment_variable_file()
application_default_credential_filename = _get_environment_variable_file()
well_known_file = _get_well_known_file()

if default_credential_filename:
if application_default_credential_filename:
try:
return _get_default_credential_from_file(default_credential_filename)
except (DefaultCredentialsError, ValueError) as error:
extra_help = (' (pointed to by ' + GOOGLE_CREDENTIALS_DEFAULT +
return _get_application_default_credential_from_file(
application_default_credential_filename)
except (ApplicationDefaultCredentialsError, ValueError) as error:
extra_help = (' (pointed to by ' + GOOGLE_APPLICATION_CREDENTIALS +
' environment variable)')
_raise_exception_for_reading_json(default_credential_filename,
extra_help, error)
_raise_exception_for_reading_json(
application_default_credential_filename, extra_help, error)
elif well_known_file:
try:
return _get_default_credential_from_file(well_known_file)
except (DefaultCredentialsError, ValueError) as error:
return _get_application_default_credential_from_file(well_known_file)
except (ApplicationDefaultCredentialsError, ValueError) as error:
extra_help = (' (produced automatically when running'
' "gcloud auth login" command)')
_raise_exception_for_reading_json(well_known_file, extra_help, error)
elif _env_name in ('GAE_PRODUCTION', 'GAE_LOCAL'):
return _get_default_credential_GAE()
return _get_application_default_credential_GAE()
elif _env_name == 'GCE_PRODUCTION':
return _get_default_credential_GCE()
return _get_application_default_credential_GCE()
else:
raise DefaultCredentialsError(
"The Default Credentials are not available. They are available if "
"running in Google App Engine or Google Compute Engine. They are "
"also available if using the Google Cloud SDK and running 'gcloud "
"auth login'. Otherwise, the environment variable " +
GOOGLE_CREDENTIALS_DEFAULT + " must be defined pointing to a file "
"defining the credentials. "
"See https://developers.google.com/accounts/docs/default-credentials "
"for details.")
raise ApplicationDefaultCredentialsError(
"The Application Default Credentials are not available. They are "
"available if running in Google Compute Engine. Otherwise, the "
" environment variable " + GOOGLE_APPLICATION_CREDENTIALS +
" must be defined pointing to a file defining the credentials. "
"See https://developers.google.com/accounts/docs/application-default-"
"credentials for more information.")

@staticmethod
def from_stream(credential_filename):
Expand All @@ -1041,41 +1045,46 @@ def from_stream(credential_filename):
are to be read

Exceptions:
DefaultCredentialsError: raised when the credentials fail to be retrieved.
ApplicationDefaultCredentialsError: raised when the credentials fail
to be retrieved.
"""

if credential_filename and os.path.isfile(credential_filename):
try:
return _get_default_credential_from_file(credential_filename)
except (DefaultCredentialsError, ValueError) as error:
return _get_application_default_credential_from_file(
credential_filename)
except (ApplicationDefaultCredentialsError, ValueError) as error:
extra_help = ' (provided as parameter to the from_stream() method)'
_raise_exception_for_reading_json(credential_filename,
extra_help,
error)
else:
raise DefaultCredentialsError('The parameter passed to the from_stream()'
' method should point to a file.')
raise ApplicationDefaultCredentialsError(
'The parameter passed to the from_stream() '
'method should point to a file.')


def _get_environment_variable_file():
default_credential_filename = os.environ.get(GOOGLE_CREDENTIALS_DEFAULT,
None)
application_default_credential_filename = (
os.environ.get(GOOGLE_APPLICATION_CREDENTIALS,
None))

if default_credential_filename:
if os.path.isfile(default_credential_filename):
return default_credential_filename
if application_default_credential_filename:
if os.path.isfile(application_default_credential_filename):
return application_default_credential_filename
else:
raise DefaultCredentialsError(
'File ' + default_credential_filename + ' (pointed by ' +
GOOGLE_CREDENTIALS_DEFAULT + ' environment variable) does not exist!')
raise ApplicationDefaultCredentialsError(
'File ' + application_default_credential_filename + ' (pointed by ' +
GOOGLE_APPLICATION_CREDENTIALS +
' environment variable) does not exist!')


def _get_well_known_file():
"""Get the well known file produced by command 'gcloud auth login'."""
# TODO(orestica): Revisit this method once gcloud provides a better way
# of pinpointing the exact location of the file.

WELL_KNOWN_CREDENTIALS_FILE = 'credentials_default.json'
WELL_KNOWN_CREDENTIALS_FILE = 'application_default_credentials.json'
CLOUDSDK_CONFIG_DIRECTORY = 'gcloud'

if os.name == 'nt':
Expand All @@ -1098,14 +1107,17 @@ def _get_well_known_file():
return default_config_path


def _get_default_credential_from_file(default_credential_filename):
"""Build the Default Credentials from file."""
def _get_application_default_credential_from_file(
application_default_credential_filename):
"""Build the Application Default Credentials from file."""

import service_account

# read the credentials from the file
with open(default_credential_filename) as default_credential:
client_credentials = service_account.simplejson.load(default_credential)
with open(application_default_credential_filename) as (
application_default_credential):
client_credentials = service_account.simplejson.load(
application_default_credential)

credentials_type = client_credentials.get('type')
if credentials_type == AUTHORIZED_USER:
Expand All @@ -1114,9 +1126,9 @@ def _get_default_credential_from_file(default_credential_filename):
required_fields = set(['client_id', 'client_email', 'private_key_id',
'private_key'])
else:
raise DefaultCredentialsError("'type' field should be defined "
"(and have one of the '" + AUTHORIZED_USER +
"' or '" + SERVICE_ACCOUNT + "' values)")
raise ApplicationDefaultCredentialsError(
"'type' field should be defined (and have one of the '" +
AUTHORIZED_USER + "' or '" + SERVICE_ACCOUNT + "' values)")

missing_fields = required_fields.difference(client_credentials.keys())

Expand All @@ -1142,25 +1154,25 @@ def _get_default_credential_from_file(default_credential_filename):


def _raise_exception_for_missing_fields(missing_fields):
raise DefaultCredentialsError('The following field(s): ' +
', '.join(missing_fields) + ' must be defined.')
raise ApplicationDefaultCredentialsError(
'The following field(s) must be defined: ' + ', '.join(missing_fields))


def _raise_exception_for_reading_json(credential_file,
extra_help,
error):
raise DefaultCredentialsError('An error was encountered while reading '
'json file: '+ credential_file + extra_help +
': ' + str(error))
raise ApplicationDefaultCredentialsError(
'An error was encountered while reading json file: '+
credential_file + extra_help + ': ' + str(error))


def _get_default_credential_GAE():
def _get_application_default_credential_GAE():
from oauth2client.appengine import AppAssertionCredentials

return AppAssertionCredentials([])


def _get_default_credential_GCE():
def _get_application_default_credential_GCE():
from oauth2client.gce import AppAssertionCredentials

return AppAssertionCredentials([])
Expand Down
9 changes: 5 additions & 4 deletions samples/call_compute_service.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# To be used to test GoogleCredential.GetDefaultCredential()
# To be used to test GoogleCredentials.get_application_default()
# from local machine and GCE.

from googleapiclient.discovery import build
from oauth2client.client import GoogleCredentials

PROJECT = "bamboo-machine-422" # Provide your own GCE project here
ZONE = "us-central1-a" # Put here a zone which has some VMs
PROJECT = 'bamboo-machine-422' # Provide your own GCE project here
ZONE = 'us-central1-a' # Put here a zone which has some VMs

service = build("compute", "v1", credentials=GoogleCredentials.get_default())
credentials = GoogleCredentials.get_application_default()
service = build('compute', 'v1', credentials=credentials)

request = service.instances().list(project=PROJECT, zone=ZONE)
response = request.execute()
Expand Down
9 changes: 5 additions & 4 deletions samples/googleappengine/call_compute_service_from_gae.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# To be used to test GoogleCredential.GetDefaultCredential()
# To be used to test GoogleCredentials.get_application_default()
# from devel GAE (ie, dev_appserver.py).

import webapp2
from googleapiclient.discovery import build
from oauth2client.client import GoogleCredentials

PROJECT = "bamboo-machine-422" # Provide your own GCE project here
ZONE = "us-central1-a" # Put here a zone which has some VMs
PROJECT = 'bamboo-machine-422' # Provide your own GCE project here
ZONE = 'us-central1-a' # Put here a zone which has some VMs

def get_instances():
service = build("compute", "v1", credentials=GoogleCredentials.get_default())
credentials = GoogleCredentials.get_application_default()
service = build('compute', 'v1', credentials=credentials)
request = service.instances().list(project=PROJECT, zone=ZONE)
return request.execute()

Expand Down
Loading