diff options
author | Danny Hermes <daniel.j.hermes@gmail.com> | 2016-02-19 14:15:23 -0800 |
---|---|---|
committer | Danny Hermes <daniel.j.hermes@gmail.com> | 2016-02-22 15:37:43 -0800 |
commit | ce0d71a497c1db7c4d5de36565df57eaec017eae (patch) | |
tree | 06f96b9a960b1fd2fcd2d0542820ce3581c3252d /oauth2client | |
parent | c66e4f201f5ed72514d18a1227be5d34efa2421a (diff) | |
download | oauth2client-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.py | 12 | ||||
-rw-r--r-- | oauth2client/contrib/appengine.py | 29 | ||||
-rw-r--r-- | oauth2client/contrib/gce.py | 73 | ||||
-rw-r--r-- | oauth2client/service_account.py | 17 |
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 |