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

Side by Side Diff: client/third_party/oauth2client/client.py

Issue 1768993002: Update oauth2client to v2.0.1 and googleapiclient to v1.5.0. Base URL: git@github.com:luci/luci-py.git@master
Patch Set: . Created 4 years, 9 months 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 unified diff | Download patch
OLDNEW
1 # Copyright 2014 Google Inc. All rights reserved. 1 # Copyright 2014 Google Inc. All rights reserved.
2 # 2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); 3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License. 4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at 5 # You may obtain a copy of the License at
6 # 6 #
7 # http://www.apache.org/licenses/LICENSE-2.0 7 # http://www.apache.org/licenses/LICENSE-2.0
8 # 8 #
9 # Unless required by applicable law or agreed to in writing, software 9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, 10 # distributed under the License is distributed on an "AS IS" BASIS,
(...skipping 12 matching lines...) Expand all
23 import datetime 23 import datetime
24 import json 24 import json
25 import logging 25 import logging
26 import os 26 import os
27 import socket 27 import socket
28 import sys 28 import sys
29 import tempfile 29 import tempfile
30 import time 30 import time
31 import shutil 31 import shutil
32 import six 32 import six
33 from six.moves import http_client
33 from six.moves import urllib 34 from six.moves import urllib
34 35
35 import httplib2 36 import httplib2
36 from oauth2client import GOOGLE_AUTH_URI 37 from oauth2client import GOOGLE_AUTH_URI
37 from oauth2client import GOOGLE_DEVICE_URI 38 from oauth2client import GOOGLE_DEVICE_URI
38 from oauth2client import GOOGLE_REVOKE_URI 39 from oauth2client import GOOGLE_REVOKE_URI
39 from oauth2client import GOOGLE_TOKEN_URI 40 from oauth2client import GOOGLE_TOKEN_URI
40 from oauth2client import GOOGLE_TOKEN_INFO_URI 41 from oauth2client import GOOGLE_TOKEN_INFO_URI
41 from oauth2client._helpers import _from_bytes 42 from oauth2client._helpers import _from_bytes
42 from oauth2client._helpers import _to_bytes 43 from oauth2client._helpers import _to_bytes
(...skipping 23 matching lines...) Expand all
66 # Which certs to use to validate id_tokens received. 67 # Which certs to use to validate id_tokens received.
67 ID_TOKEN_VERIFICATION_CERTS = 'https://www.googleapis.com/oauth2/v1/certs' 68 ID_TOKEN_VERIFICATION_CERTS = 'https://www.googleapis.com/oauth2/v1/certs'
68 # This symbol previously had a typo in the name; we keep the old name 69 # This symbol previously had a typo in the name; we keep the old name
69 # around for now, but will remove it in the future. 70 # around for now, but will remove it in the future.
70 ID_TOKEN_VERIFICATON_CERTS = ID_TOKEN_VERIFICATION_CERTS 71 ID_TOKEN_VERIFICATON_CERTS = ID_TOKEN_VERIFICATION_CERTS
71 72
72 # Constant to use for the out of band OAuth 2.0 flow. 73 # Constant to use for the out of band OAuth 2.0 flow.
73 OOB_CALLBACK_URN = 'urn:ietf:wg:oauth:2.0:oob' 74 OOB_CALLBACK_URN = 'urn:ietf:wg:oauth:2.0:oob'
74 75
75 # Google Data client libraries may need to set this to [401, 403]. 76 # Google Data client libraries may need to set this to [401, 403].
76 REFRESH_STATUS_CODES = [401] 77 REFRESH_STATUS_CODES = (http_client.UNAUTHORIZED,)
77 78
78 # The value representing user credentials. 79 # The value representing user credentials.
79 AUTHORIZED_USER = 'authorized_user' 80 AUTHORIZED_USER = 'authorized_user'
80 81
81 # The value representing service account credentials. 82 # The value representing service account credentials.
82 SERVICE_ACCOUNT = 'service_account' 83 SERVICE_ACCOUNT = 'service_account'
83 84
84 # The environment variable pointing the file with local 85 # The environment variable pointing the file with local
85 # Application Default Credentials. 86 # Application Default Credentials.
86 GOOGLE_APPLICATION_CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS' 87 GOOGLE_APPLICATION_CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS'
87 # The ~/.config subdirectory containing gcloud credentials. Intended 88 # The ~/.config subdirectory containing gcloud credentials. Intended
88 # to be swapped out in tests. 89 # to be swapped out in tests.
89 _CLOUDSDK_CONFIG_DIRECTORY = 'gcloud' 90 _CLOUDSDK_CONFIG_DIRECTORY = 'gcloud'
90 # The environment variable name which can replace ~/.config if set. 91 # The environment variable name which can replace ~/.config if set.
91 _CLOUDSDK_CONFIG_ENV_VAR = 'CLOUDSDK_CONFIG' 92 _CLOUDSDK_CONFIG_ENV_VAR = 'CLOUDSDK_CONFIG'
92 93
93 # The error message we show users when we can't find the Application 94 # The error message we show users when we can't find the Application
94 # Default Credentials. 95 # Default Credentials.
95 ADC_HELP_MSG = ( 96 ADC_HELP_MSG = (
96 'The Application Default Credentials are not available. They are ' 97 'The Application Default Credentials are not available. They are '
97 'available if running in Google Compute Engine. Otherwise, the ' 98 'available if running in Google Compute Engine. Otherwise, the '
98 'environment variable ' + 99 'environment variable ' +
99 GOOGLE_APPLICATION_CREDENTIALS + 100 GOOGLE_APPLICATION_CREDENTIALS +
100 ' must be defined pointing to a file defining the credentials. See ' 101 ' must be defined pointing to a file defining the credentials. See '
101 'https://developers.google.com/accounts/docs/' 102 'https://developers.google.com/accounts/docs/'
102 'application-default-credentials for more information.') 103 'application-default-credentials for more information.')
103 104
105 _WELL_KNOWN_CREDENTIALS_FILE = 'application_default_credentials.json'
106
104 # The access token along with the seconds in which it expires. 107 # The access token along with the seconds in which it expires.
105 AccessTokenInfo = collections.namedtuple( 108 AccessTokenInfo = collections.namedtuple(
106 'AccessTokenInfo', ['access_token', 'expires_in']) 109 'AccessTokenInfo', ['access_token', 'expires_in'])
107 110
108 DEFAULT_ENV_NAME = 'UNKNOWN' 111 DEFAULT_ENV_NAME = 'UNKNOWN'
109 112
110 # If set to True _get_environment avoid GCE check (_detect_gce_environment) 113 # If set to True _get_environment avoid GCE check (_detect_gce_environment)
111 NO_GCE_CHECK = os.environ.setdefault('NO_GCE_CHECK', 'False') 114 NO_GCE_CHECK = os.environ.setdefault('NO_GCE_CHECK', 'False')
112 115
113 _SERVER_SOFTWARE = 'SERVER_SOFTWARE' 116 _SERVER_SOFTWARE = 'SERVER_SOFTWARE'
114 _GCE_METADATA_HOST = '169.254.169.254' 117 _GCE_METADATA_HOST = '169.254.169.254'
115 _METADATA_FLAVOR_HEADER = 'Metadata-Flavor' 118 _METADATA_FLAVOR_HEADER = 'Metadata-Flavor'
116 _DESIRED_METADATA_FLAVOR = 'Google' 119 _DESIRED_METADATA_FLAVOR = 'Google'
117 120
121 # Expose utcnow() at module level to allow for
122 # easier testing (by replacing with a stub).
123 _UTCNOW = datetime.datetime.utcnow
124
118 125
119 class SETTINGS(object): 126 class SETTINGS(object):
120 """Settings namespace for globally defined values.""" 127 """Settings namespace for globally defined values."""
121 env_name = None 128 env_name = None
122 129
123 130
124 class Error(Exception): 131 class Error(Exception):
125 """Base error for this module.""" 132 """Base error for this module."""
126 133
127 134
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 def get(self, key): 192 def get(self, key):
186 return self.cache.get(key) 193 return self.cache.get(key)
187 194
188 def set(self, key, value): 195 def set(self, key, value):
189 self.cache[key] = value 196 self.cache[key] = value
190 197
191 def delete(self, key): 198 def delete(self, key):
192 self.cache.pop(key, None) 199 self.cache.pop(key, None)
193 200
194 201
202 def _parse_expiry(expiry):
203 if expiry and isinstance(expiry, datetime.datetime):
204 return expiry.strftime(EXPIRY_FORMAT)
205 else:
206 return None
207
208
195 class Credentials(object): 209 class Credentials(object):
196 """Base class for all Credentials objects. 210 """Base class for all Credentials objects.
197 211
198 Subclasses must define an authorize() method that applies the credentials 212 Subclasses must define an authorize() method that applies the credentials
199 to an HTTP transport. 213 to an HTTP transport.
200 214
201 Subclasses must also specify a classmethod named 'from_json' that takes a 215 Subclasses must also specify a classmethod named 'from_json' that takes a
202 JSON string as input and returns an instantiated Credentials object. 216 JSON string as input and returns an instantiated Credentials object.
203 """ 217 """
204 218
205 NON_SERIALIZED_MEMBERS = ['store'] 219 NON_SERIALIZED_MEMBERS = frozenset(['store'])
206 220
207 def authorize(self, http): 221 def authorize(self, http):
208 """Take an httplib2.Http instance (or equivalent) and authorizes it. 222 """Take an httplib2.Http instance (or equivalent) and authorizes it.
209 223
210 Authorizes it for the set of credentials, usually by replacing 224 Authorizes it for the set of credentials, usually by replacing
211 http.request() with a method that adds in the appropriate headers and 225 http.request() with a method that adds in the appropriate headers and
212 then delegates to the original Http.request() method. 226 then delegates to the original Http.request() method.
213 227
214 Args: 228 Args:
215 http: httplib2.Http, an http object to be used to make the refresh 229 http: httplib2.Http, an http object to be used to make the refresh
(...skipping 20 matching lines...) Expand all
236 _abstract() 250 _abstract()
237 251
238 def apply(self, headers): 252 def apply(self, headers):
239 """Add the authorization to the headers. 253 """Add the authorization to the headers.
240 254
241 Args: 255 Args:
242 headers: dict, the headers to add the Authorization header to. 256 headers: dict, the headers to add the Authorization header to.
243 """ 257 """
244 _abstract() 258 _abstract()
245 259
246 def _to_json(self, strip): 260 def _to_json(self, strip, to_serialize=None):
247 """Utility function that creates JSON repr. of a Credentials object. 261 """Utility function that creates JSON repr. of a Credentials object.
248 262
249 Args: 263 Args:
250 strip: array, An array of names of members to not include in the 264 strip: array, An array of names of members to exclude from the
251 JSON. 265 JSON.
266 to_serialize: dict, (Optional) The properties for this object
267 that will be serialized. This allows callers to modify
268 before serializing.
252 269
253 Returns: 270 Returns:
254 string, a JSON representation of this instance, suitable to pass to 271 string, a JSON representation of this instance, suitable to pass to
255 from_json(). 272 from_json().
256 """ 273 """
257 t = type(self) 274 curr_type = self.__class__
258 d = copy.copy(self.__dict__) 275 if to_serialize is None:
276 to_serialize = copy.copy(self.__dict__)
259 for member in strip: 277 for member in strip:
260 if member in d: 278 if member in to_serialize:
261 del d[member] 279 del to_serialize[member]
262 if (d.get('token_expiry') and 280 to_serialize['token_expiry'] = _parse_expiry(
263 isinstance(d['token_expiry'], datetime.datetime)): 281 to_serialize.get('token_expiry'))
264 d['token_expiry'] = d['token_expiry'].strftime(EXPIRY_FORMAT) 282 # Add in information we will need later to reconstitute this instance.
265 # Add in information we will need later to reconsistitue this instance. 283 to_serialize['_class'] = curr_type.__name__
266 d['_class'] = t.__name__ 284 to_serialize['_module'] = curr_type.__module__
267 d['_module'] = t.__module__ 285 for key, val in to_serialize.items():
268 for key, val in d.items():
269 if isinstance(val, bytes): 286 if isinstance(val, bytes):
270 d[key] = val.decode('utf-8') 287 to_serialize[key] = val.decode('utf-8')
271 if isinstance(val, set): 288 if isinstance(val, set):
272 d[key] = list(val) 289 to_serialize[key] = list(val)
273 return json.dumps(d) 290 return json.dumps(to_serialize)
274 291
275 def to_json(self): 292 def to_json(self):
276 """Creating a JSON representation of an instance of Credentials. 293 """Creating a JSON representation of an instance of Credentials.
277 294
278 Returns: 295 Returns:
279 string, a JSON representation of this instance, suitable to pass to 296 string, a JSON representation of this instance, suitable to pass to
280 from_json(). 297 from_json().
281 """ 298 """
282 return self._to_json(Credentials.NON_SERIALIZED_MEMBERS) 299 return self._to_json(self.NON_SERIALIZED_MEMBERS)
283 300
284 @classmethod 301 @classmethod
285 def new_from_json(cls, s): 302 def new_from_json(cls, json_data):
286 """Utility class method to instantiate a Credentials subclass from JSON. 303 """Utility class method to instantiate a Credentials subclass from JSON.
287 304
288 Expects the JSON string to have been produced by to_json(). 305 Expects the JSON string to have been produced by to_json().
289 306
290 Args: 307 Args:
291 s: string or bytes, JSON from to_json(). 308 json_data: string or bytes, JSON from to_json().
292 309
293 Returns: 310 Returns:
294 An instance of the subclass of Credentials that was serialized with 311 An instance of the subclass of Credentials that was serialized with
295 to_json(). 312 to_json().
296 """ 313 """
297 json_string_as_unicode = _from_bytes(s) 314 json_data_as_unicode = _from_bytes(json_data)
298 data = json.loads(json_string_as_unicode) 315 data = json.loads(json_data_as_unicode)
299 # Find and call the right classmethod from_json() to restore 316 # Find and call the right classmethod from_json() to restore
300 # the object. 317 # the object.
301 module_name = data['_module'] 318 module_name = data['_module']
302 try: 319 try:
303 module_obj = __import__(module_name) 320 module_obj = __import__(module_name)
304 except ImportError: 321 except ImportError:
305 # In case there's an object from the old package structure, 322 # In case there's an object from the old package structure,
306 # update it 323 # update it
307 module_name = module_name.replace('.googleapiclient', '') 324 module_name = module_name.replace('.googleapiclient', '')
308 module_obj = __import__(module_name) 325 module_obj = __import__(module_name)
309 326
310 module_obj = __import__(module_name, 327 module_obj = __import__(module_name,
311 fromlist=module_name.split('.')[:-1]) 328 fromlist=module_name.split('.')[:-1])
312 kls = getattr(module_obj, data['_class']) 329 kls = getattr(module_obj, data['_class'])
313 from_json = getattr(kls, 'from_json') 330 return kls.from_json(json_data_as_unicode)
314 return from_json(json_string_as_unicode)
315 331
316 @classmethod 332 @classmethod
317 def from_json(cls, unused_data): 333 def from_json(cls, unused_data):
318 """Instantiate a Credentials object from a JSON description of it. 334 """Instantiate a Credentials object from a JSON description of it.
319 335
320 The JSON should have been produced by calling .to_json() on the object. 336 The JSON should have been produced by calling .to_json() on the object.
321 337
322 Args: 338 Args:
323 unused_data: dict, A deserialized JSON object. 339 unused_data: dict, A deserialized JSON object.
324 340
325 Returns: 341 Returns:
326 An instance of a Credentials subclass. 342 An instance of a Credentials subclass.
327 """ 343 """
328 return Credentials() 344 return Credentials()
329 345
330 346
331 class Flow(object): 347 class Flow(object):
332 """Base class for all Flow objects.""" 348 """Base class for all Flow objects."""
333 pass 349 pass
334 350
335 351
336 class Storage(object): 352 class Storage(object):
337 """Base class for all Storage objects. 353 """Base class for all Storage objects.
338 354
339 Store and retrieve a single credential. This class supports locking 355 Store and retrieve a single credential. This class supports locking
340 such that multiple processes and threads can operate on a single 356 such that multiple processes and threads can operate on a single
341 store. 357 store.
342 """ 358 """
359 def __init__(self, lock=None):
360 """Create a Storage instance.
361
362 Args:
363 lock: An optional threading.Lock-like object. Must implement at
364 least acquire() and release(). Does not need to be re-entrant.
365 """
366 self._lock = lock
343 367
344 def acquire_lock(self): 368 def acquire_lock(self):
345 """Acquires any lock necessary to access this Storage. 369 """Acquires any lock necessary to access this Storage.
346 370
347 This lock is not reentrant. 371 This lock is not reentrant.
348 """ 372 """
349 pass 373 if self._lock is not None:
374 self._lock.acquire()
350 375
351 def release_lock(self): 376 def release_lock(self):
352 """Release the Storage lock. 377 """Release the Storage lock.
353 378
354 Trying to release a lock that isn't held will result in a 379 Trying to release a lock that isn't held will result in a
355 RuntimeError. 380 RuntimeError in the case of a threading.Lock or multiprocessing.Lock.
356 """ 381 """
357 pass 382 if self._lock is not None:
383 self._lock.release()
358 384
359 def locked_get(self): 385 def locked_get(self):
360 """Retrieve credential. 386 """Retrieve credential.
361 387
362 The Storage lock must be held when this is called. 388 The Storage lock must be held when this is called.
363 389
364 Returns: 390 Returns:
365 oauth2client.client.Credentials 391 oauth2client.client.Credentials
366 """ 392 """
367 _abstract() 393 _abstract()
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 Args: 702 Args:
677 http: httplib2.Http, an http object to be used to make the refresh 703 http: httplib2.Http, an http object to be used to make the refresh
678 request. 704 request.
679 705
680 Returns: 706 Returns:
681 A set of strings containing the canonical list of scopes. 707 A set of strings containing the canonical list of scopes.
682 """ 708 """
683 self._retrieve_scopes(http.request) 709 self._retrieve_scopes(http.request)
684 return self.scopes 710 return self.scopes
685 711
686 def to_json(self):
687 return self._to_json(Credentials.NON_SERIALIZED_MEMBERS)
688
689 @classmethod 712 @classmethod
690 def from_json(cls, s): 713 def from_json(cls, json_data):
691 """Instantiate a Credentials object from a JSON description of it. 714 """Instantiate a Credentials object from a JSON description of it.
692 715
693 The JSON should have been produced by calling .to_json() on the object. 716 The JSON should have been produced by calling .to_json() on the object.
694 717
695 Args: 718 Args:
696 data: dict, A deserialized JSON object. 719 json_data: string or bytes, JSON to deserialize.
697 720
698 Returns: 721 Returns:
699 An instance of a Credentials subclass. 722 An instance of a Credentials subclass.
700 """ 723 """
701 s = _from_bytes(s) 724 data = json.loads(_from_bytes(json_data))
702 data = json.loads(s)
703 if (data.get('token_expiry') and 725 if (data.get('token_expiry') and
704 not isinstance(data['token_expiry'], datetime.datetime)): 726 not isinstance(data['token_expiry'], datetime.datetime)):
705 try: 727 try:
706 data['token_expiry'] = datetime.datetime.strptime( 728 data['token_expiry'] = datetime.datetime.strptime(
707 data['token_expiry'], EXPIRY_FORMAT) 729 data['token_expiry'], EXPIRY_FORMAT)
708 except ValueError: 730 except ValueError:
709 data['token_expiry'] = None 731 data['token_expiry'] = None
710 retval = cls( 732 retval = cls(
711 data['access_token'], 733 data['access_token'],
712 data['client_id'], 734 data['client_id'],
(...skipping 15 matching lines...) Expand all
728 """True if the credential is expired or invalid. 750 """True if the credential is expired or invalid.
729 751
730 If the token_expiry isn't set, we assume the token doesn't expire. 752 If the token_expiry isn't set, we assume the token doesn't expire.
731 """ 753 """
732 if self.invalid: 754 if self.invalid:
733 return True 755 return True
734 756
735 if not self.token_expiry: 757 if not self.token_expiry:
736 return False 758 return False
737 759
738 now = datetime.datetime.utcnow() 760 now = _UTCNOW()
739 if now >= self.token_expiry: 761 if now >= self.token_expiry:
740 logger.info('access_token is expired. Now: %s, token_expiry: %s', 762 logger.info('access_token is expired. Now: %s, token_expiry: %s',
741 now, self.token_expiry) 763 now, self.token_expiry)
742 return True 764 return True
743 return False 765 return False
744 766
745 def get_access_token(self, http=None): 767 def get_access_token(self, http=None):
746 """Return the access token and its expiration information. 768 """Return the access token and its expiration information.
747 769
748 If the token does not exist, get one. 770 If the token does not exist, get one.
(...skipping 22 matching lines...) Expand all
771 """Return the number of seconds until this token expires. 793 """Return the number of seconds until this token expires.
772 794
773 If token_expiry is in the past, this method will return 0, meaning the 795 If token_expiry is in the past, this method will return 0, meaning the
774 token has already expired. 796 token has already expired.
775 797
776 If token_expiry is None, this method will return None. Note that 798 If token_expiry is None, this method will return None. Note that
777 returning 0 in such a case would not be fair: the token may still be 799 returning 0 in such a case would not be fair: the token may still be
778 valid; we just don't know anything about it. 800 valid; we just don't know anything about it.
779 """ 801 """
780 if self.token_expiry: 802 if self.token_expiry:
781 now = datetime.datetime.utcnow() 803 now = _UTCNOW()
782 if self.token_expiry > now: 804 if self.token_expiry > now:
783 time_delta = self.token_expiry - now 805 time_delta = self.token_expiry - now
784 # TODO(orestica): return time_delta.total_seconds() 806 # TODO(orestica): return time_delta.total_seconds()
785 # once dropping support for Python 2.6 807 # once dropping support for Python 2.6
786 return time_delta.days * 86400 + time_delta.seconds 808 return time_delta.days * 86400 + time_delta.seconds
787 else: 809 else:
788 return 0 810 return 0
789 811
790 def _updateFromCredential(self, other): 812 def _updateFromCredential(self, other):
791 """Update this Credential from another instance.""" 813 """Update this Credential from another instance."""
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 Raises: 888 Raises:
867 HttpAccessTokenRefreshError: When the refresh fails. 889 HttpAccessTokenRefreshError: When the refresh fails.
868 """ 890 """
869 body = self._generate_refresh_request_body() 891 body = self._generate_refresh_request_body()
870 headers = self._generate_refresh_request_headers() 892 headers = self._generate_refresh_request_headers()
871 893
872 logger.info('Refreshing access_token') 894 logger.info('Refreshing access_token')
873 resp, content = http_request( 895 resp, content = http_request(
874 self.token_uri, method='POST', body=body, headers=headers) 896 self.token_uri, method='POST', body=body, headers=headers)
875 content = _from_bytes(content) 897 content = _from_bytes(content)
876 if resp.status == 200: 898 if resp.status == http_client.OK:
877 d = json.loads(content) 899 d = json.loads(content)
878 self.token_response = d 900 self.token_response = d
879 self.access_token = d['access_token'] 901 self.access_token = d['access_token']
880 self.refresh_token = d.get('refresh_token', self.refresh_token) 902 self.refresh_token = d.get('refresh_token', self.refresh_token)
881 if 'expires_in' in d: 903 if 'expires_in' in d:
882 self.token_expiry = datetime.timedelta( 904 delta = datetime.timedelta(seconds=int(d['expires_in']))
883 seconds=int(d['expires_in'])) + datetime.datetime.utcnow() 905 self.token_expiry = delta + _UTCNOW()
884 else: 906 else:
885 self.token_expiry = None 907 self.token_expiry = None
908 if 'id_token' in d:
909 self.id_token = _extract_id_token(d['id_token'])
910 else:
911 self.id_token = None
886 # On temporary refresh errors, the user does not actually have to 912 # On temporary refresh errors, the user does not actually have to
887 # re-authorize, so we unflag here. 913 # re-authorize, so we unflag here.
888 self.invalid = False 914 self.invalid = False
889 if self.store: 915 if self.store:
890 self.store.locked_put(self) 916 self.store.locked_put(self)
891 else: 917 else:
892 # An {'error':...} response body means the token is expired or 918 # An {'error':...} response body means the token is expired or
893 # revoked, so we flag the credentials as such. 919 # revoked, so we flag the credentials as such.
894 logger.info('Failed to retrieve access token: %s', content) 920 logger.info('Failed to retrieve access token: %s', content)
895 error_msg = 'Invalid response %s.' % resp['status'] 921 error_msg = 'Invalid response %s.' % resp['status']
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
927 access_token or refresh_token. 953 access_token or refresh_token.
928 954
929 Raises: 955 Raises:
930 TokenRevokeError: If the revoke request does not return with a 956 TokenRevokeError: If the revoke request does not return with a
931 200 OK. 957 200 OK.
932 """ 958 """
933 logger.info('Revoking token') 959 logger.info('Revoking token')
934 query_params = {'token': token} 960 query_params = {'token': token}
935 token_revoke_uri = _update_query_params(self.revoke_uri, query_params) 961 token_revoke_uri = _update_query_params(self.revoke_uri, query_params)
936 resp, content = http_request(token_revoke_uri) 962 resp, content = http_request(token_revoke_uri)
937 if resp.status == 200: 963 if resp.status == http_client.OK:
938 self.invalid = True 964 self.invalid = True
939 else: 965 else:
940 error_msg = 'Invalid response %s.' % resp.status 966 error_msg = 'Invalid response %s.' % resp.status
941 try: 967 try:
942 d = json.loads(_from_bytes(content)) 968 d = json.loads(_from_bytes(content))
943 if 'error' in d: 969 if 'error' in d:
944 error_msg = d['error'] 970 error_msg = d['error']
945 except (TypeError, ValueError): 971 except (TypeError, ValueError):
946 pass 972 pass
947 raise TokenRevokeError(error_msg) 973 raise TokenRevokeError(error_msg)
(...skipping 24 matching lines...) Expand all
972 Raises: 998 Raises:
973 Error: When refresh fails, indicating the the access token is 999 Error: When refresh fails, indicating the the access token is
974 invalid. 1000 invalid.
975 """ 1001 """
976 logger.info('Refreshing scopes') 1002 logger.info('Refreshing scopes')
977 query_params = {'access_token': token, 'fields': 'scope'} 1003 query_params = {'access_token': token, 'fields': 'scope'}
978 token_info_uri = _update_query_params(self.token_info_uri, 1004 token_info_uri = _update_query_params(self.token_info_uri,
979 query_params) 1005 query_params)
980 resp, content = http_request(token_info_uri) 1006 resp, content = http_request(token_info_uri)
981 content = _from_bytes(content) 1007 content = _from_bytes(content)
982 if resp.status == 200: 1008 if resp.status == http_client.OK:
983 d = json.loads(content) 1009 d = json.loads(content)
984 self.scopes = set(util.string_to_scopes(d.get('scope', ''))) 1010 self.scopes = set(util.string_to_scopes(d.get('scope', '')))
985 else: 1011 else:
986 error_msg = 'Invalid response %s.' % (resp.status,) 1012 error_msg = 'Invalid response %s.' % (resp.status,)
987 try: 1013 try:
988 d = json.loads(content) 1014 d = json.loads(content)
989 if 'error_description' in d: 1015 if 'error_description' in d:
990 error_msg = d['error_description'] 1016 error_msg = d['error_description']
991 except (TypeError, ValueError): 1017 except (TypeError, ValueError):
992 pass 1018 pass
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1036 access_token, 1062 access_token,
1037 None, 1063 None,
1038 None, 1064 None,
1039 None, 1065 None,
1040 None, 1066 None,
1041 None, 1067 None,
1042 user_agent, 1068 user_agent,
1043 revoke_uri=revoke_uri) 1069 revoke_uri=revoke_uri)
1044 1070
1045 @classmethod 1071 @classmethod
1046 def from_json(cls, s): 1072 def from_json(cls, json_data):
1047 data = json.loads(_from_bytes(s)) 1073 data = json.loads(_from_bytes(json_data))
1048 retval = AccessTokenCredentials( 1074 retval = AccessTokenCredentials(
1049 data['access_token'], 1075 data['access_token'],
1050 data['user_agent']) 1076 data['user_agent'])
1051 return retval 1077 return retval
1052 1078
1053 def _refresh(self, http_request): 1079 def _refresh(self, http_request):
1054 raise AccessTokenCredentialsError( 1080 raise AccessTokenCredentialsError(
1055 'The access_token is expired or invalid and can\'t be refreshed.') 1081 'The access_token is expired or invalid and can\'t be refreshed.')
1056 1082
1057 def _revoke(self, http_request): 1083 def _revoke(self, http_request):
(...skipping 20 matching lines...) Expand all
1078 # could lead to false negatives in the event that we are on GCE, but 1104 # could lead to false negatives in the event that we are on GCE, but
1079 # the metadata resolution was particularly slow. The latter case is 1105 # the metadata resolution was particularly slow. The latter case is
1080 # "unlikely". 1106 # "unlikely".
1081 connection = six.moves.http_client.HTTPConnection( 1107 connection = six.moves.http_client.HTTPConnection(
1082 _GCE_METADATA_HOST, timeout=1) 1108 _GCE_METADATA_HOST, timeout=1)
1083 1109
1084 try: 1110 try:
1085 headers = {_METADATA_FLAVOR_HEADER: _DESIRED_METADATA_FLAVOR} 1111 headers = {_METADATA_FLAVOR_HEADER: _DESIRED_METADATA_FLAVOR}
1086 connection.request('GET', '/', headers=headers) 1112 connection.request('GET', '/', headers=headers)
1087 response = connection.getresponse() 1113 response = connection.getresponse()
1088 if response.status == 200: 1114 if response.status == http_client.OK:
1089 return (response.getheader(_METADATA_FLAVOR_HEADER) == 1115 return (response.getheader(_METADATA_FLAVOR_HEADER) ==
1090 _DESIRED_METADATA_FLAVOR) 1116 _DESIRED_METADATA_FLAVOR)
1091 except socket.error: # socket.timeout or socket.error(64, 'Host is down') 1117 except socket.error: # socket.timeout or socket.error(64, 'Host is down')
1092 logger.info('Timeout attempting to reach GCE metadata service.') 1118 logger.info('Timeout attempting to reach GCE metadata service.')
1093 return False 1119 return False
1094 finally: 1120 finally:
1095 connection.close() 1121 connection.close()
1096 1122
1097 1123
1098 def _in_gae_environment(): 1124 def _in_gae_environment():
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
1153 service = build('compute', 'v1', credentials=credentials) 1179 service = build('compute', 'v1', credentials=credentials)
1154 1180
1155 PROJECT = 'bamboo-machine-422' 1181 PROJECT = 'bamboo-machine-422'
1156 ZONE = 'us-central1-a' 1182 ZONE = 'us-central1-a'
1157 request = service.instances().list(project=PROJECT, zone=ZONE) 1183 request = service.instances().list(project=PROJECT, zone=ZONE)
1158 response = request.execute() 1184 response = request.execute()
1159 1185
1160 print(response) 1186 print(response)
1161 """ 1187 """
1162 1188
1189 NON_SERIALIZED_MEMBERS = (
1190 frozenset(['_private_key']) |
1191 OAuth2Credentials.NON_SERIALIZED_MEMBERS)
1192 """Members that aren't serialized when object is converted to JSON."""
1193
1163 def __init__(self, access_token, client_id, client_secret, refresh_token, 1194 def __init__(self, access_token, client_id, client_secret, refresh_token,
1164 token_expiry, token_uri, user_agent, 1195 token_expiry, token_uri, user_agent,
1165 revoke_uri=GOOGLE_REVOKE_URI): 1196 revoke_uri=GOOGLE_REVOKE_URI):
1166 """Create an instance of GoogleCredentials. 1197 """Create an instance of GoogleCredentials.
1167 1198
1168 This constructor is not usually called by the user, instead 1199 This constructor is not usually called by the user, instead
1169 GoogleCredentials objects are instantiated by 1200 GoogleCredentials objects are instantiated by
1170 GoogleCredentials.from_stream() or 1201 GoogleCredentials.from_stream() or
1171 GoogleCredentials.get_application_default(). 1202 GoogleCredentials.get_application_default().
1172 1203
(...skipping 22 matching lines...) Expand all
1195 """ 1226 """
1196 return False 1227 return False
1197 1228
1198 def create_scoped(self, scopes): 1229 def create_scoped(self, scopes):
1199 """Create a Credentials object for the given scopes. 1230 """Create a Credentials object for the given scopes.
1200 1231
1201 The Credentials type is preserved. 1232 The Credentials type is preserved.
1202 """ 1233 """
1203 return self 1234 return self
1204 1235
1236 @classmethod
1237 def from_json(cls, json_data):
1238 # TODO(issue 388): eliminate the circularity that is the reason for
1239 # this non-top-level import.
1240 from oauth2client.service_account import ServiceAccountCredentials
1241 data = json.loads(_from_bytes(json_data))
1242
1243 # We handle service_account.ServiceAccountCredentials since it is a
1244 # possible return type of GoogleCredentials.get_application_default()
1245 if (data['_module'] == 'oauth2client.service_account' and
1246 data['_class'] == 'ServiceAccountCredentials'):
1247 return ServiceAccountCredentials.from_json(data)
1248
1249 token_expiry = _parse_expiry(data.get('token_expiry'))
1250 google_credentials = cls(
1251 data['access_token'],
1252 data['client_id'],
1253 data['client_secret'],
1254 data['refresh_token'],
1255 token_expiry,
1256 data['token_uri'],
1257 data['user_agent'],
1258 revoke_uri=data.get('revoke_uri', None))
1259 google_credentials.invalid = data['invalid']
1260 return google_credentials
1261
1205 @property 1262 @property
1206 def serialization_data(self): 1263 def serialization_data(self):
1207 """Get the fields and values identifying the current credentials.""" 1264 """Get the fields and values identifying the current credentials."""
1208 return { 1265 return {
1209 'type': 'authorized_user', 1266 'type': 'authorized_user',
1210 'client_id': self.client_id, 1267 'client_id': self.client_id,
1211 'client_secret': self.client_secret, 1268 'client_secret': self.client_secret,
1212 'refresh_token': self.refresh_token 1269 'refresh_token': self.refresh_token
1213 } 1270 }
1214 1271
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
1408 'File ' + application_default_credential_filename + 1465 'File ' + application_default_credential_filename +
1409 ' (pointed by ' + 1466 ' (pointed by ' +
1410 GOOGLE_APPLICATION_CREDENTIALS + 1467 GOOGLE_APPLICATION_CREDENTIALS +
1411 ' environment variable) does not exist!') 1468 ' environment variable) does not exist!')
1412 1469
1413 1470
1414 def _get_well_known_file(): 1471 def _get_well_known_file():
1415 """Get the well known file produced by command 'gcloud auth login'.""" 1472 """Get the well known file produced by command 'gcloud auth login'."""
1416 # TODO(orestica): Revisit this method once gcloud provides a better way 1473 # TODO(orestica): Revisit this method once gcloud provides a better way
1417 # of pinpointing the exact location of the file. 1474 # of pinpointing the exact location of the file.
1418
1419 WELL_KNOWN_CREDENTIALS_FILE = 'application_default_credentials.json'
1420
1421 default_config_dir = os.getenv(_CLOUDSDK_CONFIG_ENV_VAR) 1475 default_config_dir = os.getenv(_CLOUDSDK_CONFIG_ENV_VAR)
1422 if default_config_dir is None: 1476 if default_config_dir is None:
1423 if os.name == 'nt': 1477 if os.name == 'nt':
1424 try: 1478 try:
1425 default_config_dir = os.path.join(os.environ['APPDATA'], 1479 default_config_dir = os.path.join(os.environ['APPDATA'],
1426 _CLOUDSDK_CONFIG_DIRECTORY) 1480 _CLOUDSDK_CONFIG_DIRECTORY)
1427 except KeyError: 1481 except KeyError:
1428 # This should never happen unless someone is really 1482 # This should never happen unless someone is really
1429 # messing with things. 1483 # messing with things.
1430 drive = os.environ.get('SystemDrive', 'C:') 1484 drive = os.environ.get('SystemDrive', 'C:')
1431 default_config_dir = os.path.join(drive, '\\', 1485 default_config_dir = os.path.join(drive, '\\',
1432 _CLOUDSDK_CONFIG_DIRECTORY) 1486 _CLOUDSDK_CONFIG_DIRECTORY)
1433 else: 1487 else:
1434 default_config_dir = os.path.join(os.path.expanduser('~'), 1488 default_config_dir = os.path.join(os.path.expanduser('~'),
1435 '.config', 1489 '.config',
1436 _CLOUDSDK_CONFIG_DIRECTORY) 1490 _CLOUDSDK_CONFIG_DIRECTORY)
1437 1491
1438 return os.path.join(default_config_dir, WELL_KNOWN_CREDENTIALS_FILE) 1492 return os.path.join(default_config_dir, _WELL_KNOWN_CREDENTIALS_FILE)
1439 1493
1440 1494
1441 def _get_application_default_credential_from_file(filename): 1495 def _get_application_default_credential_from_file(filename):
1442 """Build the Application Default Credentials from file.""" 1496 """Build the Application Default Credentials from file."""
1443
1444 from oauth2client import service_account
1445
1446 # read the credentials from the file 1497 # read the credentials from the file
1447 with open(filename) as file_obj: 1498 with open(filename) as file_obj:
1448 client_credentials = json.load(file_obj) 1499 client_credentials = json.load(file_obj)
1449 1500
1450 credentials_type = client_credentials.get('type') 1501 credentials_type = client_credentials.get('type')
1451 if credentials_type == AUTHORIZED_USER: 1502 if credentials_type == AUTHORIZED_USER:
1452 required_fields = set(['client_id', 'client_secret', 'refresh_token']) 1503 required_fields = set(['client_id', 'client_secret', 'refresh_token'])
1453 elif credentials_type == SERVICE_ACCOUNT: 1504 elif credentials_type == SERVICE_ACCOUNT:
1454 required_fields = set(['client_id', 'client_email', 'private_key_id', 1505 required_fields = set(['client_id', 'client_email', 'private_key_id',
1455 'private_key']) 1506 'private_key'])
(...skipping 10 matching lines...) Expand all
1466 if client_credentials['type'] == AUTHORIZED_USER: 1517 if client_credentials['type'] == AUTHORIZED_USER:
1467 return GoogleCredentials( 1518 return GoogleCredentials(
1468 access_token=None, 1519 access_token=None,
1469 client_id=client_credentials['client_id'], 1520 client_id=client_credentials['client_id'],
1470 client_secret=client_credentials['client_secret'], 1521 client_secret=client_credentials['client_secret'],
1471 refresh_token=client_credentials['refresh_token'], 1522 refresh_token=client_credentials['refresh_token'],
1472 token_expiry=None, 1523 token_expiry=None,
1473 token_uri=GOOGLE_TOKEN_URI, 1524 token_uri=GOOGLE_TOKEN_URI,
1474 user_agent='Python client library') 1525 user_agent='Python client library')
1475 else: # client_credentials['type'] == SERVICE_ACCOUNT 1526 else: # client_credentials['type'] == SERVICE_ACCOUNT
1476 return service_account._ServiceAccountCredentials( 1527 from oauth2client.service_account import ServiceAccountCredentials
1477 service_account_id=client_credentials['client_id'], 1528 return ServiceAccountCredentials.from_json_keyfile_dict(
1478 service_account_email=client_credentials['client_email'], 1529 client_credentials)
1479 private_key_id=client_credentials['private_key_id'],
1480 private_key_pkcs8_text=client_credentials['private_key'],
1481 scopes=[])
1482 1530
1483 1531
1484 def _raise_exception_for_missing_fields(missing_fields): 1532 def _raise_exception_for_missing_fields(missing_fields):
1485 raise ApplicationDefaultCredentialsError( 1533 raise ApplicationDefaultCredentialsError(
1486 'The following field(s) must be defined: ' + ', '.join(missing_fields)) 1534 'The following field(s) must be defined: ' + ', '.join(missing_fields))
1487 1535
1488 1536
1489 def _raise_exception_for_reading_json(credential_file, 1537 def _raise_exception_for_reading_json(credential_file,
1490 extra_help, 1538 extra_help,
1491 error): 1539 error):
1492 raise ApplicationDefaultCredentialsError( 1540 raise ApplicationDefaultCredentialsError(
1493 'An error was encountered while reading json file: ' + 1541 'An error was encountered while reading json file: ' +
1494 credential_file + extra_help + ': ' + str(error)) 1542 credential_file + extra_help + ': ' + str(error))
1495 1543
1496 1544
1497 def _get_application_default_credential_GAE(): 1545 def _get_application_default_credential_GAE():
1498 from oauth2client.appengine import AppAssertionCredentials 1546 from oauth2client.contrib.appengine import AppAssertionCredentials
1499 1547
1500 return AppAssertionCredentials([]) 1548 return AppAssertionCredentials([])
1501 1549
1502 1550
1503 def _get_application_default_credential_GCE(): 1551 def _get_application_default_credential_GCE():
1504 from oauth2client.gce import AppAssertionCredentials 1552 from oauth2client.contrib.gce import AppAssertionCredentials
1505 1553
1506 return AppAssertionCredentials([]) 1554 return AppAssertionCredentials()
1507 1555
1508 1556
1509 class AssertionCredentials(GoogleCredentials): 1557 class AssertionCredentials(GoogleCredentials):
1510 """Abstract Credentials object used for OAuth 2.0 assertion grants. 1558 """Abstract Credentials object used for OAuth 2.0 assertion grants.
1511 1559
1512 This credential does not require a flow to instantiate because it 1560 This credential does not require a flow to instantiate because it
1513 represents a two legged flow, and therefore has all of the required 1561 represents a two legged flow, and therefore has all of the required
1514 information to generate and refresh its own access tokens. It must 1562 information to generate and refresh its own access tokens. It must
1515 be subclassed to generate the appropriate assertion string. 1563 be subclassed to generate the appropriate assertion string.
1516 1564
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1562 def _revoke(self, http_request): 1610 def _revoke(self, http_request):
1563 """Revokes the access_token and deletes the store if available. 1611 """Revokes the access_token and deletes the store if available.
1564 1612
1565 Args: 1613 Args:
1566 http_request: callable, a callable that matches the method 1614 http_request: callable, a callable that matches the method
1567 signature of httplib2.Http.request, used to make the 1615 signature of httplib2.Http.request, used to make the
1568 revoke request. 1616 revoke request.
1569 """ 1617 """
1570 self._do_revoke(http_request, self.access_token) 1618 self._do_revoke(http_request, self.access_token)
1571 1619
1620 def sign_blob(self, blob):
1621 """Cryptographically sign a blob (of bytes).
1622
1623 Args:
1624 blob: bytes, Message to be signed.
1625
1626 Returns:
1627 tuple, A pair of the private key ID used to sign the blob and
1628 the signed contents.
1629 """
1630 raise NotImplementedError('This method is abstract.')
1631
1572 1632
1573 def _RequireCryptoOrDie(): 1633 def _RequireCryptoOrDie():
1574 """Ensure we have a crypto library, or throw CryptoUnavailableError. 1634 """Ensure we have a crypto library, or throw CryptoUnavailableError.
1575 1635
1576 The oauth2client.crypt module requires either PyCrypto or PyOpenSSL 1636 The oauth2client.crypt module requires either PyCrypto or PyOpenSSL
1577 to be available in order to function, but these are optional 1637 to be available in order to function, but these are optional
1578 dependencies. 1638 dependencies.
1579 """ 1639 """
1580 if not HAS_CRYPTO: 1640 if not HAS_CRYPTO:
1581 raise CryptoUnavailableError('No crypto library available') 1641 raise CryptoUnavailableError('No crypto library available')
1582 1642
1583 1643
1584 class SignedJwtAssertionCredentials(AssertionCredentials):
1585 """Credentials object used for OAuth 2.0 Signed JWT assertion grants.
1586
1587 This credential does not require a flow to instantiate because it
1588 represents a two legged flow, and therefore has all of the required
1589 information to generate and refresh its own access tokens.
1590
1591 SignedJwtAssertionCredentials requires either PyOpenSSL, or PyCrypto
1592 2.6 or later. For App Engine you may also consider using
1593 AppAssertionCredentials.
1594 """
1595
1596 MAX_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
1597
1598 @util.positional(4)
1599 def __init__(self,
1600 service_account_name,
1601 private_key,
1602 scope,
1603 private_key_password='notasecret',
1604 user_agent=None,
1605 token_uri=GOOGLE_TOKEN_URI,
1606 revoke_uri=GOOGLE_REVOKE_URI,
1607 **kwargs):
1608 """Constructor for SignedJwtAssertionCredentials.
1609
1610 Args:
1611 service_account_name: string, id for account, usually an email
1612 address.
1613 private_key: string or bytes, private key in PKCS12 or PEM format.
1614 scope: string or iterable of strings, scope(s) of the credentials
1615 being requested.
1616 private_key_password: string, password for private_key, unused if
1617 private_key is in PEM format.
1618 user_agent: string, HTTP User-Agent to provide for this
1619 application.
1620 token_uri: string, URI for token endpoint. For convenience defaults
1621 to Google's endpoints but any OAuth 2.0 provider can be
1622 used.
1623 revoke_uri: string, URI for revoke endpoint.
1624 kwargs: kwargs, Additional parameters to add to the JWT token, for
1625 example sub=joe@xample.org.
1626
1627 Raises:
1628 CryptoUnavailableError if no crypto library is available.
1629 """
1630 _RequireCryptoOrDie()
1631 super(SignedJwtAssertionCredentials, self).__init__(
1632 None,
1633 user_agent=user_agent,
1634 token_uri=token_uri,
1635 revoke_uri=revoke_uri,
1636 )
1637
1638 self.scope = util.scopes_to_string(scope)
1639
1640 # Keep base64 encoded so it can be stored in JSON.
1641 self.private_key = base64.b64encode(_to_bytes(private_key))
1642 self.private_key_password = private_key_password
1643 self.service_account_name = service_account_name
1644 self.kwargs = kwargs
1645
1646 @classmethod
1647 def from_json(cls, s):
1648 data = json.loads(_from_bytes(s))
1649 retval = SignedJwtAssertionCredentials(
1650 data['service_account_name'],
1651 base64.b64decode(data['private_key']),
1652 data['scope'],
1653 private_key_password=data['private_key_password'],
1654 user_agent=data['user_agent'],
1655 token_uri=data['token_uri'],
1656 **data['kwargs']
1657 )
1658 retval.invalid = data['invalid']
1659 retval.access_token = data['access_token']
1660 return retval
1661
1662 def _generate_assertion(self):
1663 """Generate the assertion that will be used in the request."""
1664 now = int(time.time())
1665 payload = {
1666 'aud': self.token_uri,
1667 'scope': self.scope,
1668 'iat': now,
1669 'exp': now + SignedJwtAssertionCredentials.MAX_TOKEN_LIFETIME_SECS,
1670 'iss': self.service_account_name
1671 }
1672 payload.update(self.kwargs)
1673 logger.debug(str(payload))
1674
1675 private_key = base64.b64decode(self.private_key)
1676 return crypt.make_signed_jwt(crypt.Signer.from_string(
1677 private_key, self.private_key_password), payload)
1678
1679 # Only used in verify_id_token(), which is always calling to the same URI 1644 # Only used in verify_id_token(), which is always calling to the same URI
1680 # for the certs. 1645 # for the certs.
1681 _cached_http = httplib2.Http(MemoryCache()) 1646 _cached_http = httplib2.Http(MemoryCache())
1682 1647
1683 1648
1684 @util.positional(2) 1649 @util.positional(2)
1685 def verify_id_token(id_token, audience, http=None, 1650 def verify_id_token(id_token, audience, http=None,
1686 cert_uri=ID_TOKEN_VERIFICATION_CERTS): 1651 cert_uri=ID_TOKEN_VERIFICATION_CERTS):
1687 """Verifies a signed JWT id_token. 1652 """Verifies a signed JWT id_token.
1688 1653
(...skipping 13 matching lines...) Expand all
1702 1667
1703 Raises: 1668 Raises:
1704 oauth2client.crypt.AppIdentityError: if the JWT fails to verify. 1669 oauth2client.crypt.AppIdentityError: if the JWT fails to verify.
1705 CryptoUnavailableError: if no crypto library is available. 1670 CryptoUnavailableError: if no crypto library is available.
1706 """ 1671 """
1707 _RequireCryptoOrDie() 1672 _RequireCryptoOrDie()
1708 if http is None: 1673 if http is None:
1709 http = _cached_http 1674 http = _cached_http
1710 1675
1711 resp, content = http.request(cert_uri) 1676 resp, content = http.request(cert_uri)
1712 if resp.status == 200: 1677 if resp.status == http_client.OK:
1713 certs = json.loads(_from_bytes(content)) 1678 certs = json.loads(_from_bytes(content))
1714 return crypt.verify_signed_jwt_with_certs(id_token, certs, audience) 1679 return crypt.verify_signed_jwt_with_certs(id_token, certs, audience)
1715 else: 1680 else:
1716 raise VerifyJwtTokenError('Status code: %d' % resp.status) 1681 raise VerifyJwtTokenError('Status code: %d' % resp.status)
1717 1682
1718 1683
1719 def _extract_id_token(id_token): 1684 def _extract_id_token(id_token):
1720 """Extract the JSON payload from a JWT. 1685 """Extract the JSON payload from a JWT.
1721 1686
1722 Does the extraction w/o checking the signature. 1687 Does the extraction w/o checking the signature.
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
2047 2012
2048 if self.user_agent is not None: 2013 if self.user_agent is not None:
2049 headers['user-agent'] = self.user_agent 2014 headers['user-agent'] = self.user_agent
2050 2015
2051 if http is None: 2016 if http is None:
2052 http = httplib2.Http() 2017 http = httplib2.Http()
2053 2018
2054 resp, content = http.request(self.device_uri, method='POST', body=body, 2019 resp, content = http.request(self.device_uri, method='POST', body=body,
2055 headers=headers) 2020 headers=headers)
2056 content = _from_bytes(content) 2021 content = _from_bytes(content)
2057 if resp.status == 200: 2022 if resp.status == http_client.OK:
2058 try: 2023 try:
2059 flow_info = json.loads(content) 2024 flow_info = json.loads(content)
2060 except ValueError as e: 2025 except ValueError as e:
2061 raise OAuth2DeviceCodeError( 2026 raise OAuth2DeviceCodeError(
2062 'Could not parse server response as JSON: "%s", ' 2027 'Could not parse server response as JSON: "%s", '
2063 'error: "%s"' % (content, e)) 2028 'error: "%s"' % (content, e))
2064 return DeviceFlowInfo.FromResponse(flow_info) 2029 return DeviceFlowInfo.FromResponse(flow_info)
2065 else: 2030 else:
2066 error_msg = 'Invalid response %s.' % resp.status 2031 error_msg = 'Invalid response %s.' % resp.status
2067 try: 2032 try:
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
2130 headers['Authorization'] = self.authorization_header 2095 headers['Authorization'] = self.authorization_header
2131 if self.user_agent is not None: 2096 if self.user_agent is not None:
2132 headers['user-agent'] = self.user_agent 2097 headers['user-agent'] = self.user_agent
2133 2098
2134 if http is None: 2099 if http is None:
2135 http = httplib2.Http() 2100 http = httplib2.Http()
2136 2101
2137 resp, content = http.request(self.token_uri, method='POST', body=body, 2102 resp, content = http.request(self.token_uri, method='POST', body=body,
2138 headers=headers) 2103 headers=headers)
2139 d = _parse_exchange_token_response(content) 2104 d = _parse_exchange_token_response(content)
2140 if resp.status == 200 and 'access_token' in d: 2105 if resp.status == http_client.OK and 'access_token' in d:
2141 access_token = d['access_token'] 2106 access_token = d['access_token']
2142 refresh_token = d.get('refresh_token', None) 2107 refresh_token = d.get('refresh_token', None)
2143 if not refresh_token: 2108 if not refresh_token:
2144 logger.info( 2109 logger.info(
2145 'Received token response with no refresh_token. Consider ' 2110 'Received token response with no refresh_token. Consider '
2146 "reauthenticating with approval_prompt='force'.") 2111 "reauthenticating with approval_prompt='force'.")
2147 token_expiry = None 2112 token_expiry = None
2148 if 'expires_in' in d: 2113 if 'expires_in' in d:
2149 token_expiry = ( 2114 delta = datetime.timedelta(seconds=int(d['expires_in']))
2150 datetime.datetime.utcnow() + 2115 token_expiry = delta + _UTCNOW()
2151 datetime.timedelta(seconds=int(d['expires_in'])))
2152 2116
2153 extracted_id_token = None 2117 extracted_id_token = None
2154 if 'id_token' in d: 2118 if 'id_token' in d:
2155 extracted_id_token = _extract_id_token(d['id_token']) 2119 extracted_id_token = _extract_id_token(d['id_token'])
2156 2120
2157 logger.info('Successfully retrieved access token') 2121 logger.info('Successfully retrieved access token')
2158 return OAuth2Credentials( 2122 return OAuth2Credentials(
2159 access_token, self.client_id, self.client_secret, 2123 access_token, self.client_id, self.client_secret,
2160 refresh_token, token_expiry, self.token_uri, self.user_agent, 2124 refresh_token, token_expiry, self.token_uri, self.user_agent,
2161 revoke_uri=self.revoke_uri, id_token=extracted_id_token, 2125 revoke_uri=self.revoke_uri, id_token=extracted_id_token,
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
2233 scope, **constructor_kwargs) 2197 scope, **constructor_kwargs)
2234 2198
2235 except clientsecrets.InvalidClientSecretsError: 2199 except clientsecrets.InvalidClientSecretsError:
2236 if message: 2200 if message:
2237 sys.exit(message) 2201 sys.exit(message)
2238 else: 2202 else:
2239 raise 2203 raise
2240 else: 2204 else:
2241 raise UnknownClientSecretsFlowError( 2205 raise UnknownClientSecretsFlowError(
2242 'This OAuth 2.0 flow is unsupported: %r' % client_type) 2206 'This OAuth 2.0 flow is unsupported: %r' % client_type)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698