OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # Copyright 2015 Google Inc. |
| 4 # |
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 # you may not use this file except in compliance with the License. |
| 7 # You may obtain a copy of the License at |
| 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # |
| 11 # Unless required by applicable law or agreed to in writing, software |
| 12 # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 # See the License for the specific language governing permissions and |
| 15 # limitations under the License. |
| 16 |
| 17 """Common credentials classes and constructors.""" |
| 18 from __future__ import print_function |
| 19 |
| 20 import datetime |
| 21 import json |
| 22 import os |
| 23 import threading |
| 24 |
| 25 import httplib2 |
| 26 import oauth2client |
| 27 import oauth2client.client |
| 28 import oauth2client.gce |
| 29 import oauth2client.locked_file |
| 30 import oauth2client.multistore_file |
| 31 import oauth2client.service_account |
| 32 from oauth2client import tools # for gflags declarations |
| 33 from six.moves import http_client |
| 34 from six.moves import urllib |
| 35 |
| 36 from apitools.base.py import exceptions |
| 37 from apitools.base.py import util |
| 38 |
| 39 try: |
| 40 # pylint: disable=wrong-import-order |
| 41 import gflags |
| 42 FLAGS = gflags.FLAGS |
| 43 except ImportError: |
| 44 FLAGS = None |
| 45 |
| 46 |
| 47 __all__ = [ |
| 48 'CredentialsFromFile', |
| 49 'GaeAssertionCredentials', |
| 50 'GceAssertionCredentials', |
| 51 'GetCredentials', |
| 52 'GetUserinfo', |
| 53 'ServiceAccountCredentials', |
| 54 'ServiceAccountCredentialsFromFile', |
| 55 ] |
| 56 |
| 57 |
| 58 # Lock when accessing the cache file to avoid resource contention. |
| 59 cache_file_lock = threading.Lock() |
| 60 |
| 61 |
| 62 def SetCredentialsCacheFileLock(lock): |
| 63 global cache_file_lock # pylint: disable=global-statement |
| 64 cache_file_lock = lock |
| 65 |
| 66 |
| 67 # List of additional methods we use when attempting to construct |
| 68 # credentials. Users can register their own methods here, which we try |
| 69 # before the defaults. |
| 70 _CREDENTIALS_METHODS = [] |
| 71 |
| 72 |
| 73 def _RegisterCredentialsMethod(method, position=None): |
| 74 """Register a new method for fetching credentials. |
| 75 |
| 76 This new method should be a function with signature: |
| 77 client_info, **kwds -> Credentials or None |
| 78 This method can be used as a decorator, unless position needs to |
| 79 be supplied. |
| 80 |
| 81 Note that method must *always* accept arbitrary keyword arguments. |
| 82 |
| 83 Args: |
| 84 method: New credential-fetching method. |
| 85 position: (default: None) Where in the list of methods to |
| 86 add this; if None, we append. In all but rare cases, |
| 87 this should be either 0 or None. |
| 88 Returns: |
| 89 method, for use as a decorator. |
| 90 |
| 91 """ |
| 92 if position is None: |
| 93 position = len(_CREDENTIALS_METHODS) |
| 94 else: |
| 95 position = min(position, len(_CREDENTIALS_METHODS)) |
| 96 _CREDENTIALS_METHODS.insert(position, method) |
| 97 return method |
| 98 |
| 99 |
| 100 def GetCredentials(package_name, scopes, client_id, client_secret, user_agent, |
| 101 credentials_filename=None, |
| 102 api_key=None, # pylint: disable=unused-argument |
| 103 client=None, # pylint: disable=unused-argument |
| 104 oauth2client_args=None, |
| 105 **kwds): |
| 106 """Attempt to get credentials, using an oauth dance as the last resort.""" |
| 107 scopes = util.NormalizeScopes(scopes) |
| 108 client_info = { |
| 109 'client_id': client_id, |
| 110 'client_secret': client_secret, |
| 111 'scope': ' '.join(sorted(scopes)), |
| 112 'user_agent': user_agent or '%s-generated/0.1' % package_name, |
| 113 } |
| 114 for method in _CREDENTIALS_METHODS: |
| 115 credentials = method(client_info, **kwds) |
| 116 if credentials is not None: |
| 117 return credentials |
| 118 credentials_filename = credentials_filename or os.path.expanduser( |
| 119 '~/.apitools.token') |
| 120 credentials = CredentialsFromFile(credentials_filename, client_info, |
| 121 oauth2client_args=oauth2client_args) |
| 122 if credentials is not None: |
| 123 return credentials |
| 124 raise exceptions.CredentialsError('Could not create valid credentials') |
| 125 |
| 126 |
| 127 def ServiceAccountCredentialsFromFile( |
| 128 service_account_name, private_key_filename, scopes, |
| 129 service_account_kwargs=None): |
| 130 with open(private_key_filename) as key_file: |
| 131 return ServiceAccountCredentials( |
| 132 service_account_name, key_file.read(), scopes, |
| 133 service_account_kwargs=service_account_kwargs) |
| 134 |
| 135 |
| 136 def ServiceAccountCredentials(service_account_name, private_key, scopes, |
| 137 service_account_kwargs=None): |
| 138 service_account_kwargs = service_account_kwargs or {} |
| 139 scopes = util.NormalizeScopes(scopes) |
| 140 return oauth2client.client.SignedJwtAssertionCredentials( |
| 141 service_account_name, private_key, scopes, **service_account_kwargs) |
| 142 |
| 143 |
| 144 def _EnsureFileExists(filename): |
| 145 """Touches a file; returns False on error, True on success.""" |
| 146 if not os.path.exists(filename): |
| 147 old_umask = os.umask(0o177) |
| 148 try: |
| 149 open(filename, 'a+b').close() |
| 150 except OSError: |
| 151 return False |
| 152 finally: |
| 153 os.umask(old_umask) |
| 154 return True |
| 155 |
| 156 |
| 157 def _GceMetadataRequest(relative_url, use_metadata_ip=False): |
| 158 """Request the given url from the GCE metadata service.""" |
| 159 if use_metadata_ip: |
| 160 base_url = 'http://169.254.169.254/' |
| 161 else: |
| 162 base_url = 'http://metadata.google.internal/' |
| 163 url = base_url + 'computeMetadata/v1/' + relative_url |
| 164 # Extra header requirement can be found here: |
| 165 # https://developers.google.com/compute/docs/metadata |
| 166 headers = {'Metadata-Flavor': 'Google'} |
| 167 request = urllib.request.Request(url, headers=headers) |
| 168 opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) |
| 169 try: |
| 170 response = opener.open(request) |
| 171 except urllib.error.URLError as e: |
| 172 raise exceptions.CommunicationError( |
| 173 'Could not reach metadata service: %s' % e.reason) |
| 174 return response |
| 175 |
| 176 |
| 177 class GceAssertionCredentials(oauth2client.gce.AppAssertionCredentials): |
| 178 |
| 179 """Assertion credentials for GCE instances.""" |
| 180 |
| 181 def __init__(self, scopes=None, service_account_name='default', **kwds): |
| 182 """Initializes the credentials instance. |
| 183 |
| 184 Args: |
| 185 scopes: The scopes to get. If None, whatever scopes that are |
| 186 available to the instance are used. |
| 187 service_account_name: The service account to retrieve the scopes |
| 188 from. |
| 189 **kwds: Additional keyword args. |
| 190 |
| 191 """ |
| 192 # If there is a connectivity issue with the metadata server, |
| 193 # detection calls may fail even if we've already successfully |
| 194 # identified these scopes in the same execution. However, the |
| 195 # available scopes don't change once an instance is created, |
| 196 # so there is no reason to perform more than one query. |
| 197 self.__service_account_name = service_account_name |
| 198 cached_scopes = None |
| 199 cache_filename = kwds.get('cache_filename') |
| 200 if cache_filename: |
| 201 cached_scopes = self._CheckCacheFileForMatch( |
| 202 cache_filename, scopes) |
| 203 |
| 204 scopes = cached_scopes or self._ScopesFromMetadataServer(scopes) |
| 205 |
| 206 if cache_filename and not cached_scopes: |
| 207 self._WriteCacheFile(cache_filename, scopes) |
| 208 |
| 209 super(GceAssertionCredentials, self).__init__(scopes, **kwds) |
| 210 |
| 211 @classmethod |
| 212 def Get(cls, *args, **kwds): |
| 213 try: |
| 214 return cls(*args, **kwds) |
| 215 except exceptions.Error: |
| 216 return None |
| 217 |
| 218 def _CheckCacheFileForMatch(self, cache_filename, scopes): |
| 219 """Checks the cache file to see if it matches the given credentials. |
| 220 |
| 221 Args: |
| 222 cache_filename: Cache filename to check. |
| 223 scopes: Scopes for the desired credentials. |
| 224 |
| 225 Returns: |
| 226 List of scopes (if cache matches) or None. |
| 227 """ |
| 228 creds = { # Credentials metadata dict. |
| 229 'scopes': sorted(list(scopes)) if scopes else None, |
| 230 'svc_acct_name': self.__service_account_name, |
| 231 } |
| 232 with cache_file_lock: |
| 233 if _EnsureFileExists(cache_filename): |
| 234 locked_file = oauth2client.locked_file.LockedFile( |
| 235 cache_filename, 'r+b', 'rb') |
| 236 try: |
| 237 locked_file.open_and_lock() |
| 238 cached_creds_str = locked_file.file_handle().read() |
| 239 if cached_creds_str: |
| 240 # Cached credentials metadata dict. |
| 241 cached_creds = json.loads(cached_creds_str) |
| 242 if (creds['svc_acct_name'] == |
| 243 cached_creds['svc_acct_name']): |
| 244 if (creds['scopes'] in |
| 245 (None, cached_creds['scopes'])): |
| 246 scopes = cached_creds['scopes'] |
| 247 finally: |
| 248 locked_file.unlock_and_close() |
| 249 return scopes |
| 250 |
| 251 def _WriteCacheFile(self, cache_filename, scopes): |
| 252 """Writes the credential metadata to the cache file. |
| 253 |
| 254 This does not save the credentials themselves (CredentialStore class |
| 255 optionally handles that after this class is initialized). |
| 256 |
| 257 Args: |
| 258 cache_filename: Cache filename to check. |
| 259 scopes: Scopes for the desired credentials. |
| 260 """ |
| 261 with cache_file_lock: |
| 262 if _EnsureFileExists(cache_filename): |
| 263 locked_file = oauth2client.locked_file.LockedFile( |
| 264 cache_filename, 'r+b', 'rb') |
| 265 try: |
| 266 locked_file.open_and_lock() |
| 267 if locked_file.is_locked(): |
| 268 creds = { # Credentials metadata dict. |
| 269 'scopes': sorted(list(scopes)), |
| 270 'svc_acct_name': self.__service_account_name} |
| 271 locked_file.file_handle().write( |
| 272 json.dumps(creds, encoding='ascii')) |
| 273 # If it's not locked, the locking process will |
| 274 # write the same data to the file, so just |
| 275 # continue. |
| 276 finally: |
| 277 locked_file.unlock_and_close() |
| 278 |
| 279 def _ScopesFromMetadataServer(self, scopes): |
| 280 if not util.DetectGce(): |
| 281 raise exceptions.ResourceUnavailableError( |
| 282 'GCE credentials requested outside a GCE instance') |
| 283 if not self.GetServiceAccount(self.__service_account_name): |
| 284 raise exceptions.ResourceUnavailableError( |
| 285 'GCE credentials requested but service account ' |
| 286 '%s does not exist.' % self.__service_account_name) |
| 287 if scopes: |
| 288 scope_ls = util.NormalizeScopes(scopes) |
| 289 instance_scopes = self.GetInstanceScopes() |
| 290 if scope_ls > instance_scopes: |
| 291 raise exceptions.CredentialsError( |
| 292 'Instance did not have access to scopes %s' % ( |
| 293 sorted(list(scope_ls - instance_scopes)),)) |
| 294 else: |
| 295 scopes = self.GetInstanceScopes() |
| 296 return scopes |
| 297 |
| 298 def GetServiceAccount(self, account): |
| 299 relative_url = 'instance/service-accounts' |
| 300 response = _GceMetadataRequest(relative_url) |
| 301 response_lines = [line.rstrip('/\n\r') |
| 302 for line in response.readlines()] |
| 303 return account in response_lines |
| 304 |
| 305 def GetInstanceScopes(self): |
| 306 relative_url = 'instance/service-accounts/{0}/scopes'.format( |
| 307 self.__service_account_name) |
| 308 response = _GceMetadataRequest(relative_url) |
| 309 return util.NormalizeScopes(scope.strip() |
| 310 for scope in response.readlines()) |
| 311 |
| 312 def _refresh(self, do_request): |
| 313 """Refresh self.access_token. |
| 314 |
| 315 This function replaces AppAssertionCredentials._refresh, which |
| 316 does not use the credential store and is therefore poorly |
| 317 suited for multi-threaded scenarios. |
| 318 |
| 319 Args: |
| 320 do_request: A function matching httplib2.Http.request's signature. |
| 321 |
| 322 """ |
| 323 # pylint: disable=protected-access |
| 324 oauth2client.client.OAuth2Credentials._refresh(self, do_request) |
| 325 # pylint: enable=protected-access |
| 326 |
| 327 def _do_refresh_request(self, unused_http_request): |
| 328 """Refresh self.access_token by querying the metadata server. |
| 329 |
| 330 If self.store is initialized, store acquired credentials there. |
| 331 """ |
| 332 relative_url = 'instance/service-accounts/{0}/token'.format( |
| 333 self.__service_account_name) |
| 334 try: |
| 335 response = _GceMetadataRequest(relative_url) |
| 336 except exceptions.CommunicationError: |
| 337 self.invalid = True |
| 338 if self.store: |
| 339 self.store.locked_put(self) |
| 340 raise |
| 341 content = response.read() |
| 342 try: |
| 343 credential_info = json.loads(content) |
| 344 except ValueError: |
| 345 raise exceptions.CredentialsError( |
| 346 'Could not parse response as JSON: %s' % content) |
| 347 |
| 348 self.access_token = credential_info['access_token'] |
| 349 if 'expires_in' in credential_info: |
| 350 expires_in = int(credential_info['expires_in']) |
| 351 self.token_expiry = ( |
| 352 datetime.timedelta(seconds=expires_in) + |
| 353 datetime.datetime.utcnow()) |
| 354 else: |
| 355 self.token_expiry = None |
| 356 self.invalid = False |
| 357 if self.store: |
| 358 self.store.locked_put(self) |
| 359 |
| 360 @classmethod |
| 361 def from_json(cls, json_data): |
| 362 data = json.loads(json_data) |
| 363 kwargs = {} |
| 364 if 'cache_filename' in data.get('kwargs', []): |
| 365 kwargs['cache_filename'] = data['kwargs']['cache_filename'] |
| 366 credentials = GceAssertionCredentials(scopes=[data['scope']], |
| 367 **kwargs) |
| 368 if 'access_token' in data: |
| 369 credentials.access_token = data['access_token'] |
| 370 if 'token_expiry' in data: |
| 371 credentials.token_expiry = datetime.datetime.strptime( |
| 372 data['token_expiry'], oauth2client.client.EXPIRY_FORMAT) |
| 373 if 'invalid' in data: |
| 374 credentials.invalid = data['invalid'] |
| 375 return credentials |
| 376 |
| 377 @property |
| 378 def serialization_data(self): |
| 379 raise NotImplementedError( |
| 380 'Cannot serialize credentials for GCE service accounts.') |
| 381 |
| 382 |
| 383 # TODO(craigcitro): Currently, we can't even *load* |
| 384 # `oauth2client.appengine` without being on appengine, because of how |
| 385 # it handles imports. Fix that by splitting that module into |
| 386 # GAE-specific and GAE-independent bits, and guarding imports. |
| 387 class GaeAssertionCredentials(oauth2client.client.AssertionCredentials): |
| 388 |
| 389 """Assertion credentials for Google App Engine apps.""" |
| 390 |
| 391 def __init__(self, scopes, **kwds): |
| 392 if not util.DetectGae(): |
| 393 raise exceptions.ResourceUnavailableError( |
| 394 'GCE credentials requested outside a GCE instance') |
| 395 self._scopes = list(util.NormalizeScopes(scopes)) |
| 396 super(GaeAssertionCredentials, self).__init__(None, **kwds) |
| 397 |
| 398 @classmethod |
| 399 def Get(cls, *args, **kwds): |
| 400 try: |
| 401 return cls(*args, **kwds) |
| 402 except exceptions.Error: |
| 403 return None |
| 404 |
| 405 @classmethod |
| 406 def from_json(cls, json_data): |
| 407 data = json.loads(json_data) |
| 408 return GaeAssertionCredentials(data['_scopes']) |
| 409 |
| 410 def _refresh(self, _): |
| 411 """Refresh self.access_token. |
| 412 |
| 413 Args: |
| 414 _: (ignored) A function matching httplib2.Http.request's signature. |
| 415 """ |
| 416 # pylint: disable=import-error |
| 417 from google.appengine.api import app_identity |
| 418 try: |
| 419 token, _ = app_identity.get_access_token(self._scopes) |
| 420 except app_identity.Error as e: |
| 421 raise exceptions.CredentialsError(str(e)) |
| 422 self.access_token = token |
| 423 |
| 424 |
| 425 def _GetRunFlowFlags(args=None): |
| 426 # There's one rare situation where gsutil will not have argparse |
| 427 # available, but doesn't need anything depending on argparse anyway, |
| 428 # since they're bringing their own credentials. So we just allow this |
| 429 # to fail with an ImportError in those cases. |
| 430 # |
| 431 # TODO(craigcitro): Move this import back to the top when we drop |
| 432 # python 2.6 support (eg when gsutil does). |
| 433 import argparse |
| 434 |
| 435 parser = argparse.ArgumentParser(parents=[tools.argparser]) |
| 436 # Get command line argparse flags. |
| 437 flags, _ = parser.parse_known_args(args=args) |
| 438 |
| 439 # Allow `gflags` and `argparse` to be used side-by-side. |
| 440 if hasattr(FLAGS, 'auth_host_name'): |
| 441 flags.auth_host_name = FLAGS.auth_host_name |
| 442 if hasattr(FLAGS, 'auth_host_port'): |
| 443 flags.auth_host_port = FLAGS.auth_host_port |
| 444 if hasattr(FLAGS, 'auth_local_webserver'): |
| 445 flags.noauth_local_webserver = (not FLAGS.auth_local_webserver) |
| 446 return flags |
| 447 |
| 448 |
| 449 # TODO(craigcitro): Switch this from taking a path to taking a stream. |
| 450 def CredentialsFromFile(path, client_info, oauth2client_args=None): |
| 451 """Read credentials from a file.""" |
| 452 credential_store = oauth2client.multistore_file.get_credential_storage( |
| 453 path, |
| 454 client_info['client_id'], |
| 455 client_info['user_agent'], |
| 456 client_info['scope']) |
| 457 if hasattr(FLAGS, 'auth_local_webserver'): |
| 458 FLAGS.auth_local_webserver = False |
| 459 credentials = credential_store.get() |
| 460 if credentials is None or credentials.invalid: |
| 461 print('Generating new OAuth credentials ...') |
| 462 for _ in range(20): |
| 463 # If authorization fails, we want to retry, rather than let this |
| 464 # cascade up and get caught elsewhere. If users want out of the |
| 465 # retry loop, they can ^C. |
| 466 try: |
| 467 flow = oauth2client.client.OAuth2WebServerFlow(**client_info) |
| 468 flags = _GetRunFlowFlags(args=oauth2client_args) |
| 469 credentials = tools.run_flow(flow, credential_store, flags) |
| 470 break |
| 471 except (oauth2client.client.FlowExchangeError, SystemExit) as e: |
| 472 # Here SystemExit is "no credential at all", and the |
| 473 # FlowExchangeError is "invalid" -- usually because |
| 474 # you reused a token. |
| 475 print('Invalid authorization: %s' % (e,)) |
| 476 except httplib2.HttpLib2Error as e: |
| 477 print('Communication error: %s' % (e,)) |
| 478 raise exceptions.CredentialsError( |
| 479 'Communication error creating credentials: %s' % e) |
| 480 return credentials |
| 481 |
| 482 |
| 483 # TODO(craigcitro): Push this into oauth2client. |
| 484 def GetUserinfo(credentials, http=None): # pylint: disable=invalid-name |
| 485 """Get the userinfo associated with the given credentials. |
| 486 |
| 487 This is dependent on the token having either the userinfo.email or |
| 488 userinfo.profile scope for the given token. |
| 489 |
| 490 Args: |
| 491 credentials: (oauth2client.client.Credentials) incoming credentials |
| 492 http: (httplib2.Http, optional) http instance to use |
| 493 |
| 494 Returns: |
| 495 The email address for this token, or None if the required scopes |
| 496 aren't available. |
| 497 """ |
| 498 http = http or httplib2.Http() |
| 499 url_root = 'https://www.googleapis.com/oauth2/v2/tokeninfo' |
| 500 query_args = {'access_token': credentials.access_token} |
| 501 url = '?'.join((url_root, urllib.parse.urlencode(query_args))) |
| 502 # We ignore communication woes here (i.e. SSL errors, socket |
| 503 # timeout), as handling these should be done in a common location. |
| 504 response, content = http.request(url) |
| 505 if response.status == http_client.BAD_REQUEST: |
| 506 credentials.refresh(http) |
| 507 response, content = http.request(url) |
| 508 return json.loads(content or '{}') # Save ourselves from an empty reply. |
| 509 |
| 510 |
| 511 @_RegisterCredentialsMethod |
| 512 def _GetServiceAccountCredentials( |
| 513 client_info, service_account_name=None, service_account_keyfile=None, |
| 514 service_account_json_keyfile=None, **unused_kwds): |
| 515 if ((service_account_name and not service_account_keyfile) or |
| 516 (service_account_keyfile and not service_account_name)): |
| 517 raise exceptions.CredentialsError( |
| 518 'Service account name or keyfile provided without the other') |
| 519 scopes = client_info['scope'].split() |
| 520 user_agent = client_info['user_agent'] |
| 521 if service_account_json_keyfile: |
| 522 with open(service_account_json_keyfile) as keyfile: |
| 523 service_account_info = json.load(keyfile) |
| 524 account_type = service_account_info.get('type') |
| 525 if account_type != oauth2client.client.SERVICE_ACCOUNT: |
| 526 raise exceptions.CredentialsError( |
| 527 'Invalid service account credentials: %s' % ( |
| 528 service_account_json_keyfile,)) |
| 529 # pylint: disable=protected-access |
| 530 credentials = oauth2client.service_account._ServiceAccountCredentials( |
| 531 service_account_id=service_account_info['client_id'], |
| 532 service_account_email=service_account_info['client_email'], |
| 533 private_key_id=service_account_info['private_key_id'], |
| 534 private_key_pkcs8_text=service_account_info['private_key'], |
| 535 scopes=scopes, user_agent=user_agent) |
| 536 # pylint: enable=protected-access |
| 537 return credentials |
| 538 if service_account_name is not None: |
| 539 # pylint: disable=redefined-variable-type |
| 540 credentials = ServiceAccountCredentialsFromFile( |
| 541 service_account_name, service_account_keyfile, scopes, |
| 542 service_account_kwargs={'user_agent': user_agent}) |
| 543 if credentials is not None: |
| 544 return credentials |
| 545 |
| 546 |
| 547 @_RegisterCredentialsMethod |
| 548 def _GetGaeServiceAccount(client_info, **unused_kwds): |
| 549 scopes = client_info['scope'].split(' ') |
| 550 return GaeAssertionCredentials.Get(scopes=scopes) |
| 551 |
| 552 |
| 553 @_RegisterCredentialsMethod |
| 554 def _GetGceServiceAccount(client_info, **unused_kwds): |
| 555 scopes = client_info['scope'].split(' ') |
| 556 return GceAssertionCredentials.Get(scopes=scopes) |
| 557 |
| 558 |
| 559 @_RegisterCredentialsMethod |
| 560 def _GetApplicationDefaultCredentials( |
| 561 client_info, skip_application_default_credentials=False, |
| 562 **unused_kwds): |
| 563 scopes = client_info['scope'].split() |
| 564 if skip_application_default_credentials: |
| 565 return None |
| 566 gc = oauth2client.client.GoogleCredentials |
| 567 with cache_file_lock: |
| 568 try: |
| 569 # pylint: disable=protected-access |
| 570 # We've already done our own check for GAE/GCE |
| 571 # credentials, we don't want to pay for checking again. |
| 572 credentials = gc._implicit_credentials_from_files() |
| 573 except oauth2client.client.ApplicationDefaultCredentialsError: |
| 574 return None |
| 575 # If we got back a non-service account credential, we need to use |
| 576 # a heuristic to decide whether or not the application default |
| 577 # credential will work for us. We assume that if we're requesting |
| 578 # cloud-platform, our scopes are a subset of cloud scopes, and the |
| 579 # ADC will work. |
| 580 cp = 'https://www.googleapis.com/auth/cloud-platform' |
| 581 if not isinstance(credentials, gc) or cp in scopes: |
| 582 return credentials |
| 583 return None |
OLD | NEW |