Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(757)

Unified Diff: gslib/third_party/storage_apitools/credentials_lib.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gslib/third_party/storage_apitools/base_api.py ('k') | gslib/third_party/storage_apitools/encoding.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gslib/third_party/storage_apitools/credentials_lib.py
===================================================================
--- gslib/third_party/storage_apitools/credentials_lib.py (revision 0)
+++ gslib/third_party/storage_apitools/credentials_lib.py (revision 0)
@@ -0,0 +1,259 @@
+# Copyright 2014 Google Inc. All Rights Reserved.
+#
+# 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.
+"""Common credentials classes and constructors."""
+
+import json
+import os
+import urllib2
+
+
+import httplib2
+import oauth2client.client
+import oauth2client.gce
+import oauth2client.multistore_file
+
+from gslib.third_party.storage_apitools import exceptions
+from gslib.third_party.storage_apitools import util
+
+__all__ = [
+ 'CredentialsFromFile',
+ 'GaeAssertionCredentials',
+ 'GceAssertionCredentials',
+ 'GetCredentials',
+ 'ServiceAccountCredentials',
+ 'ServiceAccountCredentialsFromFile',
+ ]
+
+
+# TODO: Expose the extra args here somewhere higher up,
+# possibly as flags in the generated CLI.
+def GetCredentials(package_name, scopes, client_id, client_secret, user_agent,
+ credentials_filename=None,
+ service_account_name=None, service_account_keyfile=None,
+ api_key=None, client=None):
+ """Attempt to get credentials, using an oauth dance as the last resort."""
+ scopes = util.NormalizeScopes(scopes)
+ # TODO: Error checking.
+ client_info = {
+ 'client_id': client_id,
+ 'client_secret': client_secret,
+ 'scope': ' '.join(sorted(util.NormalizeScopes(scopes))),
+ 'user_agent': user_agent or '%s-generated/0.1' % package_name,
+ }
+ if service_account_name is not None:
+ credentials = ServiceAccountCredentialsFromFile(
+ service_account_name, service_account_keyfile, scopes)
+ if credentials is not None:
+ return credentials
+ credentials = GaeAssertionCredentials.Get(scopes)
+ if credentials is not None:
+ return credentials
+ credentials = GceAssertionCredentials.Get(scopes)
+ if credentials is not None:
+ return credentials
+ credentials_filename = credentials_filename or os.path.expanduser(
+ '~/.apitools.token')
+ credentials = CredentialsFromFile(credentials_filename, client_info)
+ if credentials is not None:
+ return credentials
+ raise exceptions.CredentialsError('Could not create valid credentials')
+
+
+def ServiceAccountCredentialsFromFile(
+ service_account_name, private_key_filename, scopes):
+ with open(private_key_filename) as key_file:
+ return ServiceAccountCredentials(
+ service_account_name, key_file.read(), scopes)
+
+
+def ServiceAccountCredentials(service_account_name, private_key, scopes):
+ scopes = util.NormalizeScopes(scopes)
+ return oauth2client.client.SignedJwtAssertionCredentials(
+ service_account_name, private_key, scopes)
+
+
+# TODO: We override to add some utility code, and to
+# update the old refresh implementation. Either push this code into
+# oauth2client or drop oauth2client.
+class GceAssertionCredentials(oauth2client.gce.AppAssertionCredentials):
+ """Assertion credentials for GCE instances."""
+
+ def __init__(self, scopes=None, service_account_name='default', **kwds):
+ """Initializes the credentials instance.
+
+ Args:
+ scopes: The scopes to get. If None, whatever scopes that are available
+ to the instance are used.
+ service_account_name: The service account to retrieve the scopes from.
+ **kwds: Additional keyword args.
+ """
+ if not util.DetectGce():
+ raise exceptions.ResourceUnavailableError(
+ 'GCE credentials requested outside a GCE instance')
+ if not self.GetServiceAccount(service_account_name):
+ raise exceptions.ResourceUnavailableError(
+ 'GCE credentials requested but service account %s does not exist.' %
+ service_account_name)
+ self.__service_account_name = service_account_name
+ if scopes:
+ scope_ls = util.NormalizeScopes(scopes)
+ instance_scopes = self.GetInstanceScopes()
+ if scope_ls > instance_scopes:
+ raise exceptions.CredentialsError(
+ 'Instance did not have access to scopes %s' % (
+ sorted(list(scope_ls - instance_scopes)),))
+ else:
+ scopes = self.GetInstanceScopes()
+ super(GceAssertionCredentials, self).__init__(scopes, **kwds)
+
+ @classmethod
+ def Get(cls, *args, **kwds):
+ try:
+ return cls(*args, **kwds)
+ except exceptions.Error:
+ return None
+
+ def GetServiceAccount(self, account):
+ account_uri = (
+ 'http://metadata.google.internal/computeMetadata/'
+ 'v1/instance/service-accounts')
+ additional_headers = {'X-Google-Metadata-Request': 'True'}
+ request = urllib2.Request(account_uri, headers=additional_headers)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError as e:
+ raise exceptions.CommunicationError(
+ 'Could not reach metadata service: %s' % e.reason)
+ response_lines = [line.rstrip('/\n\r') for line in response.readlines()]
+ return account in response_lines
+
+ def GetInstanceScopes(self):
+ # Extra header requirement can be found here:
+ # https://developers.google.com/compute/docs/metadata
+ scopes_uri = (
+ 'http://metadata.google.internal/computeMetadata/v1/instance/'
+ 'service-accounts/%s/scopes') % self.__service_account_name
+ additional_headers = {'X-Google-Metadata-Request': 'True'}
+ request = urllib2.Request(scopes_uri, headers=additional_headers)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError as e:
+ raise exceptions.CommunicationError(
+ 'Could not reach metadata service: %s' % e.reason)
+ return util.NormalizeScopes(scope.strip() for scope in response.readlines())
+
+ def _refresh(self, do_request): # pylint: disable=g-bad-name
+ """Refresh self.access_token.
+
+ Args:
+ do_request: A function matching httplib2.Http.request's signature.
+ """
+ token_uri = (
+ 'http://metadata.google.internal/computeMetadata/v1/instance/'
+ 'service-accounts/%s/token') % self.__service_account_name
+ extra_headers = {'X-Google-Metadata-Request': 'True'}
+ request = urllib2.Request(token_uri, headers=extra_headers)
+ try:
+ content = urllib2.urlopen(request).read()
+ except urllib2.URLError as e:
+ raise exceptions.CommunicationError(
+ 'Could not reach metadata service: %s' % e.reason)
+ try:
+ credential_info = json.loads(content)
+ except ValueError:
+ raise exceptions.CredentialsError(
+ 'Invalid credentials response: uri %s' % token_uri)
+
+ self.access_token = credential_info['access_token']
+
+
+# TODO: Currently, we can't even *load*
+# `oauth2client.appengine` without being on appengine, because of how
+# it handles imports. Fix that by splitting that module into
+# GAE-specific and GAE-independent bits, and guarding imports.
+class GaeAssertionCredentials(oauth2client.client.AssertionCredentials):
+ """Assertion credentials for Google App Engine apps."""
+
+ def __init__(self, scopes, **kwds):
+ if not util.DetectGae():
+ raise exceptions.ResourceUnavailableError(
+ 'GCE credentials requested outside a GCE instance')
+ self._scopes = list(util.NormalizeScopes(scopes))
+ super(GaeAssertionCredentials, self).__init__(None, **kwds)
+
+ @classmethod
+ def Get(cls, *args, **kwds):
+ try:
+ return cls(*args, **kwds)
+ except exceptions.Error:
+ return None
+
+ @classmethod
+ def from_json(cls, json_data): # pylint: disable=g-bad-name
+ data = json.loads(json_data)
+ return GaeAssertionCredentials(data['_scopes'])
+
+ def _refresh(self, _): # pylint: disable=g-bad-name
+ """Refresh self.access_token.
+
+ Args:
+ _: (ignored) A function matching httplib2.Http.request's signature.
+ """
+ # pylint: disable=g-import-not-at-top
+ from google.appengine.api import app_identity
+ try:
+ token, _ = app_identity.get_access_token(self._scopes)
+ except app_identity.Error as e:
+ raise exceptions.CredentialsError(str(e))
+ self.access_token = token
+
+
+# TODO: Switch this from taking a path to taking a stream.
+def CredentialsFromFile(path, client_info):
+ """Read credentials from a file."""
+ credential_store = oauth2client.multistore_file.get_credential_storage(
+ path,
+ client_info['client_id'],
+ client_info['user_agent'],
+ client_info['scope'])
+ credentials = credential_store.get()
+ if credentials is None or credentials.invalid:
+ print 'Generating new OAuth credentials ...'
+ while True:
+ # If authorization fails, we want to retry, rather than let this
+ # cascade up and get caught elsewhere. If users want out of the
+ # retry loop, they can ^C.
+ try:
+ flow = oauth2client.client.OAuth2WebServerFlow(**client_info)
+ flow.redirect_uri = oauth2client.client.OOB_CALLBACK_URN
+ authorize_url = flow.step1_get_authorize_url()
+ print 'Go to the following link in your browser:'
+ print
+ print ' ' + authorize_url
+ print
+ code = raw_input('Enter verification code: ').strip()
+ credential = flow.step2_exchange(code)
+ credential_store.put(credential)
+ credential.set_store(credential_store)
+ break
+ except (oauth2client.client.FlowExchangeError, SystemExit) as e:
+ # Here SystemExit is "no credential at all", and the
+ # FlowExchangeError is "invalid" -- usually because you reused
+ # a token.
+ print 'Invalid authorization: %s' % (e,)
+ except httplib2.HttpLib2Error as e:
+ print 'Communication error: %s' % (e,)
+ raise exceptions.CredentialsError(
+ 'Communication error creating credentials: %s' % e)
+ return credentials
Property changes on: gslib/third_party/storage_apitools/credentials_lib.py
___________________________________________________________________
Added: svn:eol-style
+ LF
« no previous file with comments | « gslib/third_party/storage_apitools/base_api.py ('k') | gslib/third_party/storage_apitools/encoding.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698