Making oauth2client/ files pass PEP8.

This commit is contained in:
Danny Hermes
2015-08-20 16:08:20 -07:00
parent b70baa4fab
commit d7c0c38b8d
20 changed files with 558 additions and 495 deletions

View File

@@ -63,11 +63,11 @@ def _to_bytes(value, encoding='ascii'):
ValueError if the value could not be converted to bytes.
"""
result = (value.encode(encoding)
if isinstance(value, six.text_type) else value)
if isinstance(value, six.text_type) else value)
if isinstance(result, six.binary_type):
return result
else:
raise ValueError('%r could not be converted to bytes' % (value, ))
raise ValueError('%r could not be converted to bytes' % (value,))
def _from_bytes(value):
@@ -84,11 +84,11 @@ def _from_bytes(value):
ValueError if the value could not be converted to unicode.
"""
result = (value.decode('utf-8')
if isinstance(value, six.binary_type) else value)
if isinstance(value, six.binary_type) else value)
if isinstance(result, six.text_type):
return result
else:
raise ValueError('%r could not be converted to unicode' % (value, ))
raise ValueError('%r could not be converted to unicode' % (value,))
def _urlsafe_b64encode(raw_bytes):

View File

@@ -54,7 +54,7 @@ class OpenSSLVerifier(object):
return False
@staticmethod
def from_string(key_pem, is_x509_cert):
def from_string(key_pem, is_x509_cert):
"""Construct a Verified instance from a string.
Args:
@@ -136,4 +136,4 @@ def pkcs12_key_as_pem(private_key_text, private_key_password):
pkcs12 = crypto.load_pkcs12(decoded_body, private_key_password)
return crypto.dump_privatekey(crypto.FILETYPE_PEM,
pkcs12.get_privatekey())
pkcs12.get_privatekey())

View File

@@ -50,7 +50,7 @@ class PyCryptoVerifier(object):
"""
message = _to_bytes(message, encoding='utf-8')
return PKCS1_v1_5.new(self._pubkey).verify(
SHA256.new(message), signature)
SHA256.new(message), signature)
@staticmethod
def from_string(key_pem, is_x509_cert):
@@ -58,8 +58,8 @@ class PyCryptoVerifier(object):
Args:
key_pem: string, public key in PEM format.
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it is
expected to be an RSA key in PEM format.
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it
is expected to be an RSA key in PEM format.
Returns:
Verifier instance.
@@ -121,8 +121,9 @@ class PyCryptoSigner(object):
pkey = RSA.importKey(parsed_pem_key)
else:
raise NotImplementedError(
'PKCS12 format is not supported by the PyCrypto library. '
'Try converting to a "PEM" '
'(openssl pkcs12 -in xxxxx.p12 -nodes -nocerts > privatekey.pem) '
'or using PyOpenSSL if native code is an option.')
'PKCS12 format is not supported by the PyCrypto library. '
'Try converting to a "PEM" '
'(openssl pkcs12 -in xxxxx.p12 -nodes -nocerts > '
'privatekey.pem) '
'or using PyOpenSSL if native code is an option.')
return PyCryptoSigner(pkey)

View File

@@ -132,7 +132,8 @@ def xsrf_secret_key():
model.secret = _generate_new_xsrf_secret_key()
model.put()
secret = model.secret
memcache.add(XSRF_MEMCACHE_ID, secret, namespace=OAUTH2CLIENT_NAMESPACE)
memcache.add(XSRF_MEMCACHE_ID, secret,
namespace=OAUTH2CLIENT_NAMESPACE)
return str(secret)
@@ -166,7 +167,8 @@ class AppAssertionCredentials(AssertionCredentials):
self._kwargs = kwargs
self.service_account_id = kwargs.get('service_account_id', None)
# Assertion type is no longer used, but still in the parent class signature.
# Assertion type is no longer used, but still in the
# parent class signature.
super(AppAssertionCredentials, self).__init__(None)
@classmethod
@@ -192,14 +194,15 @@ class AppAssertionCredentials(AssertionCredentials):
try:
scopes = self.scope.split()
(token, _) = app_identity.get_access_token(
scopes, service_account_id=self.service_account_id)
scopes, service_account_id=self.service_account_id)
except app_identity.Error as e:
raise AccessTokenRefreshError(str(e))
self.access_token = token
@property
def serialization_data(self):
raise NotImplementedError('Cannot serialize credentials for AppEngine.')
raise NotImplementedError('Cannot serialize credentials '
'for Google App Engine.')
def create_scoped_required(self):
return not self.scope
@@ -220,8 +223,8 @@ class FlowProperty(db.Property):
# For writing to datastore.
def get_value_for_datastore(self, model_instance):
flow = super(FlowProperty,
self).get_value_for_datastore(model_instance)
flow = super(FlowProperty, self).get_value_for_datastore(
model_instance)
return db.Blob(pickle.dumps(flow))
# For reading from datastore.
@@ -233,8 +236,8 @@ class FlowProperty(db.Property):
def validate(self, value):
if value is not None and not isinstance(value, Flow):
raise db.BadValueError('Property %s must be convertible '
'to a FlowThreeLegged instance (%s)' %
(self.name, value))
'to a FlowThreeLegged instance (%s)' %
(self.name, value))
return super(FlowProperty, self).validate(value)
def empty(self, value):
@@ -266,7 +269,8 @@ if ndb is not None:
logger.info('validate: Got type %s', type(value))
if value is not None and not isinstance(value, Flow):
raise TypeError('Property %s must be convertible to a flow '
'instance; received: %s.' % (self._name, value))
'instance; received: %s.' % (self._name,
value))
class CredentialsProperty(db.Property):
@@ -282,8 +286,8 @@ class CredentialsProperty(db.Property):
# For writing to datastore.
def get_value_for_datastore(self, model_instance):
logger.info("get: Got type " + str(type(model_instance)))
cred = super(CredentialsProperty,
self).get_value_for_datastore(model_instance)
cred = super(CredentialsProperty, self).get_value_for_datastore(
model_instance)
if cred is None:
cred = ''
else:
@@ -308,16 +312,12 @@ class CredentialsProperty(db.Property):
logger.info("validate: Got type " + str(type(value)))
if value is not None and not isinstance(value, Credentials):
raise db.BadValueError('Property %s must be convertible '
'to a Credentials instance (%s)' %
(self.name, value))
#if value is not None and not isinstance(value, Credentials):
# return None
'to a Credentials instance (%s)' %
(self.name, value))
return value
if ndb is not None:
# TODO(dhermes): Turn this into a JsonProperty and overhaul the Credentials
# and subclass mechanics to use new_from_dict, to_dict,
# from_dict, etc.
@@ -344,8 +344,9 @@ if ndb is not None:
"""
logger.info('validate: Got type %s', type(value))
if value is not None and not isinstance(value, Credentials):
raise TypeError('Property %s must be convertible to a credentials '
'instance; received: %s.' % (self._name, value))
raise TypeError('Property %s must be convertible to a '
'credentials instance; received: %s.' %
(self._name, value))
def _to_base_type(self, value):
"""Converts our validated value to a JSON serialized string.
@@ -409,7 +410,8 @@ class StorageByKeyName(Storage):
"""
if key_name is None:
if user is None:
raise ValueError('StorageByKeyName called with no key name or user.')
raise ValueError('StorageByKeyName called with no '
'key name or user.')
key_name = user.user_id()
self._model = model
@@ -423,15 +425,17 @@ class StorageByKeyName(Storage):
Returns:
Boolean indicating whether or not the model is an NDB or DB model.
"""
# issubclass will fail if one of the arguments is not a class, only need
# worry about new-style classes since ndb and db models are new-style
# issubclass will fail if one of the arguments is not a class, only
# need worry about new-style classes since ndb and db models are
# new-style
if isinstance(self._model, type):
if ndb is not None and issubclass(self._model, ndb.Model):
return True
elif issubclass(self._model, db.Model):
return False
raise TypeError('Model class not an NDB or DB model: %s.' % (self._model, ))
raise TypeError('Model class not an NDB or DB model: %s.' %
(self._model,))
def _get_entity(self):
"""Retrieve entity from datastore.
@@ -460,7 +464,7 @@ class StorageByKeyName(Storage):
db.delete(entity_key)
@db.non_transactional(allow_existing=True)
def locked_get(self):
def locked_get(self):
"""Retrieve Credential from datastore.
Returns:
@@ -496,7 +500,7 @@ class StorageByKeyName(Storage):
self._cache.set(self._key_name, credentials.to_json())
@db.non_transactional(allow_existing=True)
def locked_delete(self):
def locked_delete(self):
"""Delete Credential from datastore."""
if self._cache:
@@ -548,8 +552,8 @@ def _build_state_value(request_handler, user):
"""
uri = request_handler.request.url
token = xsrfutil.generate_token(xsrf_secret_key(), user.user_id(),
action_id=str(uri))
return uri + ':' + token
action_id=str(uri))
return uri + ':' + token
def _parse_state_value(state, user):
@@ -569,7 +573,7 @@ def _parse_state_value(state, user):
"""
uri, token = state.rsplit(':', 1)
if not xsrfutil.validate_token(xsrf_secret_key(), token, user.user_id(),
action_id=uri):
action_id=uri):
raise InvalidXsrfTokenError()
return uri
@@ -629,18 +633,17 @@ class OAuth2Decorator(object):
@util.positional(4)
def __init__(self, client_id, client_secret, scope,
auth_uri=GOOGLE_AUTH_URI,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
user_agent=None,
message=None,
callback_path='/oauth2callback',
token_response_param=None,
_storage_class=StorageByKeyName,
_credentials_class=CredentialsModel,
_credentials_property_name='credentials',
**kwargs):
auth_uri=GOOGLE_AUTH_URI,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
user_agent=None,
message=None,
callback_path='/oauth2callback',
token_response_param=None,
_storage_class=StorageByKeyName,
_credentials_class=CredentialsModel,
_credentials_property_name='credentials',
**kwargs):
"""Constructor for OAuth2Decorator
Args:
@@ -659,13 +662,14 @@ class OAuth2Decorator(object):
provider can be used.
user_agent: string, User agent of your application, default to
None.
message: Message to display if there are problems with the OAuth 2.0
configuration. The message may contain HTML and will be
presented on the web interface for any method that uses
the decorator.
message: Message to display if there are problems with the
OAuth 2.0 configuration. The message may contain HTML and
will be presented on the web interface for any method that
uses the decorator.
callback_path: string, The absolute path to use as the callback
URI. Note that this must match up with the URI given
when registering the application in the APIs Console.
when registering the application in the APIs
Console.
token_response_param: string. If provided, the full JSON response
to the access token request will be encoded
and included in this query parameter in the
@@ -730,19 +734,21 @@ class OAuth2Decorator(object):
return
user = users.get_current_user()
# Don't use @login_decorator as this could be used in a POST request.
# Don't use @login_decorator as this could be used in a
# POST request.
if not user:
request_handler.redirect(users.create_login_url(
request_handler.request.uri))
request_handler.request.uri))
return
self._create_flow(request_handler)
# Store the request URI in 'state' so we can use it later
self.flow.params['state'] = _build_state_value(request_handler, user)
self.flow.params['state'] = _build_state_value(
request_handler, user)
self.credentials = self._storage_class(
self._credentials_class, None,
self._credentials_property_name, user=user).get()
self._credentials_class, None,
self._credentials_property_name, user=user).get()
if not self.has_credentials():
return request_handler.redirect(self.authorize_url())
@@ -768,14 +774,12 @@ class OAuth2Decorator(object):
"""
if self.flow is None:
redirect_uri = request_handler.request.relative_url(
self._callback_path) # Usually /oauth2callback
self.flow = OAuth2WebServerFlow(self._client_id, self._client_secret,
self._scope, redirect_uri=redirect_uri,
user_agent=self._user_agent,
auth_uri=self._auth_uri,
token_uri=self._token_uri,
revoke_uri=self._revoke_uri,
**self._kwargs)
self._callback_path) # Usually /oauth2callback
self.flow = OAuth2WebServerFlow(
self._client_id, self._client_secret, self._scope,
redirect_uri=redirect_uri, user_agent=self._user_agent,
auth_uri=self._auth_uri, token_uri=self._token_uri,
revoke_uri=self._revoke_uri, **self._kwargs)
def oauth_aware(self, method):
"""Decorator that sets up for OAuth 2.0 dance, but doesn't do it.
@@ -797,18 +801,20 @@ class OAuth2Decorator(object):
return
user = users.get_current_user()
# Don't use @login_decorator as this could be used in a POST request.
# Don't use @login_decorator as this could be used in a
# POST request.
if not user:
request_handler.redirect(users.create_login_url(
request_handler.request.uri))
request_handler.request.uri))
return
self._create_flow(request_handler)
self.flow.params['state'] = _build_state_value(request_handler, user)
self.flow.params['state'] = _build_state_value(request_handler,
user)
self.credentials = self._storage_class(
self._credentials_class, None,
self._credentials_property_name, user=user).get()
self._credentials_class, None,
self._credentials_property_name, user=user).get()
try:
resp = method(request_handler, *args, **kwargs)
finally:
@@ -885,21 +891,26 @@ class OAuth2Decorator(object):
if error:
errormsg = self.request.get('error_description', error)
self.response.out.write(
'The authorization request failed: %s' % _safe_html(errormsg))
'The authorization request failed: %s' %
_safe_html(errormsg))
else:
user = users.get_current_user()
decorator._create_flow(self)
credentials = decorator.flow.step2_exchange(self.request.params)
credentials = decorator.flow.step2_exchange(
self.request.params)
decorator._storage_class(
decorator._credentials_class, None,
decorator._credentials_property_name, user=user).put(credentials)
redirect_uri = _parse_state_value(str(self.request.get('state')),
user)
decorator._credentials_class, None,
decorator._credentials_property_name,
user=user).put(credentials)
redirect_uri = _parse_state_value(
str(self.request.get('state')), user)
if decorator._token_response_param and credentials.token_response:
if (decorator._token_response_param and
credentials.token_response):
resp_json = json.dumps(credentials.token_response)
redirect_uri = util._add_query_parameter(
redirect_uri, decorator._token_response_param, resp_json)
redirect_uri, decorator._token_response_param,
resp_json)
self.redirect(redirect_uri)
@@ -916,7 +927,7 @@ class OAuth2Decorator(object):
server during the OAuth 2.0 dance.
"""
return webapp.WSGIApplication([
(self.callback_path, self.callback_handler())
(self.callback_path, self.callback_handler())
])
@@ -959,23 +970,25 @@ class OAuth2DecoratorFromClientSecrets(OAuth2Decorator):
**kwargs: dict, Keyword arguments are passed along as kwargs to
the OAuth2WebServerFlow constructor.
"""
client_type, client_info = clientsecrets.loadfile(filename, cache=cache)
if client_type not in [
clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED]:
client_type, client_info = clientsecrets.loadfile(filename,
cache=cache)
if client_type not in (clientsecrets.TYPE_WEB,
clientsecrets.TYPE_INSTALLED):
raise InvalidClientSecretsError(
"OAuth2Decorator doesn't support this OAuth 2.0 flow.")
"OAuth2Decorator doesn't support this OAuth 2.0 flow.")
constructor_kwargs = dict(kwargs)
constructor_kwargs.update({
'auth_uri': client_info['auth_uri'],
'token_uri': client_info['token_uri'],
'message': message,
'auth_uri': client_info['auth_uri'],
'token_uri': client_info['token_uri'],
'message': message,
})
revoke_uri = client_info.get('revoke_uri')
if revoke_uri is not None:
constructor_kwargs['revoke_uri'] = revoke_uri
super(OAuth2DecoratorFromClientSecrets, self).__init__(
client_info['client_id'], client_info['client_secret'],
scope, **constructor_kwargs)
client_info['client_id'], client_info['client_secret'],
scope, **constructor_kwargs)
if message is not None:
self._message = message
else:
@@ -1001,4 +1014,4 @@ def oauth2decorator_from_clientsecrets(filename, scope,
Returns: An OAuth2Decorator
"""
return OAuth2DecoratorFromClientSecrets(filename, scope,
message=message, cache=cache)
message=message, cache=cache)

View File

@@ -93,12 +93,13 @@ _CLOUDSDK_CONFIG_ENV_VAR = 'CLOUDSDK_CONFIG'
# The error message we show users when we can't find the Application
# Default Credentials.
ADC_HELP_MSG = (
'The Application Default Credentials are not available. They are available '
'if running in Google Compute Engine. Otherwise, the environment variable '
+ GOOGLE_APPLICATION_CREDENTIALS +
'The Application Default Credentials are not available. They are '
'available if running in Google Compute Engine. Otherwise, the '
'environment variable ' +
GOOGLE_APPLICATION_CREDENTIALS +
' must be defined pointing to a file defining the credentials. See '
'https://developers.google.com/accounts/docs/application-default-credentials' # pylint:disable=line-too-long
' for more information.')
'https://developers.google.com/accounts/docs/'
'application-default-credentials for more information.')
# The access token along with the seconds in which it expires.
AccessTokenInfo = collections.namedtuple(
@@ -132,7 +133,7 @@ class TokenRevokeError(Error):
class UnknownClientSecretsFlowError(Error):
"""The client secrets file called for an unknown type of OAuth 2.0 flow. """
"""The client secrets file called for an unknown type of OAuth 2.0 flow."""
class AccessTokenCredentialsError(Error):
@@ -247,7 +248,7 @@ class Credentials(object):
if member in d:
del d[member]
if (d.get('token_expiry') and
isinstance(d['token_expiry'], datetime.datetime)):
isinstance(d['token_expiry'], datetime.datetime)):
d['token_expiry'] = d['token_expiry'].strftime(EXPIRY_FORMAT)
# Add in information we will need later to reconsistitue this instance.
d['_class'] = t.__name__
@@ -283,16 +284,19 @@ class Credentials(object):
"""
json_string_as_unicode = _from_bytes(s)
data = json.loads(json_string_as_unicode)
# Find and call the right classmethod from_json() to restore the object.
# Find and call the right classmethod from_json() to restore
# the object.
module_name = data['_module']
try:
module_obj = __import__(module_name)
except ImportError:
# In case there's an object from the old package structure, update it
# In case there's an object from the old package structure,
# update it
module_name = module_name.replace('.googleapiclient', '')
module_obj = __import__(module_name)
module_obj = __import__(module_name, fromlist=module_name.split('.')[:-1])
module_obj = __import__(module_name,
fromlist=module_name.split('.')[:-1])
kls = getattr(module_obj, data['_class'])
from_json = getattr(kls, 'from_json')
return from_json(json_string_as_unicode)
@@ -465,9 +469,9 @@ class OAuth2Credentials(Credentials):
@util.positional(8)
def __init__(self, access_token, client_id, client_secret, refresh_token,
token_expiry, token_uri, user_agent, revoke_uri=None,
id_token=None, token_response=None, scopes=None,
token_info_uri=None):
token_expiry, token_uri, user_agent, revoke_uri=None,
id_token=None, token_response=None, scopes=None,
token_info_uri=None):
"""Create an instance of OAuth2Credentials.
This constructor is not usually called by the user, instead
@@ -522,10 +526,9 @@ class OAuth2Credentials(Credentials):
The modified http.request method will add authentication headers to
each request and will refresh access_tokens when a 401 is received on a
request. In addition the http.request method has a credentials property,
http.request.credentials, which is the Credentials object that
authorized it.
request. In addition the http.request method has a credentials
property, http.request.credentials, which is the Credentials object
that authorized it.
Args:
http: An instance of ``httplib2.Http`` or something that acts
@@ -549,10 +552,11 @@ class OAuth2Credentials(Credentials):
# The closure that will replace 'httplib2.Http.request'.
def new_request(uri, method='GET', body=None, headers=None,
redirections=httplib2.DEFAULT_MAX_REDIRECTS,
connection_type=None):
redirections=httplib2.DEFAULT_MAX_REDIRECTS,
connection_type=None):
if not self.access_token:
logger.info('Attempting refresh to obtain initial access_token')
logger.info('Attempting refresh to obtain '
'initial access_token')
self._refresh(request_orig)
# Clone and modify the request headers to add the appropriate
@@ -565,33 +569,37 @@ class OAuth2Credentials(Credentials):
if self.user_agent is not None:
if 'user-agent' in headers:
headers['user-agent'] = self.user_agent + ' ' + headers['user-agent']
headers['user-agent'] = (self.user_agent + ' ' +
headers['user-agent'])
else:
headers['user-agent'] = self.user_agent
body_stream_position = None
if all(getattr(body, stream_prop, None) for stream_prop in
('read', 'seek', 'tell')):
if all(getattr(body, stream_prop, None) for stream_prop in
('read', 'seek', 'tell')):
body_stream_position = body.tell()
resp, content = request_orig(uri, method, body, clean_headers(headers),
redirections, connection_type)
resp, content = request_orig(uri, method, body,
clean_headers(headers),
redirections, connection_type)
# A stored token may expire between the time it is retrieved and the time
# the request is made, so we may need to try twice.
# A stored token may expire between the time it is retrieved and
# the time the request is made, so we may need to try twice.
max_refresh_attempts = 2
for refresh_attempt in range(max_refresh_attempts):
if resp.status not in REFRESH_STATUS_CODES:
break
logger.info('Refreshing due to a %s (attempt %s/%s)', resp.status,
refresh_attempt + 1, max_refresh_attempts)
logger.info('Refreshing due to a %s (attempt %s/%s)',
resp.status, refresh_attempt + 1,
max_refresh_attempts)
self._refresh(request_orig)
self.apply(headers)
if body_stream_position is not None:
body.seek(body_stream_position)
resp, content = request_orig(uri, method, body, clean_headers(headers),
redirections, connection_type)
resp, content = request_orig(uri, method, body,
clean_headers(headers),
redirections, connection_type)
return (resp, content)
@@ -681,25 +689,25 @@ class OAuth2Credentials(Credentials):
s = _from_bytes(s)
data = json.loads(s)
if (data.get('token_expiry') and
not isinstance(data['token_expiry'], datetime.datetime)):
not isinstance(data['token_expiry'], datetime.datetime)):
try:
data['token_expiry'] = datetime.datetime.strptime(
data['token_expiry'], EXPIRY_FORMAT)
data['token_expiry'], EXPIRY_FORMAT)
except ValueError:
data['token_expiry'] = None
retval = cls(
data['access_token'],
data['client_id'],
data['client_secret'],
data['refresh_token'],
data['token_expiry'],
data['token_uri'],
data['user_agent'],
revoke_uri=data.get('revoke_uri', None),
id_token=data.get('id_token', None),
token_response=data.get('token_response', None),
scopes=data.get('scopes', None),
token_info_uri=data.get('token_info_uri', None))
data['access_token'],
data['client_id'],
data['client_secret'],
data['refresh_token'],
data['token_expiry'],
data['token_uri'],
data['user_agent'],
revoke_uri=data.get('revoke_uri', None),
id_token=data.get('id_token', None),
token_response=data.get('token_response', None),
scopes=data.get('scopes', None),
token_info_uri=data.get('token_info_uri', None))
retval.invalid = data['invalid']
return retval
@@ -718,7 +726,7 @@ class OAuth2Credentials(Credentials):
now = datetime.datetime.utcnow()
if now >= self.token_expiry:
logger.info('access_token is expired. Now: %s, token_expiry: %s',
now, self.token_expiry)
now, self.token_expiry)
return True
return False
@@ -733,7 +741,7 @@ class OAuth2Credentials(Credentials):
http = httplib2.Http()
self.refresh(http)
return AccessTokenInfo(access_token=self.access_token,
expires_in=self._expires_in())
expires_in=self._expires_in())
def set_store(self, store):
"""Set the Storage for the credential.
@@ -785,17 +793,17 @@ class OAuth2Credentials(Credentials):
def _generate_refresh_request_body(self):
"""Generate the body that will be used in the refresh request."""
body = urllib.parse.urlencode({
'grant_type': 'refresh_token',
'client_id': self.client_id,
'client_secret': self.client_secret,
'refresh_token': self.refresh_token,
'grant_type': 'refresh_token',
'client_id': self.client_id,
'client_secret': self.client_secret,
'refresh_token': self.refresh_token,
})
return body
def _generate_refresh_request_headers(self):
"""Generate the headers that will be used in the refresh request."""
headers = {
'content-type': 'application/x-www-form-urlencoded',
'content-type': 'application/x-www-form-urlencoded',
}
if self.user_agent is not None:
@@ -826,8 +834,8 @@ class OAuth2Credentials(Credentials):
new_cred = self.store.locked_get()
if (new_cred and not new_cred.invalid and
new_cred.access_token != self.access_token and
not new_cred.access_token_expired):
new_cred.access_token != self.access_token and
not new_cred.access_token_expired):
logger.info('Updated access_token read from Storage')
self._updateFromCredential(new_cred)
else:
@@ -851,7 +859,7 @@ class OAuth2Credentials(Credentials):
logger.info('Refreshing access_token')
resp, content = http_request(
self.token_uri, method='POST', body=body, headers=headers)
self.token_uri, method='POST', body=body, headers=headers)
content = _from_bytes(content)
if resp.status == 200:
d = json.loads(content)
@@ -860,7 +868,7 @@ class OAuth2Credentials(Credentials):
self.refresh_token = d.get('refresh_token', self.refresh_token)
if 'expires_in' in d:
self.token_expiry = datetime.timedelta(
seconds=int(d['expires_in'])) + datetime.datetime.utcnow()
seconds=int(d['expires_in'])) + datetime.datetime.utcnow()
else:
self.token_expiry = None
# On temporary refresh errors, the user does not actually have to
@@ -869,8 +877,8 @@ class OAuth2Credentials(Credentials):
if self.store:
self.store.locked_put(self)
else:
# An {'error':...} response body means the token is expired or revoked,
# so we flag the credentials as such.
# An {'error':...} response body means the token is expired or
# revoked, so we flag the credentials as such.
logger.info('Failed to retrieve access token: %s', content)
error_msg = 'Invalid response %s.' % resp['status']
try:
@@ -955,14 +963,15 @@ class OAuth2Credentials(Credentials):
"""
logger.info('Refreshing scopes')
query_params = {'access_token': token, 'fields': 'scope'}
token_info_uri = _update_query_params(self.token_info_uri, query_params)
token_info_uri = _update_query_params(self.token_info_uri,
query_params)
resp, content = http_request(token_info_uri)
content = _from_bytes(content)
if resp.status == 200:
d = json.loads(content)
self.scopes = set(util.string_to_scopes(d.get('scope', '')))
else:
error_msg = 'Invalid response %s.' % (resp.status, )
error_msg = 'Invalid response %s.' % (resp.status,)
try:
d = json.loads(content)
if 'error_description' in d:
@@ -1012,26 +1021,26 @@ class AccessTokenCredentials(OAuth2Credentials):
token can't be revoked if this is None.
"""
super(AccessTokenCredentials, self).__init__(
access_token,
None,
None,
None,
None,
None,
user_agent,
revoke_uri=revoke_uri)
access_token,
None,
None,
None,
None,
None,
user_agent,
revoke_uri=revoke_uri)
@classmethod
def from_json(cls, s):
data = json.loads(_from_bytes(s))
retval = AccessTokenCredentials(
data['access_token'],
data['user_agent'])
data['access_token'],
data['user_agent'])
return retval
def _refresh(self, http_request):
raise AccessTokenCredentialsError(
'The access_token is expired or invalid and can\'t be refreshed.')
'The access_token is expired or invalid and can\'t be refreshed.')
def _revoke(self, http_request):
"""Revokes the access_token and deletes the store if available.
@@ -1143,8 +1152,8 @@ class GoogleCredentials(OAuth2Credentials):
"""
def __init__(self, access_token, client_id, client_secret, refresh_token,
token_expiry, token_uri, user_agent,
revoke_uri=GOOGLE_REVOKE_URI):
token_expiry, token_uri, user_agent,
revoke_uri=GOOGLE_REVOKE_URI):
"""Create an instance of GoogleCredentials.
This constructor is not usually called by the user, instead
@@ -1166,8 +1175,8 @@ class GoogleCredentials(OAuth2Credentials):
is None.
"""
super(GoogleCredentials, self).__init__(
access_token, client_id, client_secret, refresh_token, token_expiry,
token_uri, user_agent, revoke_uri=revoke_uri)
access_token, client_id, client_secret, refresh_token,
token_expiry, token_uri, user_agent, revoke_uri=revoke_uri)
def create_scoped_required(self):
"""Whether this Credentials object is scopeless.
@@ -1188,10 +1197,10 @@ class GoogleCredentials(OAuth2Credentials):
def serialization_data(self):
"""Get the fields and values identifying the current credentials."""
return {
'type': 'authorized_user',
'client_id': self.client_id,
'client_secret': self.client_secret,
'refresh_token': self.refresh_token
'type': 'authorized_user',
'client_id': self.client_id,
'client_secret': self.client_secret,
'refresh_token': self.refresh_token
}
@staticmethod
@@ -1247,27 +1256,29 @@ class GoogleCredentials(OAuth2Credentials):
credentials_filename = _get_well_known_file()
if os.path.isfile(credentials_filename):
extra_help = (' (produced automatically when running'
' "gcloud auth login" command)')
' "gcloud auth login" command)')
else:
credentials_filename = None
else:
extra_help = (' (pointed to by ' + GOOGLE_APPLICATION_CREDENTIALS +
' environment variable)')
' environment variable)')
if not credentials_filename:
return
# If we can read the credentials from a file, we don't need to know what
# environment we are in.
# If we can read the credentials from a file, we don't need to know
# what environment we are in.
SETTINGS.env_name = DEFAULT_ENV_NAME
try:
return _get_application_default_credential_from_file(credentials_filename)
return _get_application_default_credential_from_file(
credentials_filename)
except (ApplicationDefaultCredentialsError, ValueError) as error:
_raise_exception_for_reading_json(credentials_filename, extra_help, error)
_raise_exception_for_reading_json(credentials_filename,
extra_help, error)
@classmethod
def _get_implicit_credentials(cls):
def _get_implicit_credentials(cls):
"""Gets credentials implicitly from the environment.
Checks environment in order of precedence:
@@ -1283,9 +1294,9 @@ class GoogleCredentials(OAuth2Credentials):
"""
# Environ checks (in order).
environ_checkers = [
cls._implicit_credentials_from_gae,
cls._implicit_credentials_from_files,
cls._implicit_credentials_from_gce,
cls._implicit_credentials_from_gae,
cls._implicit_credentials_from_files,
cls._implicit_credentials_from_gce,
]
for checker in environ_checkers:
@@ -1323,16 +1334,17 @@ class GoogleCredentials(OAuth2Credentials):
if credential_filename and os.path.isfile(credential_filename):
try:
return _get_application_default_credential_from_file(
credential_filename)
credential_filename)
except (ApplicationDefaultCredentialsError, ValueError) as error:
extra_help = ' (provided as parameter to the from_stream() method)'
extra_help = (' (provided as parameter to the '
'from_stream() method)')
_raise_exception_for_reading_json(credential_filename,
extra_help,
error)
extra_help,
error)
else:
raise ApplicationDefaultCredentialsError(
'The parameter passed to the from_stream() '
'method should point to a file.')
'The parameter passed to the from_stream() '
'method should point to a file.')
def _save_private_file(filename, json_contents):
@@ -1346,7 +1358,7 @@ def _save_private_file(filename, json_contents):
file_desc = os.open(temp_filename, os.O_WRONLY | os.O_CREAT, 0o600)
with os.fdopen(file_desc, 'w') as file_handle:
json.dump(json_contents, file_handle, sort_keys=True,
indent=2, separators=(',', ': '))
indent=2, separators=(',', ': '))
shutil.move(temp_filename, filename)
@@ -1384,9 +1396,10 @@ def _get_environment_variable_file():
return application_default_credential_filename
else:
raise ApplicationDefaultCredentialsError(
'File ' + application_default_credential_filename + ' (pointed by ' +
GOOGLE_APPLICATION_CREDENTIALS +
' environment variable) does not exist!')
'File ' + application_default_credential_filename +
' (pointed by ' +
GOOGLE_APPLICATION_CREDENTIALS +
' environment variable) does not exist!')
def _get_well_known_file():
@@ -1401,16 +1414,17 @@ def _get_well_known_file():
if os.name == 'nt':
try:
default_config_dir = os.path.join(os.environ['APPDATA'],
_CLOUDSDK_CONFIG_DIRECTORY)
_CLOUDSDK_CONFIG_DIRECTORY)
except KeyError:
# This should never happen unless someone is really messing with things.
# This should never happen unless someone is really
# messing with things.
drive = os.environ.get('SystemDrive', 'C:')
default_config_dir = os.path.join(drive, '\\',
_CLOUDSDK_CONFIG_DIRECTORY)
_CLOUDSDK_CONFIG_DIRECTORY)
else:
default_config_dir = os.path.join(os.path.expanduser('~'),
'.config',
_CLOUDSDK_CONFIG_DIRECTORY)
'.config',
_CLOUDSDK_CONFIG_DIRECTORY)
return os.path.join(default_config_dir, WELL_KNOWN_CREDENTIALS_FILE)
@@ -1429,11 +1443,11 @@ def _get_application_default_credential_from_file(filename):
required_fields = set(['client_id', 'client_secret', 'refresh_token'])
elif credentials_type == SERVICE_ACCOUNT:
required_fields = set(['client_id', 'client_email', 'private_key_id',
'private_key'])
'private_key'])
else:
raise ApplicationDefaultCredentialsError(
"'type' field should be defined (and have one of the '" +
AUTHORIZED_USER + "' or '" + SERVICE_ACCOUNT + "' values)")
"'type' field should be defined (and have one of the '" +
AUTHORIZED_USER + "' or '" + SERVICE_ACCOUNT + "' values)")
missing_fields = required_fields.difference(client_credentials.keys())
@@ -1442,25 +1456,25 @@ def _get_application_default_credential_from_file(filename):
if client_credentials['type'] == AUTHORIZED_USER:
return GoogleCredentials(
access_token=None,
client_id=client_credentials['client_id'],
client_secret=client_credentials['client_secret'],
refresh_token=client_credentials['refresh_token'],
token_expiry=None,
token_uri=GOOGLE_TOKEN_URI,
user_agent='Python client library')
access_token=None,
client_id=client_credentials['client_id'],
client_secret=client_credentials['client_secret'],
refresh_token=client_credentials['refresh_token'],
token_expiry=None,
token_uri=GOOGLE_TOKEN_URI,
user_agent='Python client library')
else: # client_credentials['type'] == SERVICE_ACCOUNT
return service_account._ServiceAccountCredentials(
service_account_id=client_credentials['client_id'],
service_account_email=client_credentials['client_email'],
private_key_id=client_credentials['private_key_id'],
private_key_pkcs8_text=client_credentials['private_key'],
scopes=[])
service_account_id=client_credentials['client_id'],
service_account_email=client_credentials['client_email'],
private_key_id=client_credentials['private_key_id'],
private_key_pkcs8_text=client_credentials['private_key'],
scopes=[])
def _raise_exception_for_missing_fields(missing_fields):
raise ApplicationDefaultCredentialsError(
'The following field(s) must be defined: ' + ', '.join(missing_fields))
'The following field(s) must be defined: ' + ', '.join(missing_fields))
def _raise_exception_for_reading_json(credential_file,
@@ -1496,9 +1510,9 @@ class AssertionCredentials(GoogleCredentials):
@util.positional(2)
def __init__(self, assertion_type, user_agent=None,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
**unused_kwargs):
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
**unused_kwargs):
"""Constructor for AssertionFlowCredentials.
Args:
@@ -1512,22 +1526,22 @@ class AssertionCredentials(GoogleCredentials):
revoke_uri: string, URI for revoke endpoint.
"""
super(AssertionCredentials, self).__init__(
None,
None,
None,
None,
None,
token_uri,
user_agent,
revoke_uri=revoke_uri)
None,
None,
None,
None,
None,
token_uri,
user_agent,
revoke_uri=revoke_uri)
self.assertion_type = assertion_type
def _generate_refresh_request_body(self):
assertion = self._generate_assertion()
body = urllib.parse.urlencode({
'assertion': assertion,
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': assertion,
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
})
return body
@@ -1574,14 +1588,14 @@ class SignedJwtAssertionCredentials(AssertionCredentials):
@util.positional(4)
def __init__(self,
service_account_name,
private_key,
scope,
private_key_password='notasecret',
user_agent=None,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
**kwargs):
service_account_name,
private_key,
scope,
private_key_password='notasecret',
user_agent=None,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
**kwargs):
"""Constructor for SignedJwtAssertionCredentials.
Args:
@@ -1606,10 +1620,10 @@ class SignedJwtAssertionCredentials(AssertionCredentials):
"""
_RequireCryptoOrDie()
super(SignedJwtAssertionCredentials, self).__init__(
None,
user_agent=user_agent,
token_uri=token_uri,
revoke_uri=revoke_uri,
None,
user_agent=user_agent,
token_uri=token_uri,
revoke_uri=revoke_uri,
)
self.scope = util.scopes_to_string(scope)
@@ -1625,13 +1639,13 @@ class SignedJwtAssertionCredentials(AssertionCredentials):
def from_json(cls, s):
data = json.loads(_from_bytes(s))
retval = SignedJwtAssertionCredentials(
data['service_account_name'],
base64.b64decode(data['private_key']),
data['scope'],
private_key_password=data['private_key_password'],
user_agent=data['user_agent'],
token_uri=data['token_uri'],
**data['kwargs']
data['service_account_name'],
base64.b64decode(data['private_key']),
data['scope'],
private_key_password=data['private_key_password'],
user_agent=data['user_agent'],
token_uri=data['token_uri'],
**data['kwargs']
)
retval.invalid = data['invalid']
retval.access_token = data['access_token']
@@ -1641,18 +1655,18 @@ class SignedJwtAssertionCredentials(AssertionCredentials):
"""Generate the assertion that will be used in the request."""
now = int(time.time())
payload = {
'aud': self.token_uri,
'scope': self.scope,
'iat': now,
'exp': now + SignedJwtAssertionCredentials.MAX_TOKEN_LIFETIME_SECS,
'iss': self.service_account_name
'aud': self.token_uri,
'scope': self.scope,
'iat': now,
'exp': now + SignedJwtAssertionCredentials.MAX_TOKEN_LIFETIME_SECS,
'iss': self.service_account_name
}
payload.update(self.kwargs)
logger.debug(str(payload))
private_key = base64.b64decode(self.private_key)
return crypt.make_signed_jwt(crypt.Signer.from_string(
private_key, self.private_key_password), payload)
private_key, self.private_key_password), payload)
# Only used in verify_id_token(), which is always calling to the same URI
# for the certs.
@@ -1712,7 +1726,7 @@ def _extract_id_token(id_token):
if len(segments) != 3:
raise VerifyJwtTokenError(
'Wrong number of segments in token: %s' % id_token)
'Wrong number of segments in token: %s' % id_token)
return json.loads(_from_bytes(_urlsafe_b64decode(segments[1])))
@@ -1786,10 +1800,11 @@ def credentials_from_code(client_id, client_secret, scope, code,
access token
"""
flow = OAuth2WebServerFlow(client_id, client_secret, scope,
redirect_uri=redirect_uri, user_agent=user_agent,
auth_uri=auth_uri, token_uri=token_uri,
revoke_uri=revoke_uri, device_uri=device_uri,
token_info_uri=token_info_uri)
redirect_uri=redirect_uri,
user_agent=user_agent, auth_uri=auth_uri,
token_uri=token_uri, revoke_uri=revoke_uri,
device_uri=device_uri,
token_info_uri=token_info_uri)
credentials = flow.step2_exchange(code, http=http)
return credentials
@@ -1836,16 +1851,16 @@ def credentials_from_clientsecrets_and_code(filename, scope, code,
clientsecrets.InvalidClientSecretsError: if the clientsecrets file is
invalid.
"""
flow = flow_from_clientsecrets(filename, scope, message=message, cache=cache,
redirect_uri=redirect_uri,
device_uri=device_uri)
flow = flow_from_clientsecrets(filename, scope, message=message,
cache=cache, redirect_uri=redirect_uri,
device_uri=device_uri)
credentials = flow.step2_exchange(code, http=http)
return credentials
class DeviceFlowInfo(collections.namedtuple('DeviceFlowInfo', (
'device_code', 'user_code', 'interval', 'verification_url',
'user_code_expiry'))):
'device_code', 'user_code', 'interval', 'verification_url',
'user_code_expiry'))):
"""Intermediate information the OAuth2 for devices flow."""
@classmethod
@@ -1858,26 +1873,26 @@ class DeviceFlowInfo(collections.namedtuple('DeviceFlowInfo', (
"""
# device_code, user_code, and verification_url are required.
kwargs = {
'device_code': response['device_code'],
'user_code': response['user_code'],
'device_code': response['device_code'],
'user_code': response['user_code'],
}
# The response may list the verification address as either
# verification_url or verification_uri, so we check for both.
verification_url = response.get(
'verification_url', response.get('verification_uri'))
'verification_url', response.get('verification_uri'))
if verification_url is None:
raise OAuth2DeviceCodeError(
'No verification_url provided in server response')
'No verification_url provided in server response')
kwargs['verification_url'] = verification_url
# expires_in and interval are optional.
kwargs.update({
'interval': response.get('interval'),
'user_code_expiry': None,
'interval': response.get('interval'),
'user_code_expiry': None,
})
if 'expires_in' in response:
kwargs['user_code_expiry'] = datetime.datetime.now() + datetime.timedelta(
seconds=int(response['expires_in']))
kwargs['user_code_expiry'] = (
datetime.datetime.now() +
datetime.timedelta(seconds=int(response['expires_in'])))
return cls(**kwargs)
@@ -1889,18 +1904,18 @@ class OAuth2WebServerFlow(Flow):
@util.positional(4)
def __init__(self, client_id,
client_secret=None,
scope=None,
redirect_uri=None,
user_agent=None,
auth_uri=GOOGLE_AUTH_URI,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
login_hint=None,
device_uri=GOOGLE_DEVICE_URI,
token_info_uri=GOOGLE_TOKEN_INFO_URI,
authorization_header=None,
**kwargs):
client_secret=None,
scope=None,
redirect_uri=None,
user_agent=None,
auth_uri=GOOGLE_AUTH_URI,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
login_hint=None,
device_uri=GOOGLE_DEVICE_URI,
token_info_uri=GOOGLE_TOKEN_INFO_URI,
authorization_header=None,
**kwargs):
"""Constructor for OAuth2WebServerFlow.
The kwargs argument is used to set extra query parameters on the
@@ -1915,7 +1930,8 @@ class OAuth2WebServerFlow(Flow):
redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob'
for a non-web-based application, or a URI that
handles the callback from the authorization server.
user_agent: string, HTTP User-Agent to provide for this application.
user_agent: string, HTTP User-Agent to provide for this
application.
auth_uri: string, URI for authorization endpoint. For convenience
defaults to Google's endpoints but any OAuth 2.0 provider
can be used.
@@ -1956,8 +1972,8 @@ class OAuth2WebServerFlow(Flow):
self.token_info_uri = token_info_uri
self.authorization_header = authorization_header
self.params = {
'access_type': 'offline',
'response_type': 'code',
'access_type': 'offline',
'response_type': 'code',
}
self.params.update(kwargs)
@@ -1981,18 +1997,19 @@ class OAuth2WebServerFlow(Flow):
"""
if redirect_uri is not None:
logger.warning((
'The redirect_uri parameter for '
'OAuth2WebServerFlow.step1_get_authorize_url is deprecated. Please '
'move to passing the redirect_uri in via the constructor.'))
'The redirect_uri parameter for '
'OAuth2WebServerFlow.step1_get_authorize_url is deprecated. '
'Please move to passing the redirect_uri in via the '
'constructor.'))
self.redirect_uri = redirect_uri
if self.redirect_uri is None:
raise ValueError('The value of redirect_uri must not be None.')
query_params = {
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'scope': self.scope,
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'scope': self.scope,
}
if state is not None:
query_params['state'] = state
@@ -2013,11 +2030,11 @@ class OAuth2WebServerFlow(Flow):
raise ValueError('The value of device_uri must not be None.')
body = urllib.parse.urlencode({
'client_id': self.client_id,
'scope': self.scope,
'client_id': self.client_id,
'scope': self.scope,
})
headers = {
'content-type': 'application/x-www-form-urlencoded',
'content-type': 'application/x-www-form-urlencoded',
}
if self.user_agent is not None:
@@ -2027,15 +2044,15 @@ class OAuth2WebServerFlow(Flow):
http = httplib2.Http()
resp, content = http.request(self.device_uri, method='POST', body=body,
headers=headers)
headers=headers)
content = _from_bytes(content)
if resp.status == 200:
try:
flow_info = json.loads(content)
except ValueError as e:
raise OAuth2DeviceCodeError(
'Could not parse server response as JSON: "%s", error: "%s"' % (
content, e))
'Could not parse server response as JSON: "%s", '
'error: "%s"' % (content, e))
return DeviceFlowInfo.FromResponse(flow_info)
else:
error_msg = 'Invalid response %s.' % resp.status
@@ -2044,12 +2061,13 @@ class OAuth2WebServerFlow(Flow):
if 'error' in d:
error_msg += ' Error: %s' % d['error']
except ValueError:
# Couldn't decode a JSON response, stick with the default message.
# Couldn't decode a JSON response, stick with the
# default message.
pass
raise OAuth2DeviceCodeError(error_msg)
@util.positional(2)
def step2_exchange(self, code=None, http=None, device_flow_info=None):
def step2_exchange(self, code=None, http=None, device_flow_info=None):
"""Exchanges a code for OAuth2Credentials.
Args:
@@ -2081,13 +2099,13 @@ class OAuth2WebServerFlow(Flow):
elif not isinstance(code, six.string_types):
if 'code' not in code:
raise FlowExchangeError(code.get(
'error', 'No code was supplied in the query parameters.'))
'error', 'No code was supplied in the query parameters.'))
code = code['code']
post_data = {
'client_id': self.client_id,
'code': code,
'scope': self.scope,
'client_id': self.client_id,
'code': code,
'scope': self.scope,
}
if self.client_secret is not None:
post_data['client_secret'] = self.client_secret
@@ -2098,7 +2116,7 @@ class OAuth2WebServerFlow(Flow):
post_data['redirect_uri'] = self.redirect_uri
body = urllib.parse.urlencode(post_data)
headers = {
'content-type': 'application/x-www-form-urlencoded',
'content-type': 'application/x-www-form-urlencoded',
}
if self.authorization_header is not None:
headers['Authorization'] = self.authorization_header
@@ -2109,38 +2127,38 @@ class OAuth2WebServerFlow(Flow):
http = httplib2.Http()
resp, content = http.request(self.token_uri, method='POST', body=body,
headers=headers)
headers=headers)
d = _parse_exchange_token_response(content)
if resp.status == 200 and 'access_token' in d:
access_token = d['access_token']
refresh_token = d.get('refresh_token', None)
if not refresh_token:
logger.info(
'Received token response with no refresh_token. Consider '
"reauthenticating with approval_prompt='force'.")
'Received token response with no refresh_token. Consider '
"reauthenticating with approval_prompt='force'.")
token_expiry = None
if 'expires_in' in d:
token_expiry = datetime.datetime.utcnow() + datetime.timedelta(
seconds=int(d['expires_in']))
token_expiry = (
datetime.datetime.utcnow() +
datetime.timedelta(seconds=int(d['expires_in'])))
extracted_id_token = None
if 'id_token' in d:
extracted_id_token = _extract_id_token(d['id_token'])
logger.info('Successfully retrieved access token')
return OAuth2Credentials(access_token, self.client_id,
self.client_secret, refresh_token, token_expiry,
self.token_uri, self.user_agent,
revoke_uri=self.revoke_uri,
id_token=extracted_id_token,
token_response=d,
scopes=self.scope,
token_info_uri=self.token_info_uri)
return OAuth2Credentials(
access_token, self.client_id, self.client_secret,
refresh_token, token_expiry, self.token_uri, self.user_agent,
revoke_uri=self.revoke_uri, id_token=extracted_id_token,
token_response=d, scopes=self.scope,
token_info_uri=self.token_info_uri)
else:
logger.info('Failed to retrieve access token: %s', content)
if 'error' in d:
# you never know what those providers got to say
error_msg = str(d['error']) + str(d.get('error_description', ''))
error_msg = (str(d['error']) +
str(d.get('error_description', '')))
else:
error_msg = 'Invalid response: %s.' % str(resp.status)
raise FlowExchangeError(error_msg)
@@ -2187,13 +2205,15 @@ def flow_from_clientsecrets(filename, scope, redirect_uri=None,
invalid.
"""
try:
client_type, client_info = clientsecrets.loadfile(filename, cache=cache)
if client_type in (clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED):
client_type, client_info = clientsecrets.loadfile(filename,
cache=cache)
if client_type in (clientsecrets.TYPE_WEB,
clientsecrets.TYPE_INSTALLED):
constructor_kwargs = {
'redirect_uri': redirect_uri,
'auth_uri': client_info['auth_uri'],
'token_uri': client_info['token_uri'],
'login_hint': login_hint,
'redirect_uri': redirect_uri,
'auth_uri': client_info['auth_uri'],
'token_uri': client_info['token_uri'],
'login_hint': login_hint,
}
revoke_uri = client_info.get('revoke_uri')
if revoke_uri is not None:
@@ -2201,8 +2221,8 @@ def flow_from_clientsecrets(filename, scope, redirect_uri=None,
if device_uri is not None:
constructor_kwargs['device_uri'] = device_uri
return OAuth2WebServerFlow(
client_info['client_id'], client_info['client_secret'],
scope, **constructor_kwargs)
client_info['client_id'], client_info['client_secret'],
scope, **constructor_kwargs)
except clientsecrets.InvalidClientSecretsError:
if message:
@@ -2211,4 +2231,4 @@ def flow_from_clientsecrets(filename, scope, redirect_uri=None,
raise
else:
raise UnknownClientSecretsFlowError(
'This OAuth 2.0 flow is unsupported: %r' % client_type)
'This OAuth 2.0 flow is unsupported: %r' % client_type)

View File

@@ -70,30 +70,31 @@ class InvalidClientSecretsError(Error):
def _validate_clientsecrets(obj):
_INVALID_FILE_FORMAT_MSG = (
'Invalid file format. See '
'https://developers.google.com/api-client-library/'
'python/guide/aaa_client_secrets')
'Invalid file format. See '
'https://developers.google.com/api-client-library/'
'python/guide/aaa_client_secrets')
if obj is None:
raise InvalidClientSecretsError(_INVALID_FILE_FORMAT_MSG)
if len(obj) != 1:
raise InvalidClientSecretsError(
_INVALID_FILE_FORMAT_MSG + ' '
'Expected a JSON object with a single property for a "web" or '
'"installed" application')
_INVALID_FILE_FORMAT_MSG + ' '
'Expected a JSON object with a single property for a "web" or '
'"installed" application')
client_type = tuple(obj)[0]
if client_type not in VALID_CLIENT:
raise InvalidClientSecretsError('Unknown client type: %s.' % (client_type, ))
raise InvalidClientSecretsError(
'Unknown client type: %s.' % (client_type,))
client_info = obj[client_type]
for prop_name in VALID_CLIENT[client_type]['required']:
if prop_name not in client_info:
raise InvalidClientSecretsError(
'Missing property "%s" in a client type of "%s".' % (prop_name,
client_type))
'Missing property "%s" in a client type of "%s".' %
(prop_name, client_type))
for prop_name in VALID_CLIENT[client_type]['string']:
if client_info[prop_name].startswith('[['):
raise InvalidClientSecretsError(
'Property "%s" is not configured.' % prop_name)
'Property "%s" is not configured.' % prop_name)
return client_type, client_info

View File

@@ -45,11 +45,9 @@ except ImportError:
OpenSSLVerifier = None
OpenSSLSigner = None
def pkcs12_key_as_pem(*args, **kwargs):
raise NotImplementedError('pkcs12_key_as_pem requires OpenSSL.')
try:
from oauth2client._pycrypto_crypt import PyCryptoVerifier
from oauth2client._pycrypto_crypt import PyCryptoSigner
@@ -66,7 +64,7 @@ elif PyCryptoSigner:
Verifier = PyCryptoVerifier
else:
raise ImportError('No encryption library found. Please install either '
'PyOpenSSL, or PyCrypto 2.6 or later')
'PyOpenSSL, or PyCrypto 2.6 or later')
def make_signed_jwt(signer, payload):
@@ -157,10 +155,10 @@ def verify_signed_jwt_with_certs(jwt, certs, audience):
if now < earliest:
raise AppIdentityError('Token used too early, %d < %d: %s' %
(now, earliest, json_body))
(now, earliest, json_body))
if now > latest:
raise AppIdentityError('Token used too late, %d > %d: %s' %
(now, latest, json_body))
(now, latest, json_body))
# Check audience.
if audience is not None:
@@ -169,6 +167,6 @@ def verify_signed_jwt_with_certs(jwt, certs, audience):
raise AppIdentityError('No aud field in token: %s' % json_body)
if aud != audience:
raise AppIdentityError('Wrong recipient, %s != %s: %s' %
(aud, audience, json_body))
(aud, audience, json_body))
return parsed

View File

@@ -35,8 +35,9 @@ class CommunicationError(Error):
class NoDevshellServer(Error):
"""Error when no Developer Shell server can be contacted."""
# The request for credential information to the Developer Shell client socket is
# always an empty PBLite-formatted JSON object, so just define it as a constant.
# The request for credential information to the Developer Shell client socket
# is always an empty PBLite-formatted JSON object, so just define it as a
# constant.
CREDENTIAL_INFO_REQUEST_JSON = '[]'
@@ -44,7 +45,9 @@ class CredentialInfoResponse(object):
"""Credential information response from Developer Shell server.
The credential information response from Developer Shell socket is a
PBLite-formatted JSON array with fields encoded by their index in the array:
PBLite-formatted JSON array with fields encoded by their index in the
array:
* Index 0 - user email
* Index 1 - default project ID. None if the project context is not known.
* Index 2 - OAuth2 access token. None if there is no valid auth context.
@@ -103,13 +106,13 @@ class DevshellCredentials(client.GoogleCredentials):
def __init__(self, user_agent=None):
super(DevshellCredentials, self).__init__(
None, # access_token, initialized below
None, # client_id
None, # client_secret
None, # refresh_token
None, # token_expiry
None, # token_uri
user_agent)
None, # access_token, initialized below
None, # client_id
None, # client_secret
None, # refresh_token
None, # token_expiry
None, # token_uri
user_agent)
self._refresh(None)
def _refresh(self, http_request):
@@ -127,9 +130,9 @@ class DevshellCredentials(client.GoogleCredentials):
@classmethod
def from_json(cls, json_data):
raise NotImplementedError(
'Cannot load Developer Shell credentials from JSON.')
'Cannot load Developer Shell credentials from JSON.')
@property
def serialization_data(self):
raise NotImplementedError(
'Cannot serialize Developer Shell credentials.')
'Cannot serialize Developer Shell credentials.')

View File

@@ -93,7 +93,8 @@ class Storage(BaseStorage):
Args:
model: db.Model, model class
key_name: string, key name for the entity that has the credentials
key_value: string, key value for the entity that has the credentials
key_value: string, key value for the entity that has the
credentials
property_name: string, name of the property that is an
CredentialsProperty
"""
@@ -124,12 +125,14 @@ class Storage(BaseStorage):
Args:
credentials: Credentials, the credentials to store.
overwrite: Boolean, indicates whether you would like these
credentials to overwrite any existing stored credentials.
credentials to overwrite any existing stored
credentials.
"""
args = {self.key_name: self.key_value}
if overwrite:
entity, unused_is_new = self.model_class.objects.get_or_create(**args)
(entity,
unused_is_new) = self.model_class.objects.get_or_create(**args)
else:
entity = self.model_class(**args)

View File

@@ -42,7 +42,7 @@ class Storage(BaseStorage):
def _validate_file(self):
if os.path.islink(self._filename):
raise CredentialsFileSymbolicLinkError(
'File: %s is a symbolic link.' % self._filename)
'File: %s is a symbolic link.' % self._filename)
def acquire_lock(self):
"""Acquires any lock necessary to access this Storage.

View File

@@ -46,8 +46,8 @@ apiui/credential>`__.
Usage
=====
Once configured, you can use the :meth:`UserOAuth2.required` decorator to ensure
that credentials are available within a view.
Once configured, you can use the :meth:`UserOAuth2.required` decorator to
ensure that credentials are available within a view.
.. code-block:: python
:emphasize-lines: 3,7,10
@@ -190,7 +190,7 @@ from oauth2client import util
__author__ = 'jonwayne@google.com (Jon Wayne Parrott)'
DEFAULT_SCOPES = ('email', )
DEFAULT_SCOPES = ('email',)
class UserOAuth2(object):
@@ -291,8 +291,9 @@ class UserOAuth2(object):
raise ValueError(
'OAuth2 configuration could not be found. Either specify the '
'client_secrets_file or client_id and client_secret or set the'
'app configuration variables GOOGLE_OAUTH2_CLIENT_SECRETS_FILE '
'or GOOGLE_OAUTH2_CLIENT_ID and GOOGLE_OAUTH2_CLIENT_SECRET.')
'app configuration variables '
'GOOGLE_OAUTH2_CLIENT_SECRETS_FILE or '
'GOOGLE_OAUTH2_CLIENT_ID and GOOGLE_OAUTH2_CLIENT_SECRET.')
def _load_client_secrets(self, filename):
"""Loads client secrets from the given filename."""
@@ -392,7 +393,8 @@ class UserOAuth2(object):
credentials = flow.step2_exchange(code)
except FlowExchangeError as exchange_error:
current_app.logger.exception(exchange_error)
return 'An error occurred: %s' % exchange_error, httplib.BAD_REQUEST
content = 'An error occurred: %s' % (exchange_error,)
return content, httplib.BAD_REQUEST
# Save the credentials to the storage.
self.storage.put(credentials)
@@ -420,9 +422,9 @@ class UserOAuth2(object):
def email(self):
"""Returns the user's email address or None if there are no credentials.
The email address is provided by the current credentials' id_token. This
should not be used as unique identifier as the user can change their
email. If you need a unique identifier, use user_id.
The email address is provided by the current credentials' id_token.
This should not be used as unique identifier as the user can change
their email. If you need a unique identifier, use user_id.
"""
if not self.credentials:
return None
@@ -451,8 +453,9 @@ class UserOAuth2(object):
def authorize_url(self, return_url, **kwargs):
"""Creates a URL that can be used to start the authorization flow.
When the user is directed to the URL, the authorization flow will begin.
Once complete, the user will be redirected to the specified return URL.
When the user is directed to the URL, the authorization flow will
begin. Once complete, the user will be redirected to the specified
return URL.
Any kwargs are passed into the flow constructor.
"""

View File

@@ -44,9 +44,9 @@ class AppAssertionCredentials(AssertionCredentials):
used for the purpose of accessing data stored under an account assigned to
the Compute Engine instance itself.
This credential does not require a flow to instantiate because it represents
a two legged flow, and therefore has all of the required information to
generate and refresh its own access tokens.
This credential does not require a flow to instantiate because it
represents a two legged flow, and therefore has all of the required
information to generate and refresh its own access tokens.
"""
@util.positional(2)
@@ -60,7 +60,8 @@ class AppAssertionCredentials(AssertionCredentials):
self.scope = util.scopes_to_string(scope)
self.kwargs = kwargs
# Assertion type is no longer used, but still in the parent class signature.
# Assertion type is no longer used, but still in the
# parent class signature.
super(AppAssertionCredentials, self).__init__(None)
@classmethod
@@ -74,9 +75,9 @@ class AppAssertionCredentials(AssertionCredentials):
Skip all the storage hoops and just refresh using the API.
Args:
http_request: callable, a callable that matches the method signature
of httplib2.Http.request, used to make the refresh
request.
http_request: callable, a callable that matches the method
signature of httplib2.Http.request, used to make
the refresh request.
Raises:
AccessTokenRefreshError: When the refresh fails.
@@ -94,13 +95,13 @@ class AppAssertionCredentials(AssertionCredentials):
else:
if response.status == 404:
content += (' This can occur if a VM was created'
' with no service account or scopes.')
' with no service account or scopes.')
raise AccessTokenRefreshError(content)
@property
def serialization_data(self):
def serialization_data(self):
raise NotImplementedError(
'Cannot serialize credentials for GCE service accounts.')
'Cannot serialize credentials for GCE service accounts.')
def create_scoped_required(self):
return not self.scope

View File

@@ -103,7 +103,7 @@ class Storage(BaseStorage):
credentials: Credentials, the credentials to store.
"""
keyring.set_password(self._service_name, self._user_name,
credentials.to_json())
credentials.to_json())
def locked_delete(self):
"""Delete Credentials file.

View File

@@ -57,7 +57,7 @@ class AlreadyLockedException(Exception):
def validate_file(filename):
if os.path.islink(filename):
raise CredentialsFileSymbolicLinkError(
'File: %s is a symbolic link.' % filename)
'File: %s is a symbolic link.' % filename)
class _Opener(object):
@@ -123,7 +123,7 @@ class _PosixOpener(_Opener):
"""
if self._locked:
raise AlreadyLockedException('File %s is already locked' %
self._filename)
self._filename)
self._locked = False
validate_file(self._filename)
@@ -140,7 +140,7 @@ class _PosixOpener(_Opener):
while True:
try:
self._lock_fd = os.open(lock_filename,
os.O_CREAT | os.O_EXCL | os.O_RDWR)
os.O_CREAT | os.O_EXCL | os.O_RDWR)
self._locked = True
break
@@ -149,7 +149,7 @@ class _PosixOpener(_Opener):
raise
if (time.time() - start_time) >= timeout:
logger.warn('Could not acquire lock %s in %s seconds',
lock_filename, timeout)
lock_filename, timeout)
# Close the file and open in fallback_mode.
if self._fh:
self._fh.close()
@@ -176,7 +176,6 @@ class _PosixOpener(_Opener):
try:
import fcntl
class _FcntlOpener(_Opener):
"""Open, lock, and unlock a file using fcntl.lockf."""
@@ -190,18 +189,20 @@ try:
Raises:
AlreadyLockedException: if the lock is already acquired.
IOError: if the open fails.
CredentialsFileSymbolicLinkError if the file is a symbolic link.
CredentialsFileSymbolicLinkError: if the file is a symbolic
link.
"""
if self._locked:
raise AlreadyLockedException('File %s is already locked' %
self._filename)
self._filename)
start_time = time.time()
validate_file(self._filename)
try:
self._fh = open(self._filename, self._mode)
except IOError as e:
# If we can't access with _mode, try _fallback_mode and don't lock.
# If we can't access with _mode, try _fallback_mode and
# don't lock.
if e.errno in (errno.EPERM, errno.EACCES):
self._fh = open(self._filename, self._fallback_mode)
return
@@ -221,7 +222,7 @@ try:
# We could not acquire the lock. Try again.
if (time.time() - start_time) >= timeout:
logger.warn('Could not lock %s in %s seconds',
self._filename, timeout)
self._filename, timeout)
if self._fh:
self._fh.close()
self._fh = open(self._filename, self._fallback_mode)
@@ -244,7 +245,6 @@ try:
import win32con
import win32file
class _Win32Opener(_Opener):
"""Open, lock, and unlock a file using windows primitives."""
@@ -271,14 +271,15 @@ try:
"""
if self._locked:
raise AlreadyLockedException('File %s is already locked' %
self._filename)
self._filename)
start_time = time.time()
validate_file(self._filename)
try:
self._fh = open(self._filename, self._mode)
except IOError as e:
# If we can't access with _mode, try _fallback_mode and don't lock.
# If we can't access with _mode, try _fallback_mode
# and don't lock.
if e.errno == errno.EACCES:
self._fh = open(self._filename, self._fallback_mode)
return
@@ -288,24 +289,25 @@ try:
try:
hfile = win32file._get_osfhandle(self._fh.fileno())
win32file.LockFileEx(
hfile,
(win32con.LOCKFILE_FAIL_IMMEDIATELY |
win32con.LOCKFILE_EXCLUSIVE_LOCK), 0, -0x10000,
pywintypes.OVERLAPPED())
hfile,
(win32con.LOCKFILE_FAIL_IMMEDIATELY |
win32con.LOCKFILE_EXCLUSIVE_LOCK), 0, -0x10000,
pywintypes.OVERLAPPED())
self._locked = True
return
except pywintypes.error as e:
if timeout == 0:
raise
# If the error is not that the file is already in use, raise.
# If the error is not that the file is already
# in use, raise.
if e[0] != _Win32Opener.FILE_IN_USE_ERROR:
raise
# We could not acquire the lock. Try again.
if (time.time() - start_time) >= timeout:
logger.warn('Could not lock %s in %s seconds' % (
self._filename, timeout))
self._filename, timeout))
if self._fh:
self._fh.close()
self._fh = open(self._filename, self._fallback_mode)
@@ -317,12 +319,13 @@ try:
if self._locked:
try:
hfile = win32file._get_osfhandle(self._fh.fileno())
win32file.UnlockFileEx(hfile, 0, -0x10000, pywintypes.OVERLAPPED())
win32file.UnlockFileEx(hfile, 0, -0x10000,
pywintypes.OVERLAPPED())
except pywintypes.error as e:
if e[0] != _Win32Opener.FILE_ALREADY_UNLOCKED_ERROR:
raise
self._locked = False
if self._fh:
if self._fh:
self._fh.close()
except ImportError:
_Win32Opener = None

View File

@@ -91,14 +91,14 @@ def get_credential_storage(filename, client_id, user_agent, scope,
"""
# Recreate the legacy key with these specific parameters
key = {'clientId': client_id, 'userAgent': user_agent,
'scope': util.scopes_to_string(scope)}
'scope': util.scopes_to_string(scope)}
return get_credential_storage_custom_key(
filename, key, warn_on_readonly=warn_on_readonly)
@util.positional(2)
def get_credential_storage_custom_string_key(
filename, key_string, warn_on_readonly=True):
def get_credential_storage_custom_string_key(filename, key_string,
warn_on_readonly=True):
"""Get a Storage instance for a credential using a single string as a key.
Allows you to provide a string as a custom key that will be used for
@@ -120,8 +120,8 @@ def get_credential_storage_custom_string_key(
@util.positional(2)
def get_credential_storage_custom_key(
filename, key_dict, warn_on_readonly=True):
def get_credential_storage_custom_key(filename, key_dict,
warn_on_readonly=True):
"""Get a Storage instance for a credential using a dictionary as a key.
Allows you to provide a dictionary as a custom key that will be used for
@@ -179,7 +179,7 @@ def _get_multistore(filename, warn_on_readonly=True):
_multistores_lock.acquire()
try:
multistore = _multistores.setdefault(
filename, _MultiStore(filename, warn_on_readonly=warn_on_readonly))
filename, _MultiStore(filename, warn_on_readonly=warn_on_readonly))
finally:
_multistores_lock.release()
return multistore
@@ -211,7 +211,7 @@ class _MultiStore(object):
self._data = None
class _Storage(BaseStorage):
"""A Storage object that knows how to read/write a single credential."""
"""A Storage object that can read/write a single credential."""
def __init__(self, multistore, key):
self._multistore = multistore
@@ -285,19 +285,20 @@ class _MultiStore(object):
self._file.open_and_lock()
except IOError as e:
if e.errno == errno.ENOSYS:
logger.warn('File system does not support locking the credentials '
'file.')
logger.warn('File system does not support locking the '
'credentials file.')
elif e.errno == errno.ENOLCK:
logger.warn('File system is out of resources for writing the '
'credentials file (is your disk full?).')
'credentials file (is your disk full?).')
else:
raise
if not self._file.is_locked():
self._read_only = True
if self._warn_on_readonly:
logger.warn('The credentials file (%s) is not writable. Opening in '
'read-only mode. Any refreshed credentials will only be '
'valid for this run.', self._file.filename())
logger.warn('The credentials file (%s) is not writable. '
'Opening in read-only mode. Any refreshed '
'credentials will only be '
'valid for this run.', self._file.filename())
if os.path.getsize(self._file.filename()) == 0:
logger.debug('Initializing empty multistore file')
# The multistore is empty so write out an empty file.
@@ -340,7 +341,8 @@ class _MultiStore(object):
if self._read_only:
return
self._file.file_handle().seek(0)
json.dump(data, self._file.file_handle(), sort_keys=True, indent=2, separators=(',', ': '))
json.dump(data, self._file.file_handle(),
sort_keys=True, indent=2, separators=(',', ': '))
self._file.file_handle().truncate()
def _refresh_data_cache(self):
@@ -357,7 +359,7 @@ class _MultiStore(object):
raw_data = self._locked_json_read()
except Exception:
logger.warn('Credential data store could not be loaded. '
'Will ignore and overwrite.')
'Will ignore and overwrite.')
return
version = 0
@@ -365,11 +367,11 @@ class _MultiStore(object):
version = raw_data['file_version']
except Exception:
logger.warn('Missing version for credential data store. It may be '
'corrupt or an old version. Overwriting.')
'corrupt or an old version. Overwriting.')
if version > 1:
raise NewerCredentialStoreError(
'Credential file has file_version of %d. '
'Only file_version of 1 is supported.' % version)
'Credential file has file_version of %d. '
'Only file_version of 1 is supported.' % version)
credentials = []
try:
@@ -379,11 +381,12 @@ class _MultiStore(object):
for cred_entry in credentials:
try:
(key, credential) = self._decode_credential_from_json(cred_entry)
key, credential = self._decode_credential_from_json(cred_entry)
self._data[key] = credential
except:
# If something goes wrong loading a credential, just ignore it
logger.info('Error decoding credential, skipping', exc_info=True)
logger.info('Error decoding credential, skipping',
exc_info=True)
def _decode_credential_from_json(self, cred_entry):
"""Load a credential from our JSON serialization.
@@ -398,7 +401,8 @@ class _MultiStore(object):
raw_key = cred_entry['key']
key = util.dict_to_tuple_key(raw_key)
credential = None
credential = Credentials.new_from_json(json.dumps(cred_entry['credential']))
credential = Credentials.new_from_json(
json.dumps(cred_entry['credential']))
return (key, credential)
def _write(self):

View File

@@ -72,7 +72,8 @@ def run(flow, storage, http=None):
of values.
``--[no]auth_local_webserver`` (boolean, default: ``True``)
Run a local web server to handle redirects during OAuth authorization.
Run a local web server to handle redirects during OAuth
authorization.
Since it uses flags make sure to initialize the ``gflags`` module before
calling ``run()``.
@@ -87,8 +88,8 @@ def run(flow, storage, http=None):
Credentials, the obtained credential.
"""
logging.warning('This function, oauth2client.tools.run(), and the use of '
'the gflags library are deprecated and will be removed in a future '
'version of the library.')
'the gflags library are deprecated and will be removed in '
'a future version of the library.')
if FLAGS.auth_local_webserver:
success = False
port_number = 0
@@ -96,7 +97,7 @@ def run(flow, storage, http=None):
port_number = port
try:
httpd = ClientRedirectServer((FLAGS.auth_host_name, port),
ClientRedirectHandler)
ClientRedirectHandler)
except socket.error as e:
pass
else:
@@ -104,13 +105,14 @@ def run(flow, storage, http=None):
break
FLAGS.auth_local_webserver = success
if not success:
print('Failed to start a local webserver listening on either port 8080')
print('or port 9090. Please check your firewall settings and locally')
print('running programs that may be blocking or using those ports.')
print()
print('Falling back to --noauth_local_webserver and continuing with')
print('authorization.')
print()
print('Failed to start a local webserver listening on '
'either port 8080')
print('or port 9090. Please check your firewall settings and locally')
print('running programs that may be blocking or using those ports.')
print()
print('Falling back to --noauth_local_webserver and continuing with')
print('authorization.')
print()
if FLAGS.auth_local_webserver:
oauth_callback = 'http://%s:%s/' % (FLAGS.auth_host_name, port_number)
@@ -144,7 +146,8 @@ def run(flow, storage, http=None):
if 'code' in httpd.query_params:
code = httpd.query_params['code']
else:
print('Failed to find "code" in the query parameters of the redirect.')
print('Failed to find "code" in the query '
'parameters of the redirect.')
sys.exit('Try running with --noauth_local_webserver.')
else:
code = input('Enter verification code: ').strip()

View File

@@ -38,13 +38,14 @@ class _ServiceAccountCredentials(AssertionCredentials):
MAX_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
def __init__(self, service_account_id, service_account_email, private_key_id,
private_key_pkcs8_text, scopes, user_agent=None,
token_uri=GOOGLE_TOKEN_URI, revoke_uri=GOOGLE_REVOKE_URI,
**kwargs):
def __init__(self, service_account_id, service_account_email,
private_key_id, private_key_pkcs8_text, scopes,
user_agent=None, token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI, **kwargs):
super(_ServiceAccountCredentials, self).__init__(
None, user_agent=user_agent, token_uri=token_uri, revoke_uri=revoke_uri)
None, user_agent=user_agent, token_uri=token_uri,
revoke_uri=revoke_uri)
self._service_account_id = service_account_id
self._service_account_email = service_account_email
@@ -61,18 +62,18 @@ class _ServiceAccountCredentials(AssertionCredentials):
"""Generate the assertion that will be used in the request."""
header = {
'alg': 'RS256',
'typ': 'JWT',
'kid': self._private_key_id
'alg': 'RS256',
'typ': 'JWT',
'kid': self._private_key_id
}
now = int(time.time())
payload = {
'aud': self._token_uri,
'scope': self._scopes,
'iat': now,
'exp': now + _ServiceAccountCredentials.MAX_TOKEN_LIFETIME_SECS,
'iss': self._service_account_email
'aud': self._token_uri,
'scope': self._scopes,
'iat': now,
'exp': now + _ServiceAccountCredentials.MAX_TOKEN_LIFETIME_SECS,
'iss': self._service_account_email
}
payload.update(self._kwargs)
@@ -81,7 +82,8 @@ class _ServiceAccountCredentials(AssertionCredentials):
assertion_input = first_segment + b'.' + second_segment
# Sign the assertion.
rsa_bytes = rsa.pkcs1.sign(assertion_input, self._private_key, 'SHA-256')
rsa_bytes = rsa.pkcs1.sign(assertion_input, self._private_key,
'SHA-256')
signature = base64.urlsafe_b64encode(rsa_bytes).rstrip(b'=')
return assertion_input + b'.' + signature
@@ -90,7 +92,7 @@ class _ServiceAccountCredentials(AssertionCredentials):
# Ensure that it is bytes
blob = _to_bytes(blob, encoding='utf-8')
return (self._private_key_id,
rsa.pkcs1.sign(blob, self._private_key, 'SHA-256'))
rsa.pkcs1.sign(blob, self._private_key, 'SHA-256'))
@property
def service_account_email(self):
@@ -99,11 +101,11 @@ class _ServiceAccountCredentials(AssertionCredentials):
@property
def serialization_data(self):
return {
'type': 'service_account',
'client_id': self._service_account_id,
'client_email': self._service_account_email,
'private_key_id': self._private_key_id,
'private_key': self._private_key_pkcs8_text
'type': 'service_account',
'client_id': self._service_account_id,
'client_email': self._service_account_email,
'private_key_id': self._private_key_id,
'private_key': self._private_key_pkcs8_text
}
def create_scoped_required(self):
@@ -111,14 +113,14 @@ class _ServiceAccountCredentials(AssertionCredentials):
def create_scoped(self, scopes):
return _ServiceAccountCredentials(self._service_account_id,
self._service_account_email,
self._private_key_id,
self._private_key_pkcs8_text,
scopes,
user_agent=self._user_agent,
token_uri=self._token_uri,
revoke_uri=self._revoke_uri,
**self._kwargs)
self._service_account_email,
self._private_key_id,
self._private_key_pkcs8_text,
scopes,
user_agent=self._user_agent,
token_uri=self._token_uri,
revoke_uri=self._revoke_uri,
**self._kwargs)
def _get_private_key(private_key_pkcs8_text):
@@ -127,5 +129,5 @@ def _get_private_key(private_key_pkcs8_text):
der = rsa.pem.load_pem(private_key_pkcs8_text, 'PRIVATE KEY')
asn1_private_key, _ = decoder.decode(der, asn1Spec=PrivateKeyInfo())
return rsa.PrivateKey.load_pkcs1(
asn1_private_key.getComponentByName('privateKey').asOctets(),
format='DER')
asn1_private_key.getComponentByName('privateKey').asOctets(),
format='DER')

View File

@@ -55,14 +55,15 @@ def _CreateArgumentParser():
return None
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--auth_host_name', default='localhost',
help='Hostname when running a local web server.')
help='Hostname when running a local web server.')
parser.add_argument('--noauth_local_webserver', action='store_true',
default=False, help='Do not run a local web server.')
default=False, help='Do not run a local web server.')
parser.add_argument('--auth_host_port', default=[8080, 8090], type=int,
nargs='*', help='Port web server should listen on.')
parser.add_argument('--logging_level', default='ERROR',
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
help='Set the logging level of detail.')
nargs='*', help='Port web server should listen on.')
parser.add_argument(
'--logging_level', default='ERROR',
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
help='Set the logging level of detail.')
return parser
# argparser is an ArgumentParser that contains command-line options expected
@@ -100,12 +101,14 @@ class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
query = self.path.split('?', 1)[-1]
query = dict(urllib.parse.parse_qsl(query))
self.server.query_params = query
self.wfile.write(b"<html><head><title>Authentication Status</title></head>")
self.wfile.write(b"<body><p>The authentication flow has completed.</p>")
self.wfile.write(
b"<html><head><title>Authentication Status</title></head>")
self.wfile.write(
b"<body><p>The authentication flow has completed.</p>")
self.wfile.write(b"</body></html>")
def log_message(self, format, *args):
"""Do not log messages to stdout while running as command line program."""
"""Do not log messages to stdout while running as cmd. line program."""
@util.positional(3)
@@ -137,9 +140,9 @@ def run_flow(flow, storage, flags, http=None):
Run a local web server to handle redirects during OAuth
authorization.
The tools module defines an ``ArgumentParser`` the already contains the flag
definitions that ``run()`` requires. You can pass that ``ArgumentParser`` to
your ``ArgumentParser`` constructor::
The tools module defines an ``ArgumentParser`` the already contains the
flag definitions that ``run()`` requires. You can pass that
``ArgumentParser`` to your ``ArgumentParser`` constructor::
parser = argparse.ArgumentParser(
description=__doc__,
@@ -167,7 +170,7 @@ def run_flow(flow, storage, flags, http=None):
port_number = port
try:
httpd = ClientRedirectServer((flags.auth_host_name, port),
ClientRedirectHandler)
ClientRedirectHandler)
except socket.error:
pass
else:
@@ -175,13 +178,14 @@ def run_flow(flow, storage, flags, http=None):
break
flags.noauth_local_webserver = not success
if not success:
print('Failed to start a local webserver listening on either port 8080')
print('or port 9090. Please check your firewall settings and locally')
print('running programs that may be blocking or using those ports.')
print()
print('Falling back to --noauth_local_webserver and continuing with')
print('authorization.')
print()
print('Failed to start a local webserver listening '
'on either port 8080')
print('or port 9090. Please check your firewall settings and locally')
print('running programs that may be blocking or using those ports.')
print()
print('Falling back to --noauth_local_webserver and continuing with')
print('authorization.')
print()
if not flags.noauth_local_webserver:
oauth_callback = 'http://%s:%s/' % (flags.auth_host_name, port_number)
@@ -197,7 +201,8 @@ def run_flow(flow, storage, flags, http=None):
print()
print(' ' + authorize_url)
print()
print('If your browser is on a different machine then exit and re-run this')
print('If your browser is on a different machine then '
'exit and re-run this')
print('application with the command-line parameter ')
print()
print(' --noauth_local_webserver')
@@ -216,7 +221,8 @@ def run_flow(flow, storage, flags, http=None):
if 'code' in httpd.query_params:
code = httpd.query_params['code']
else:
print('Failed to find "code" in the query parameters of the redirect.')
print('Failed to find "code" in the query parameters '
'of the redirect.')
sys.exit('Try running with --noauth_local_webserver.')
else:
code = input('Enter verification code: ').strip()
@@ -235,15 +241,15 @@ def run_flow(flow, storage, flags, http=None):
def message_if_missing(filename):
"""Helpful message to display if the CLIENT_SECRETS file is missing."""
return _CLIENT_SECRETS_MESSAGE % filename
try:
from oauth2client.old_run import run
from oauth2client.old_run import FLAGS
except ImportError:
def run(*args, **kwargs):
raise NotImplementedError(
'The gflags library must be installed to use tools.run(). '
'Please install gflags or preferrably switch to using '
'tools.run_flow().')
'The gflags library must be installed to use tools.run(). '
'Please install gflags or preferrably switch to using '
'tools.run_flow().')

View File

@@ -129,8 +129,10 @@ def positional(max_positional_args):
plural_s = ''
if max_positional_args != 1:
plural_s = 's'
message = '%s() takes at most %d positional argument%s (%d given)' % (
wrapped.__name__, max_positional_args, plural_s, len(args))
message = ('%s() takes at most %d positional '
'argument%s (%d given)' % (
wrapped.__name__, max_positional_args,
plural_s, len(args)))
if positional_parameters_enforcement == POSITIONAL_EXCEPTION:
raise TypeError(message)
elif positional_parameters_enforcement == POSITIONAL_WARNING:

View File

@@ -104,7 +104,7 @@ def validate_token(key, token, user_id, action_id="", current_time=None):
# The given token should match the generated one with the same time.
expected_token = generate_token(key, user_id, action_id=action_id,
when=token_time)
when=token_time)
if len(token) != len(expected_token):
return False