aboutsummaryrefslogtreecommitdiff
path: root/oauth2client
diff options
context:
space:
mode:
authorDanny Hermes <daniel.j.hermes@gmail.com>2016-02-19 14:15:23 -0800
committerDanny Hermes <daniel.j.hermes@gmail.com>2016-02-22 15:37:43 -0800
commitce0d71a497c1db7c4d5de36565df57eaec017eae (patch)
tree06f96b9a960b1fd2fcd2d0542820ce3581c3252d /oauth2client
parentc66e4f201f5ed72514d18a1227be5d34efa2421a (diff)
downloadoauth2client-ce0d71a497c1db7c4d5de36565df57eaec017eae.tar.gz
Adding common sign_blob() service account types.
Also adding service_account_email() property.
Diffstat (limited to 'oauth2client')
-rw-r--r--oauth2client/client.py12
-rw-r--r--oauth2client/contrib/appengine.py29
-rw-r--r--oauth2client/contrib/gce.py73
-rw-r--r--oauth2client/service_account.py17
4 files changed, 129 insertions, 2 deletions
diff --git a/oauth2client/client.py b/oauth2client/client.py
index fd0d699..a388fb8 100644
--- a/oauth2client/client.py
+++ b/oauth2client/client.py
@@ -1617,6 +1617,18 @@ class AssertionCredentials(GoogleCredentials):
"""
self._do_revoke(http_request, self.access_token)
+ def sign_blob(self, blob):
+ """Cryptographically sign a blob (of bytes).
+
+ Args:
+ blob: bytes, Message to be signed.
+
+ Returns:
+ tuple, A pair of the private key ID used to sign the blob and
+ the signed contents.
+ """
+ raise NotImplementedError('This method is abstract.')
+
def _RequireCryptoOrDie():
"""Ensure we have a crypto library, or throw CryptoUnavailableError.
diff --git a/oauth2client/contrib/appengine.py b/oauth2client/contrib/appengine.py
index a56cb74..2f05254 100644
--- a/oauth2client/contrib/appengine.py
+++ b/oauth2client/contrib/appengine.py
@@ -166,6 +166,7 @@ class AppAssertionCredentials(AssertionCredentials):
self.scope = util.scopes_to_string(scope)
self._kwargs = kwargs
self.service_account_id = kwargs.get('service_account_id', None)
+ self._service_account_email = None
# Assertion type is no longer used, but still in the
# parent class signature.
@@ -210,6 +211,34 @@ class AppAssertionCredentials(AssertionCredentials):
def create_scoped(self, scopes):
return AppAssertionCredentials(scopes, **self._kwargs)
+ def sign_blob(self, blob):
+ """Cryptographically sign a blob (of bytes).
+
+ Implements abstract method
+ :meth:`oauth2client.client.AssertionCredentials.sign_blob`.
+
+ Args:
+ blob: bytes, Message to be signed.
+
+ Returns:
+ tuple, A pair of the private key ID used to sign the blob and
+ the signed contents.
+ """
+ return app_identity.sign_blob(blob)
+
+ @property
+ def service_account_email(self):
+ """Get the email for the current service account.
+
+ Returns:
+ string, The email associated with the Google App Engine
+ service account.
+ """
+ if self._service_account_email is None:
+ self._service_account_email = (
+ app_identity.get_service_account_name())
+ return self._service_account_email
+
class FlowProperty(db.Property):
"""App Engine datastore Property for Flow.
diff --git a/oauth2client/contrib/gce.py b/oauth2client/contrib/gce.py
index 53a7b1c..6542008 100644
--- a/oauth2client/contrib/gce.py
+++ b/oauth2client/contrib/gce.py
@@ -21,6 +21,7 @@ import json
import logging
import warnings
+import httplib2
from six.moves import http_client
from six.moves import urllib
@@ -35,8 +36,10 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
logger = logging.getLogger(__name__)
# URI Template for the endpoint that returns access_tokens.
-META = ('http://metadata.google.internal/computeMetadata/v1/instance/'
- 'service-accounts/default/token')
+_METADATA_ROOT = ('http://metadata.google.internal/computeMetadata/v1/'
+ 'instance/service-accounts/default/')
+META = _METADATA_ROOT + 'token'
+_DEFAULT_EMAIL_METADATA = _METADATA_ROOT + 'email'
_SCOPES_WARNING = """\
You have requested explicit scopes to be used with a GCE service account.
Using this argument will have no effect on the actual scopes for tokens
@@ -45,6 +48,30 @@ can't be overridden in the request.
"""
+def _get_service_account_email(http_request=None):
+ """Get the GCE service account email from the current environment.
+
+ Args:
+ http_request: callable, (Optional) a callable that matches the method
+ signature of httplib2.Http.request, used to make
+ the request to the metadata service.
+
+ Returns:
+ tuple, A pair where the first entry is an optional response (from a
+ failed request) and the second is service account email found (as
+ a string).
+ """
+ if http_request is None:
+ http_request = httplib2.Http().request
+ response, content = http_request(
+ _DEFAULT_EMAIL_METADATA, headers={'Metadata-Flavor': 'Google'})
+ if response.status == http_client.OK:
+ content = _from_bytes(content)
+ return None, content
+ else:
+ return response, content
+
+
class AppAssertionCredentials(AssertionCredentials):
"""Credentials object for Compute Engine Assertion Grants
@@ -78,6 +105,7 @@ class AppAssertionCredentials(AssertionCredentials):
# Assertion type is no longer used, but still in the
# parent class signature.
super(AppAssertionCredentials, self).__init__(None)
+ self._service_account_email = None
@classmethod
def from_json(cls, json_data):
@@ -123,3 +151,44 @@ class AppAssertionCredentials(AssertionCredentials):
def create_scoped(self, scopes):
return AppAssertionCredentials(scopes, **self.kwargs)
+
+ def sign_blob(self, blob):
+ """Cryptographically sign a blob (of bytes).
+
+ This method is provided to support a common interface, but
+ the actual key used for a Google Compute Engine service account
+ is not available, so it can't be used to sign content.
+
+ Args:
+ blob: bytes, Message to be signed.
+
+ Raises:
+ NotImplementedError, always.
+ """
+ raise NotImplementedError(
+ 'Compute Engine service accounts cannot sign blobs')
+
+ @property
+ def service_account_email(self):
+ """Get the email for the current service account.
+
+ Uses the Google Compute Engine metadata service to retrieve the email
+ of the default service account.
+
+ Returns:
+ string, The email associated with the Google Compute Engine
+ service account.
+
+ Raises:
+ AttributeError, if the email can not be retrieved from the Google
+ Compute Engine metadata service.
+ """
+ if self._service_account_email is None:
+ failure, email = _get_service_account_email()
+ if failure is None:
+ self._service_account_email = email
+ else:
+ raise AttributeError('Failed to retrieve the email from the '
+ 'Google Compute Engine metadata service',
+ failure, email)
+ return self._service_account_email
diff --git a/oauth2client/service_account.py b/oauth2client/service_account.py
index b4d1dc8..f009b0c 100644
--- a/oauth2client/service_account.py
+++ b/oauth2client/service_account.py
@@ -320,10 +320,27 @@ class ServiceAccountCredentials(AssertionCredentials):
key_id=self._private_key_id)
def sign_blob(self, blob):
+ """Cryptographically sign a blob (of bytes).
+
+ Implements abstract method
+ :meth:`oauth2client.client.AssertionCredentials.sign_blob`.
+
+ Args:
+ blob: bytes, Message to be signed.
+
+ Returns:
+ tuple, A pair of the private key ID used to sign the blob and
+ the signed contents.
+ """
return self._private_key_id, self._signer.sign(blob)
@property
def service_account_email(self):
+ """Get the email for the current service account.
+
+ Returns:
+ string, The email associated with the service account.
+ """
return self._service_account_email
@property