From 119f5b57fe2801e3eee51dd4fba8c12de3eefe59 Mon Sep 17 00:00:00 2001 From: Danny Hermes Date: Fri, 21 Aug 2015 08:05:44 -0700 Subject: [PATCH] Docstring pass after pep8ify in oauth2client/ --- oauth2client/_helpers.py | 62 +- oauth2client/_openssl_crypt.py | 86 +-- oauth2client/_pycrypto_crypt.py | 70 +- oauth2client/appengine.py | 609 +++++++-------- oauth2client/client.py | 1226 ++++++++++++++++--------------- oauth2client/clientsecrets.py | 48 +- oauth2client/crypt.py | 36 +- oauth2client/devshell.py | 27 +- oauth2client/django_orm.py | 40 +- oauth2client/file.py | 43 +- oauth2client/flask_util.py | 19 +- oauth2client/gce.py | 39 +- oauth2client/keyring_storage.py | 65 +- oauth2client/locked_file.py | 96 +-- oauth2client/multistore_file.py | 257 +++---- oauth2client/old_run.py | 58 +- oauth2client/tools.py | 99 ++- oauth2client/util.py | 159 ++-- oauth2client/xsrfutil.py | 44 +- 19 files changed, 1579 insertions(+), 1504 deletions(-) diff --git a/oauth2client/_helpers.py b/oauth2client/_helpers.py index 89921d8..f252bf5 100644 --- a/oauth2client/_helpers.py +++ b/oauth2client/_helpers.py @@ -21,15 +21,17 @@ import six def _parse_pem_key(raw_key_input): """Identify and extract PEM keys. - Determines whether the given key is in the format of PEM key, and extracts - the relevant part of the key if it is. + Determines whether the given key is in the format of PEM key, and extracts + the relevant part of the key if it is. - Args: - raw_key_input: The contents of a private key file (either PEM or PKCS12). + Args: + raw_key_input: The contents of a private key file (either PEM or + PKCS12). - Returns: - string, The actual key if the contents are from a PEM file, or else None. - """ + Returns: + string, The actual key if the contents are from a PEM file, or + else None. + """ offset = raw_key_input.find(b'-----BEGIN ') if offset != -1: return raw_key_input[offset:] @@ -42,24 +44,24 @@ def _json_encode(data): def _to_bytes(value, encoding='ascii'): """Converts a string value to bytes, if necessary. - Unfortunately, ``six.b`` is insufficient for this task since in - Python2 it does not modify ``unicode`` objects. + Unfortunately, ``six.b`` is insufficient for this task since in + Python2 it does not modify ``unicode`` objects. - Args: - value: The string/bytes value to be converted. - encoding: The encoding to use to convert unicode to bytes. Defaults - to "ascii", which will not allow any characters from ordinals - larger than 127. Other useful values are "latin-1", which - which will only allows byte ordinals (up to 255) and "utf-8", - which will encode any unicode that needs to be. + Args: + value: The string/bytes value to be converted. + encoding: The encoding to use to convert unicode to bytes. Defaults + to "ascii", which will not allow any characters from ordinals + larger than 127. Other useful values are "latin-1", which + which will only allows byte ordinals (up to 255) and "utf-8", + which will encode any unicode that needs to be. - Returns: - The original value converted to bytes (if unicode) or as passed in - if it started out as bytes. + Returns: + The original value converted to bytes (if unicode) or as passed in + if it started out as bytes. - Raises: - ValueError if the value could not be converted to bytes. - """ + Raises: + ValueError if the value could not be converted to bytes. + """ result = (value.encode(encoding) if isinstance(value, six.text_type) else value) if isinstance(result, six.binary_type): @@ -71,16 +73,16 @@ def _to_bytes(value, encoding='ascii'): def _from_bytes(value): """Converts bytes to a string value, if necessary. - Args: - value: The string/bytes value to be converted. + Args: + value: The string/bytes value to be converted. - Returns: - The original value converted to unicode (if bytes) or as passed in - if it started out as unicode. + Returns: + The original value converted to unicode (if bytes) or as passed in + if it started out as unicode. - Raises: - ValueError if the value could not be converted to unicode. - """ + Raises: + 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(result, six.text_type): diff --git a/oauth2client/_openssl_crypt.py b/oauth2client/_openssl_crypt.py index 34f64ec..9896f62 100644 --- a/oauth2client/_openssl_crypt.py +++ b/oauth2client/_openssl_crypt.py @@ -27,24 +27,24 @@ class OpenSSLVerifier(object): def __init__(self, pubkey): """Constructor. - Args: - pubkey, OpenSSL.crypto.PKey, The public key to verify with. - """ + Args: + pubkey: OpenSSL.crypto.PKey, The public key to verify with. + """ self._pubkey = pubkey def verify(self, message, signature): """Verifies a message against a signature. - Args: - message: string or bytes, The message to verify. If string, will be - encoded to bytes as utf-8. - signature: string or bytes, The signature on the message. If string, - will be encoded to bytes as utf-8. + Args: + message: string or bytes, The message to verify. If string, will be + encoded to bytes as utf-8. + signature: string or bytes, The signature on the message. If string, + will be encoded to bytes as utf-8. - Returns: - True if message was signed by the private key associated with the public - key that this object was constructed with. - """ + Returns: + True if message was signed by the private key associated with the + public key that this object was constructed with. + """ message = _to_bytes(message, encoding='utf-8') signature = _to_bytes(signature, encoding='utf-8') try: @@ -57,17 +57,17 @@ class OpenSSLVerifier(object): def from_string(key_pem, is_x509_cert): """Construct a Verified instance from a string. - 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. + 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. - Returns: - Verifier instance. + Returns: + Verifier instance. - Raises: - OpenSSL.crypto.Error if the key_pem can't be parsed. - """ + Raises: + OpenSSL.crypto.Error: if the key_pem can't be parsed. + """ if is_x509_cert: pubkey = crypto.load_certificate(crypto.FILETYPE_PEM, key_pem) else: @@ -81,20 +81,20 @@ class OpenSSLSigner(object): def __init__(self, pkey): """Constructor. - Args: - pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with. - """ + Args: + pkey: OpenSSL.crypto.PKey (or equiv), The private key to sign with. + """ self._key = pkey def sign(self, message): """Signs a message. - Args: - message: bytes, Message to be signed. + Args: + message: bytes, Message to be signed. - Returns: - string, The signature of the message for the given key. - """ + Returns: + string, The signature of the message for the given key. + """ message = _to_bytes(message, encoding='utf-8') return crypto.sign(self._key, message, 'sha256') @@ -102,16 +102,16 @@ class OpenSSLSigner(object): def from_string(key, password=b'notasecret'): """Construct a Signer instance from a string. - Args: - key: string, private key in PKCS12 or PEM format. - password: string, password for the private key file. + Args: + key: string, private key in PKCS12 or PEM format. + password: string, password for the private key file. - Returns: - Signer instance. + Returns: + Signer instance. - Raises: - OpenSSL.crypto.Error if the key can't be parsed. - """ + Raises: + OpenSSL.crypto.Error if the key can't be parsed. + """ parsed_pem_key = _parse_pem_key(key) if parsed_pem_key: pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, parsed_pem_key) @@ -124,13 +124,13 @@ class OpenSSLSigner(object): def pkcs12_key_as_pem(private_key_text, private_key_password): """Convert the contents of a PKCS12 key to PEM using OpenSSL. - Args: - private_key_text: String. Private key. - private_key_password: String. Password for PKCS12. + Args: + private_key_text: String. Private key. + private_key_password: String. Password for PKCS12. - Returns: - String. PEM contents of ``private_key_text``. - """ + Returns: + String. PEM contents of ``private_key_text``. + """ decoded_body = base64.b64decode(private_key_text) private_key_password = _to_bytes(private_key_password) diff --git a/oauth2client/_pycrypto_crypt.py b/oauth2client/_pycrypto_crypt.py index 0f13b0a..e604095 100644 --- a/oauth2client/_pycrypto_crypt.py +++ b/oauth2client/_pycrypto_crypt.py @@ -30,23 +30,24 @@ class PyCryptoVerifier(object): def __init__(self, pubkey): """Constructor. - Args: - pubkey, OpenSSL.crypto.PKey (or equiv), The public key to verify with. - """ + Args: + pubkey: OpenSSL.crypto.PKey (or equiv), The public key to verify + with. + """ self._pubkey = pubkey def verify(self, message, signature): """Verifies a message against a signature. - Args: - message: string or bytes, The message to verify. If string, will be - encoded to bytes as utf-8. - signature: string or bytes, The signature on the message. + Args: + message: string or bytes, The message to verify. If string, will be + encoded to bytes as utf-8. + signature: string or bytes, The signature on the message. - Returns: - True if message was signed by the private key associated with the public - key that this object was constructed with. - """ + Returns: + True if message was signed by the private key associated with the + public key that this object was constructed with. + """ message = _to_bytes(message, encoding='utf-8') return PKCS1_v1_5.new(self._pubkey).verify( SHA256.new(message), signature) @@ -55,14 +56,14 @@ class PyCryptoVerifier(object): def from_string(key_pem, is_x509_cert): """Construct a Verified instance from a string. - 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. + 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. - Returns: - Verifier instance. - """ + Returns: + Verifier instance. + """ if is_x509_cert: key_pem = _to_bytes(key_pem) pemLines = key_pem.replace(b' ', b'').split() @@ -83,20 +84,20 @@ class PyCryptoSigner(object): def __init__(self, pkey): """Constructor. - Args: - pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with. - """ + Args: + pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with. + """ self._key = pkey def sign(self, message): """Signs a message. - Args: - message: string, Message to be signed. + Args: + message: string, Message to be signed. - Returns: - string, The signature of the message for the given key. - """ + Returns: + string, The signature of the message for the given key. + """ message = _to_bytes(message, encoding='utf-8') return PKCS1_v1_5.new(self._key).sign(SHA256.new(message)) @@ -104,16 +105,17 @@ class PyCryptoSigner(object): def from_string(key, password='notasecret'): """Construct a Signer instance from a string. - Args: - key: string, private key in PEM format. - password: string, password for private key file. Unused for PEM files. + Args: + key: string, private key in PEM format. + password: string, password for private key file. Unused for PEM + files. - Returns: - Signer instance. + Returns: + Signer instance. - Raises: - NotImplementedError if the key isn't in PEM format. - """ + Raises: + NotImplementedError if the key isn't in PEM format. + """ parsed_pem_key = _parse_pem_key(key) if parsed_pem_key: pkey = RSA.importKey(parsed_pem_key) diff --git a/oauth2client/appengine.py b/oauth2client/appengine.py index e434675..221e00e 100644 --- a/oauth2client/appengine.py +++ b/oauth2client/appengine.py @@ -65,12 +65,12 @@ XSRF_MEMCACHE_ID = 'xsrf_secret_key' def _safe_html(s): """Escape text to make it safe to display. - Args: - s: string, The text to escape. + Args: + s: string, The text to escape. - Returns: - The escaped text as a string. - """ + Returns: + The escaped text as a string. + """ return cgi.escape(s, quote=1).replace("'", ''') @@ -85,22 +85,22 @@ class InvalidXsrfTokenError(Exception): class SiteXsrfSecretKey(db.Model): """Storage for the sites XSRF secret key. - There will only be one instance stored of this model, the one used for the - site. - """ + There will only be one instance stored of this model, the one used for the + site. + """ secret = db.StringProperty() if ndb is not None: class SiteXsrfSecretKeyNDB(ndb.Model): """NDB Model for storage for the sites XSRF secret key. - Since this model uses the same kind as SiteXsrfSecretKey, it can be used - interchangeably. This simply provides an NDB model for interacting with the - same data the DB model interacts with. + Since this model uses the same kind as SiteXsrfSecretKey, it can be + used interchangeably. This simply provides an NDB model for interacting + with the same data the DB model interacts with. - There should only be one instance stored of this model, the one used for the - site. - """ + There should only be one instance stored of this model, the one used + for the site. + """ secret = ndb.StringProperty() @classmethod @@ -110,20 +110,19 @@ if ndb is not None: def _generate_new_xsrf_secret_key(): - """Returns a random XSRF secret key. - """ + """Returns a random XSRF secret key.""" return os.urandom(16).encode("hex") def xsrf_secret_key(): """Return the secret key for use for XSRF protection. - If the Site entity does not have a secret key, this method will also create - one and persist it. + If the Site entity does not have a secret key, this method will also create + one and persist it. - Returns: - The secret key. - """ + Returns: + The secret key. + """ secret = memcache.get(XSRF_MEMCACHE_ID, namespace=OAUTH2CLIENT_NAMESPACE) if not secret: # Load the one and only instance of SiteXsrfSecretKey. @@ -140,27 +139,28 @@ def xsrf_secret_key(): class AppAssertionCredentials(AssertionCredentials): """Credentials object for App Engine Assertion Grants - This object will allow an App Engine application to identify itself to Google - and other OAuth 2.0 servers that can verify assertions. It can be used for the - purpose of accessing data stored under an account assigned to the App Engine - application itself. + This object will allow an App Engine application to identify itself to + Google and other OAuth 2.0 servers that can verify assertions. It can be + used for the purpose of accessing data stored under an account assigned to + the App Engine application 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) def __init__(self, scope, **kwargs): """Constructor for AppAssertionCredentials - Args: - scope: string or iterable of strings, scope(s) of the credentials being - requested. - **kwargs: optional keyword args, including: - service_account_id: service account id of the application. If None or - unspecified, the default service account for the app is used. - """ + Args: + scope: string or iterable of strings, scope(s) of the credentials + being requested. + **kwargs: optional keyword args, including: + service_account_id: service account id of the application. If None + or unspecified, the default service account for + the app is used. + """ self.scope = util.scopes_to_string(scope) self._kwargs = kwargs self.service_account_id = kwargs.get('service_account_id', None) @@ -176,17 +176,18 @@ class AppAssertionCredentials(AssertionCredentials): def _refresh(self, http_request): """Refreshes the access_token. - Since the underlying App Engine app_identity implementation does its own - caching we can skip all the storage hoops and just to a refresh using the - API. + Since the underlying App Engine app_identity implementation does its + own caching we can skip all the storage hoops and just to a 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. + Args: + 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. - """ + Raises: + AccessTokenRefreshError: When the refresh fails. + """ try: scopes = self.scope.split() (token, _) = app_identity.get_access_token( @@ -209,8 +210,9 @@ class AppAssertionCredentials(AssertionCredentials): class FlowProperty(db.Property): """App Engine datastore Property for Flow. - Utility property that allows easy storage and retrieval of an - oauth2client.Flow""" + Utility property that allows easy storage and retrieval of an + oauth2client.Flow + """ # Tell what the user type is. data_type = Flow @@ -242,23 +244,24 @@ if ndb is not None: class FlowNDBProperty(ndb.PickleProperty): """App Engine NDB datastore Property for Flow. - Serves the same purpose as the DB FlowProperty, but for NDB models. Since - PickleProperty inherits from BlobProperty, the underlying representation of - the data in the datastore will be the same as in the DB case. + Serves the same purpose as the DB FlowProperty, but for NDB models. + Since PickleProperty inherits from BlobProperty, the underlying + representation of the data in the datastore will be the same as in the + DB case. - Utility property that allows easy storage and retrieval of an - oauth2client.Flow - """ + Utility property that allows easy storage and retrieval of an + oauth2client.Flow + """ def _validate(self, value): """Validates a value as a proper Flow object. - Args: - value: A value to be set on the property. + Args: + value: A value to be set on the property. - Raises: - TypeError if the value is not an instance of Flow. - """ + Raises: + TypeError if the value is not an instance of Flow. + """ 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 ' @@ -268,9 +271,9 @@ if ndb is not None: class CredentialsProperty(db.Property): """App Engine datastore Property for Credentials. - Utility property that allows easy storage and retrieval of - oath2client.Credentials - """ + Utility property that allows easy storage and retrieval of + oath2client.Credentials + """ # Tell what the user type is. data_type = Credentials @@ -320,23 +323,24 @@ if ndb is not None: class CredentialsNDBProperty(ndb.BlobProperty): """App Engine NDB datastore Property for Credentials. - Serves the same purpose as the DB CredentialsProperty, but for NDB models. - Since CredentialsProperty stores data as a blob and this inherits from - BlobProperty, the data in the datastore will be the same as in the DB case. + Serves the same purpose as the DB CredentialsProperty, but for NDB + models. Since CredentialsProperty stores data as a blob and this + inherits from BlobProperty, the data in the datastore will be the same + as in the DB case. - Utility property that allows easy storage and retrieval of Credentials and - subclasses. - """ + Utility property that allows easy storage and retrieval of Credentials + and subclasses. + """ def _validate(self, value): """Validates a value as a proper credentials object. - Args: - value: A value to be set on the property. + Args: + value: A value to be set on the property. - Raises: - TypeError if the value is not an instance of Credentials. - """ + Raises: + TypeError if the value is not an instance of Credentials. + """ 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 ' @@ -345,12 +349,13 @@ if ndb is not None: def _to_base_type(self, value): """Converts our validated value to a JSON serialized string. - Args: - value: A value to be set in the datastore. + Args: + value: A value to be set in the datastore. - Returns: - A JSON serialized version of the credential, else '' if value is None. - """ + Returns: + A JSON serialized version of the credential, else '' if value + is None. + """ if value is None: return '' else: @@ -359,13 +364,14 @@ if ndb is not None: def _from_base_type(self, value): """Converts our stored JSON string back to the desired type. - Args: - value: A value from the datastore to be converted to the desired type. + Args: + value: A value from the datastore to be converted to the + desired type. - Returns: - A deserialized Credentials (or subclass) object, else None if the - value can't be parsed. - """ + Returns: + A deserialized Credentials (or subclass) object, else None if + the value can't be parsed. + """ if not value: return None try: @@ -379,26 +385,27 @@ if ndb is not None: class StorageByKeyName(Storage): """Store and retrieve a credential to and from the App Engine datastore. - This Storage helper presumes the Credentials have been stored as a - CredentialsProperty or CredentialsNDBProperty on a datastore model class, and - that entities are stored by key_name. - """ + This Storage helper presumes the Credentials have been stored as a + CredentialsProperty or CredentialsNDBProperty on a datastore model class, + and that entities are stored by key_name. + """ @util.positional(4) def __init__(self, model, key_name, property_name, cache=None, user=None): """Constructor for Storage. - Args: - model: db.Model or ndb.Model, model class - key_name: string, key name for the entity that has the credentials - property_name: string, name of the property that is a CredentialsProperty - or CredentialsNDBProperty. - cache: memcache, a write-through cache to put in front of the datastore. - If the model you are using is an NDB model, using a cache will be - redundant since the model uses an instance cache and memcache for you. - user: users.User object, optional. Can be used to grab user ID as a - key_name if no key name is specified. - """ + Args: + model: db.Model or ndb.Model, model class + key_name: string, key name for the entity that has the credentials + property_name: string, name of the property that is a + CredentialsProperty or CredentialsNDBProperty. + cache: memcache, a write-through cache to put in front of the + datastore. If the model you are using is an NDB model, using + a cache will be redundant since the model uses an instance + cache and memcache for you. + user: users.User object, optional. Can be used to grab user ID as a + key_name if no key name is specified. + """ if key_name is None: if user is None: raise ValueError('StorageByKeyName called with no key name or user.') @@ -412,9 +419,9 @@ class StorageByKeyName(Storage): def _is_ndb(self): """Determine whether the model of the instance is an NDB model. - Returns: - Boolean indicating whether or not the model is an NDB or DB model. - """ + 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 if isinstance(self._model, type): @@ -428,12 +435,12 @@ class StorageByKeyName(Storage): def _get_entity(self): """Retrieve entity from datastore. - Uses a different model method for db or ndb models. + Uses a different model method for db or ndb models. - Returns: - Instance of the model corresponding to the current storage object - and stored using the key name of the storage object. - """ + Returns: + Instance of the model corresponding to the current storage object + and stored using the key name of the storage object. + """ if self._is_ndb(): return self._model.get_by_id(self._key_name) else: @@ -442,9 +449,9 @@ class StorageByKeyName(Storage): def _delete_entity(self): """Delete entity from datastore. - Attempts to delete using the key_name stored on the object, whether or not - the given key is in the datastore. - """ + Attempts to delete using the key_name stored on the object, whether or + not the given key is in the datastore. + """ if self._is_ndb(): ndb.Key(self._model, self._key_name).delete() else: @@ -455,9 +462,9 @@ class StorageByKeyName(Storage): def locked_get(self): """Retrieve Credential from datastore. - Returns: - oauth2client.Credentials - """ + Returns: + oauth2client.Credentials + """ credentials = None if self._cache: json = self._cache.get(self._key_name) @@ -478,9 +485,9 @@ class StorageByKeyName(Storage): def locked_put(self, credentials): """Write a Credentials to the datastore. - Args: - credentials: Credentials, the credentials to store. - """ + Args: + credentials: Credentials, the credentials to store. + """ entity = self._model.get_or_insert(self._key_name) setattr(entity, self._property_name, credentials) entity.put() @@ -500,8 +507,8 @@ class StorageByKeyName(Storage): class CredentialsModel(db.Model): """Storage for OAuth 2.0 Credentials - Storage of the model is keyed by the user.user_id(). - """ + Storage of the model is keyed by the user.user_id(). + """ credentials = CredentialsProperty() @@ -509,14 +516,14 @@ if ndb is not None: class CredentialsNDBModel(ndb.Model): """NDB Model for storage of OAuth 2.0 Credentials - Since this model uses the same kind as CredentialsModel and has a property - which can serialize and deserialize Credentials correctly, it can be used - interchangeably with a CredentialsModel to access, insert and delete the - same entities. This simply provides an NDB model for interacting with the - same data the DB model interacts with. + Since this model uses the same kind as CredentialsModel and has a + property which can serialize and deserialize Credentials correctly, it + can be used interchangeably with a CredentialsModel to access, insert + and delete the same entities. This simply provides an NDB model for + interacting with the same data the DB model interacts with. - Storage of the model is keyed by the user.user_id(). - """ + Storage of the model is keyed by the user.user_id(). + """ credentials = CredentialsNDBProperty() @classmethod @@ -528,16 +535,16 @@ if ndb is not None: def _build_state_value(request_handler, user): """Composes the value for the 'state' parameter. - Packs the current request URI and an XSRF token into an opaque string that - can be passed to the authentication server via the 'state' parameter. + Packs the current request URI and an XSRF token into an opaque string that + can be passed to the authentication server via the 'state' parameter. - Args: - request_handler: webapp.RequestHandler, The request. - user: google.appengine.api.users.User, The current user. + Args: + request_handler: webapp.RequestHandler, The request. + user: google.appengine.api.users.User, The current user. - Returns: - The state value as a string. - """ + Returns: + The state value as a string. + """ uri = request_handler.request.url token = xsrfutil.generate_token(xsrf_secret_key(), user.user_id(), action_id=str(uri)) @@ -547,18 +554,18 @@ def _build_state_value(request_handler, user): def _parse_state_value(state, user): """Parse the value of the 'state' parameter. - Parses the value and validates the XSRF token in the state parameter. + Parses the value and validates the XSRF token in the state parameter. - Args: - state: string, The value of the state parameter. - user: google.appengine.api.users.User, The current user. + Args: + state: string, The value of the state parameter. + user: google.appengine.api.users.User, The current user. - Raises: - InvalidXsrfTokenError: if the XSRF token is invalid. + Raises: + InvalidXsrfTokenError: if the XSRF token is invalid. - Returns: - The redirect URI. - """ + Returns: + The redirect URI. + """ uri, token = state.rsplit(':', 1) if not xsrfutil.validate_token(xsrf_secret_key(), token, user.user_id(), action_id=uri): @@ -570,24 +577,24 @@ def _parse_state_value(state, user): class OAuth2Decorator(object): """Utility for making OAuth 2.0 easier. - Instantiate and then use with oauth_required or oauth_aware - as decorators on webapp.RequestHandler methods. + Instantiate and then use with oauth_required or oauth_aware + as decorators on webapp.RequestHandler methods. - :: + :: - decorator = OAuth2Decorator( - client_id='837...ent.com', - client_secret='Qh...wwI', - scope='https://www.googleapis.com/auth/plus') + decorator = OAuth2Decorator( + client_id='837...ent.com', + client_secret='Qh...wwI', + scope='https://www.googleapis.com/auth/plus') - class MainHandler(webapp.RequestHandler): - @decorator.oauth_required - def get(self): - http = decorator.http() - # http is authorized with the user's Credentials and can be used - # in API calls + class MainHandler(webapp.RequestHandler): + @decorator.oauth_required + def get(self): + http = decorator.http() + # http is authorized with the user's Credentials and can be + # used in API calls - """ + """ def set_credentials(self, credentials): self._tls.credentials = credentials @@ -595,11 +602,11 @@ class OAuth2Decorator(object): def get_credentials(self): """A thread local Credentials object. - Returns: - A client.Credentials object, or None if credentials hasn't been set in - this thread yet, which may happen when calling has_credentials inside - oauth_aware. - """ + Returns: + A client.Credentials object, or None if credentials hasn't been set + in this thread yet, which may happen when calling has_credentials + inside oauth_aware. + """ return getattr(self._tls, 'credentials', None) credentials = property(get_credentials, set_credentials) @@ -610,11 +617,11 @@ class OAuth2Decorator(object): def get_flow(self): """A thread local Flow object. - Returns: - A credentials.Flow object, or None if the flow hasn't been set in this - thread yet, which happens in _create_flow() since Flows are created - lazily. - """ + Returns: + A credentials.Flow object, or None if the flow hasn't been set in + this thread yet, which happens in _create_flow() since Flows are + created lazily. + """ return getattr(self._tls, 'flow', None) flow = property(get_flow, set_flow) @@ -635,42 +642,52 @@ class OAuth2Decorator(object): """Constructor for OAuth2Decorator - Args: - client_id: string, client identifier. - client_secret: string client secret. - scope: string or iterable of strings, scope(s) of the credentials being - requested. - auth_uri: string, URI for authorization endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - token_uri: string, URI for token endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - revoke_uri: string, URI for revoke endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 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. - 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. - 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 callback URI. This is useful with providers (e.g. - wordpress.com) that include extra fields that the client may want. - _storage_class: "Protected" keyword argument not typically provided to - this constructor. A storage class to aid in storing a Credentials object - for a user in the datastore. Defaults to StorageByKeyName. - _credentials_class: "Protected" keyword argument not typically provided to - this constructor. A db or ndb Model class to hold credentials. Defaults - to CredentialsModel. - _credentials_property_name: "Protected" keyword argument not typically - provided to this constructor. A string indicating the name of the field - on the _credentials_class where a Credentials object will be stored. - Defaults to 'credentials'. - **kwargs: dict, Keyword arguments are passed along as kwargs to - the OAuth2WebServerFlow constructor. - - """ + Args: + client_id: string, client identifier. + client_secret: string client secret. + scope: string or iterable of strings, scope(s) of the credentials + being requested. + auth_uri: string, URI for authorization endpoint. For convenience + defaults to Google's endpoints but any OAuth 2.0 provider + can be used. + token_uri: string, URI for token endpoint. For convenience defaults + to Google's endpoints but any OAuth 2.0 provider can be + used. + revoke_uri: string, URI for revoke endpoint. For convenience + defaults to Google's endpoints but any OAuth 2.0 + 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. + 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. + 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 + callback URI. This is useful with providers + (e.g. wordpress.com) that include extra + fields that the client may want. + _storage_class: "Protected" keyword argument not typically provided + to this constructor. A storage class to aid in + storing a Credentials object for a user in the + datastore. Defaults to StorageByKeyName. + _credentials_class: "Protected" keyword argument not typically + provided to this constructor. A db or ndb Model + class to hold credentials. Defaults to + CredentialsModel. + _credentials_property_name: "Protected" keyword argument not + typically provided to this constructor. + A string indicating the name of the + field on the _credentials_class where a + Credentials object will be stored. + Defaults to 'credentials'. + **kwargs: dict, Keyword arguments are passed along as kwargs to + the OAuth2WebServerFlow constructor. + """ self._tls = threading.local() self.flow = None self.credentials = None @@ -698,13 +715,13 @@ class OAuth2Decorator(object): def oauth_required(self, method): """Decorator that starts the OAuth 2.0 dance. - Starts the OAuth dance for the logged in user if they haven't already - granted access for this application. + Starts the OAuth dance for the logged in user if they haven't already + granted access for this application. - Args: - method: callable, to be decorated method of a webapp.RequestHandler - instance. - """ + Args: + method: callable, to be decorated method of a webapp.RequestHandler + instance. + """ def check_oauth(request_handler, *args, **kwargs): if self._in_error: @@ -741,13 +758,13 @@ class OAuth2Decorator(object): def _create_flow(self, request_handler): """Create the Flow object. - The Flow is calculated lazily since we don't know where this app is - running until it receives a request, at which point redirect_uri can be - calculated and then the Flow object can be constructed. + The Flow is calculated lazily since we don't know where this app is + running until it receives a request, at which point redirect_uri can be + calculated and then the Flow object can be constructed. - Args: - request_handler: webapp.RequestHandler, the request handler. - """ + Args: + request_handler: webapp.RequestHandler, the request handler. + """ if self.flow is None: redirect_uri = request_handler.request.relative_url( self._callback_path) # Usually /oauth2callback @@ -762,16 +779,16 @@ class OAuth2Decorator(object): def oauth_aware(self, method): """Decorator that sets up for OAuth 2.0 dance, but doesn't do it. - Does all the setup for the OAuth dance, but doesn't initiate it. - This decorator is useful if you want to create a page that knows - whether or not the user has granted access to this application. - From within a method decorated with @oauth_aware the has_credentials() - and authorize_url() methods can be called. + Does all the setup for the OAuth dance, but doesn't initiate it. + This decorator is useful if you want to create a page that knows + whether or not the user has granted access to this application. + From within a method decorated with @oauth_aware the has_credentials() + and authorize_url() methods can be called. - Args: - method: callable, to be decorated method of a webapp.RequestHandler - instance. - """ + Args: + method: callable, to be decorated method of a webapp.RequestHandler + instance. + """ def setup_oauth(request_handler, *args, **kwargs): if self._in_error: @@ -801,61 +818,61 @@ class OAuth2Decorator(object): def has_credentials(self): """True if for the logged in user there are valid access Credentials. - Must only be called from with a webapp.RequestHandler subclassed method - that had been decorated with either @oauth_required or @oauth_aware. - """ + Must only be called from with a webapp.RequestHandler subclassed method + that had been decorated with either @oauth_required or @oauth_aware. + """ return self.credentials is not None and not self.credentials.invalid def authorize_url(self): """Returns the URL to start the OAuth dance. - Must only be called from with a webapp.RequestHandler subclassed method - that had been decorated with either @oauth_required or @oauth_aware. - """ + Must only be called from with a webapp.RequestHandler subclassed method + that had been decorated with either @oauth_required or @oauth_aware. + """ url = self.flow.step1_get_authorize_url() return str(url) def http(self, *args, **kwargs): """Returns an authorized http instance. - Must only be called from within an @oauth_required decorated method, or - from within an @oauth_aware decorated method where has_credentials() - returns True. + Must only be called from within an @oauth_required decorated method, or + from within an @oauth_aware decorated method where has_credentials() + returns True. - Args: - *args: Positional arguments passed to httplib2.Http constructor. - **kwargs: Positional arguments passed to httplib2.Http constructor. - """ + Args: + *args: Positional arguments passed to httplib2.Http constructor. + **kwargs: Positional arguments passed to httplib2.Http constructor. + """ return self.credentials.authorize(httplib2.Http(*args, **kwargs)) @property def callback_path(self): """The absolute path where the callback will occur. - Note this is the absolute path, not the absolute URI, that will be - calculated by the decorator at runtime. See callback_handler() for how this - should be used. + Note this is the absolute path, not the absolute URI, that will be + calculated by the decorator at runtime. See callback_handler() for how + this should be used. - Returns: - The callback path as a string. - """ + Returns: + The callback path as a string. + """ return self._callback_path def callback_handler(self): """RequestHandler for the OAuth 2.0 redirect callback. - Usage:: + Usage:: - app = webapp.WSGIApplication([ - ('/index', MyIndexHandler), - ..., - (decorator.callback_path, decorator.callback_handler()) - ]) + app = webapp.WSGIApplication([ + ('/index', MyIndexHandler), + ..., + (decorator.callback_path, decorator.callback_handler()) + ]) - Returns: - A webapp.RequestHandler that handles the redirect back from the - server during the OAuth 2.0 dance. - """ + Returns: + A webapp.RequestHandler that handles the redirect back from the + server during the OAuth 2.0 dance. + """ decorator = self class OAuth2Handler(webapp.RequestHandler): @@ -890,13 +907,13 @@ class OAuth2Decorator(object): def callback_application(self): """WSGI application for handling the OAuth 2.0 redirect callback. - If you need finer grained control use `callback_handler` which returns just - the webapp.RequestHandler. + If you need finer grained control use `callback_handler` which returns + just the webapp.RequestHandler. - Returns: - A webapp.WSGIApplication that handles the redirect back from the - server during the OAuth 2.0 dance. - """ + Returns: + A webapp.WSGIApplication that handles the redirect back from the + server during the OAuth 2.0 dance. + """ return webapp.WSGIApplication([ (self.callback_path, self.callback_handler()) ]) @@ -905,41 +922,42 @@ class OAuth2Decorator(object): class OAuth2DecoratorFromClientSecrets(OAuth2Decorator): """An OAuth2Decorator that builds from a clientsecrets file. - Uses a clientsecrets file as the source for all the information when - constructing an OAuth2Decorator. + Uses a clientsecrets file as the source for all the information when + constructing an OAuth2Decorator. - :: + :: - decorator = OAuth2DecoratorFromClientSecrets( - os.path.join(os.path.dirname(__file__), 'client_secrets.json') - scope='https://www.googleapis.com/auth/plus') + decorator = OAuth2DecoratorFromClientSecrets( + os.path.join(os.path.dirname(__file__), 'client_secrets.json') + scope='https://www.googleapis.com/auth/plus') - class MainHandler(webapp.RequestHandler): - @decorator.oauth_required - def get(self): - http = decorator.http() - # http is authorized with the user's Credentials and can be used - # in API calls + class MainHandler(webapp.RequestHandler): + @decorator.oauth_required + def get(self): + http = decorator.http() + # http is authorized with the user's Credentials and can be + # used in API calls - """ + """ @util.positional(3) def __init__(self, filename, scope, message=None, cache=None, **kwargs): """Constructor - Args: - filename: string, File name of client secrets. - scope: string or iterable of strings, scope(s) of the credentials being - requested. - message: string, A friendly string to display to the user if the - clientsecrets file is missing or invalid. The message may contain HTML - and will be presented on the web interface for any method that uses the - decorator. - cache: An optional cache service client that implements get() and set() - methods. See clientsecrets.loadfile() for details. - **kwargs: dict, Keyword arguments are passed along as kwargs to - the OAuth2WebServerFlow constructor. - """ + Args: + filename: string, File name of client secrets. + scope: string or iterable of strings, scope(s) of the credentials + being requested. + message: string, A friendly string to display to the user if the + clientsecrets file is missing or invalid. The message may + contain HTML and will be presented on the web interface + for any method that uses the decorator. + cache: An optional cache service client that implements get() and + set() + methods. See clientsecrets.loadfile() for details. + **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]: @@ -968,19 +986,18 @@ def oauth2decorator_from_clientsecrets(filename, scope, message=None, cache=None): """Creates an OAuth2Decorator populated from a clientsecrets file. - Args: - filename: string, File name of client secrets. - scope: string or list of strings, scope(s) of the credentials being - requested. - message: string, A friendly string to display to the user if the - clientsecrets file is missing or invalid. The message may contain HTML and - will be presented on the web interface for any method that uses the - decorator. - cache: An optional cache service client that implements get() and set() - methods. See clientsecrets.loadfile() for details. + Args: + filename: string, File name of client secrets. + scope: string or list of strings, scope(s) of the credentials being + requested. + message: string, A friendly string to display to the user if the + clientsecrets file is missing or invalid. The message may + contain HTML and will be presented on the web interface for + any method that uses the decorator. + cache: An optional cache service client that implements get() and set() + methods. See clientsecrets.loadfile() for details. - Returns: An OAuth2Decorator - - """ + Returns: An OAuth2Decorator + """ return OAuth2DecoratorFromClientSecrets(filename, scope, message=message, cache=cache) diff --git a/oauth2client/client.py b/oauth2client/client.py index 45e54fe..e743a91 100644 --- a/oauth2client/client.py +++ b/oauth2client/client.py @@ -180,64 +180,65 @@ class MemoryCache(object): class Credentials(object): """Base class for all Credentials objects. - Subclasses must define an authorize() method that applies the credentials to - an HTTP transport. + Subclasses must define an authorize() method that applies the credentials + to an HTTP transport. - Subclasses must also specify a classmethod named 'from_json' that takes a JSON - string as input and returns an instantiated Credentials object. - """ + Subclasses must also specify a classmethod named 'from_json' that takes a + JSON string as input and returns an instantiated Credentials object. + """ NON_SERIALIZED_MEMBERS = ['store'] def authorize(self, http): """Take an httplib2.Http instance (or equivalent) and authorizes it. - Authorizes it for the set of credentials, usually by replacing - http.request() with a method that adds in the appropriate headers and then - delegates to the original Http.request() method. + Authorizes it for the set of credentials, usually by replacing + http.request() with a method that adds in the appropriate headers and + then delegates to the original Http.request() method. - Args: - http: httplib2.Http, an http object to be used to make the refresh - request. - """ + Args: + http: httplib2.Http, an http object to be used to make the refresh + request. + """ _abstract() def refresh(self, http): """Forces a refresh of the access_token. - Args: - http: httplib2.Http, an http object to be used to make the refresh - request. - """ + Args: + http: httplib2.Http, an http object to be used to make the refresh + request. + """ _abstract() def revoke(self, http): """Revokes a refresh_token and makes the credentials void. - Args: - http: httplib2.Http, an http object to be used to make the revoke - request. - """ + Args: + http: httplib2.Http, an http object to be used to make the revoke + request. + """ _abstract() def apply(self, headers): """Add the authorization to the headers. - Args: - headers: dict, the headers to add the Authorization header to. - """ + Args: + headers: dict, the headers to add the Authorization header to. + """ _abstract() def _to_json(self, strip): """Utility function that creates JSON repr. of a Credentials object. - Args: - strip: array, An array of names of members to not include in the JSON. + Args: + strip: array, An array of names of members to not include in the + JSON. - Returns: - string, a JSON representation of this instance, suitable to pass to - from_json(). - """ + Returns: + string, a JSON representation of this instance, suitable to pass to + from_json(). + """ t = type(self) d = copy.copy(self.__dict__) for member in strip: @@ -259,25 +260,25 @@ class Credentials(object): def to_json(self): """Creating a JSON representation of an instance of Credentials. - Returns: - string, a JSON representation of this instance, suitable to pass to - from_json(). - """ + Returns: + string, a JSON representation of this instance, suitable to pass to + from_json(). + """ return self._to_json(Credentials.NON_SERIALIZED_MEMBERS) @classmethod def new_from_json(cls, s): """Utility class method to instantiate a Credentials subclass from JSON. - Expects the JSON string to have been produced by to_json(). + Expects the JSON string to have been produced by to_json(). - Args: - s: string or bytes, JSON from to_json(). + Args: + s: string or bytes, JSON from to_json(). - Returns: - An instance of the subclass of Credentials that was serialized with - to_json(). - """ + Returns: + An instance of the subclass of Credentials that was serialized with + to_json(). + """ 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. @@ -298,14 +299,14 @@ class Credentials(object): def from_json(cls, unused_data): """Instantiate a Credentials object from a JSON description of it. - The JSON should have been produced by calling .to_json() on the object. + The JSON should have been produced by calling .to_json() on the object. - Args: - unused_data: dict, A deserialized JSON object. + Args: + unused_data: dict, A deserialized JSON object. - Returns: - An instance of a Credentials subclass. - """ + Returns: + An instance of a Credentials subclass. + """ return Credentials() @@ -317,61 +318,61 @@ class Flow(object): class Storage(object): """Base class for all Storage objects. - Store and retrieve a single credential. This class supports locking - such that multiple processes and threads can operate on a single - store. - """ + Store and retrieve a single credential. This class supports locking + such that multiple processes and threads can operate on a single + store. + """ def acquire_lock(self): """Acquires any lock necessary to access this Storage. - This lock is not reentrant. - """ + This lock is not reentrant. + """ pass def release_lock(self): """Release the Storage lock. - Trying to release a lock that isn't held will result in a - RuntimeError. - """ + Trying to release a lock that isn't held will result in a + RuntimeError. + """ pass def locked_get(self): """Retrieve credential. - The Storage lock must be held when this is called. + The Storage lock must be held when this is called. - Returns: - oauth2client.client.Credentials - """ + Returns: + oauth2client.client.Credentials + """ _abstract() def locked_put(self, credentials): """Write a credential. - The Storage lock must be held when this is called. + The Storage lock must be held when this is called. - Args: - credentials: Credentials, the credentials to store. - """ + Args: + credentials: Credentials, the credentials to store. + """ _abstract() def locked_delete(self): """Delete a credential. - The Storage lock must be held when this is called. - """ + The Storage lock must be held when this is called. + """ _abstract() def get(self): """Retrieve credential. - The Storage lock must *not* be held when this is called. + The Storage lock must *not* be held when this is called. - Returns: - oauth2client.client.Credentials - """ + Returns: + oauth2client.client.Credentials + """ self.acquire_lock() try: return self.locked_get() @@ -381,11 +382,11 @@ class Storage(object): def put(self, credentials): """Write a credential. - The Storage lock must be held when this is called. + The Storage lock must be held when this is called. - Args: - credentials: Credentials, the credentials to store. - """ + Args: + credentials: Credentials, the credentials to store. + """ self.acquire_lock() try: self.locked_put(credentials) @@ -395,12 +396,12 @@ class Storage(object): def delete(self): """Delete credential. - Frees any resources associated with storing the credential. - The Storage lock must *not* be held when this is called. + Frees any resources associated with storing the credential. + The Storage lock must *not* be held when this is called. - Returns: - None - """ + Returns: + None + """ self.acquire_lock() try: return self.locked_delete() @@ -411,16 +412,16 @@ class Storage(object): def clean_headers(headers): """Forces header keys and values to be strings, i.e not unicode. - The httplib module just concats the header keys and values in a way that may - make the message header a unicode string, which, if it then tries to - contatenate to a binary request body may result in a unicode decode error. + The httplib module just concats the header keys and values in a way that + may make the message header a unicode string, which, if it then tries to + contatenate to a binary request body may result in a unicode decode error. - Args: - headers: dict, A dictionary of headers. + Args: + headers: dict, A dictionary of headers. - Returns: - The same dictionary but with all the keys converted to strings. - """ + Returns: + The same dictionary but with all the keys converted to strings. + """ clean = {} try: for k, v in six.iteritems(headers): @@ -437,13 +438,13 @@ def clean_headers(headers): def _update_query_params(uri, params): """Updates a URI with new query parameters. - Args: - uri: string, A valid URI, with potential existing query parameters. - params: dict, A dictionary of query parameters. + Args: + uri: string, A valid URI, with potential existing query parameters. + params: dict, A dictionary of query parameters. - Returns: - The same URI but with the new query parameters added. - """ + Returns: + The same URI but with the new query parameters added. + """ parts = urllib.parse.urlparse(uri) query_params = dict(urllib.parse.parse_qsl(parts.query)) query_params.update(params) @@ -454,11 +455,11 @@ def _update_query_params(uri, params): class OAuth2Credentials(Credentials): """Credentials object for OAuth 2.0. - Credentials can be applied to an httplib2.Http object using the authorize() - method, which then adds the OAuth 2.0 access token to each request. + Credentials can be applied to an httplib2.Http object using the authorize() + method, which then adds the OAuth 2.0 access token to each request. - OAuth2Credentials objects may be safely pickled and unpickled. - """ + OAuth2Credentials objects may be safely pickled and unpickled. + """ @util.positional(8) def __init__(self, access_token, client_id, client_secret, refresh_token, @@ -467,33 +468,35 @@ class OAuth2Credentials(Credentials): token_info_uri=None): """Create an instance of OAuth2Credentials. - This constructor is not usually called by the user, instead - OAuth2Credentials objects are instantiated by the OAuth2WebServerFlow. + This constructor is not usually called by the user, instead + OAuth2Credentials objects are instantiated by the OAuth2WebServerFlow. - Args: - access_token: string, access token. - client_id: string, client identifier. - client_secret: string, client secret. - refresh_token: string, refresh token. - token_expiry: datetime, when the access_token expires. - token_uri: string, URI of token endpoint. - user_agent: string, The HTTP User-Agent to provide for this application. - revoke_uri: string, URI for revoke endpoint. Defaults to None; a token - can't be revoked if this is None. - id_token: object, The identity of the resource owner. - token_response: dict, the decoded response to the token request. None - if a token hasn't been requested yet. Stored because some providers - (e.g. wordpress.com) include extra fields that clients may want. - scopes: list, authorized scopes for these credentials. - token_info_uri: string, the URI for the token info endpoint. Defaults to - None; scopes can not be refreshed if this is None. + Args: + access_token: string, access token. + client_id: string, client identifier. + client_secret: string, client secret. + refresh_token: string, refresh token. + token_expiry: datetime, when the access_token expires. + token_uri: string, URI of token endpoint. + user_agent: string, The HTTP User-Agent to provide for this + application. + revoke_uri: string, URI for revoke endpoint. Defaults to None; a + token can't be revoked if this is None. + id_token: object, The identity of the resource owner. + token_response: dict, the decoded response to the token request. + None if a token hasn't been requested yet. Stored + because some providers (e.g. wordpress.com) include + extra fields that clients may want. + scopes: list, authorized scopes for these credentials. + token_info_uri: string, the URI for the token info endpoint. Defaults + to None; scopes can not be refreshed if this is None. - Notes: - store: callable, A callable that when passed a Credential - will store the credential back to where it came from. - This is needed to store the latest access_token if it - has expired and been refreshed. - """ + Notes: + store: callable, A callable that when passed a Credential + will store the credential back to where it came from. + This is needed to store the latest access_token if it + has expired and been refreshed. + """ self.access_token = access_token self.client_id = client_id self.client_secret = client_secret @@ -515,31 +518,31 @@ class OAuth2Credentials(Credentials): def authorize(self, http): """Authorize an httplib2.Http instance with these 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. + 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. - Args: - http: An instance of ``httplib2.Http`` or something that acts - like it. - Returns: - A modified instance of http that was passed in. + Args: + http: An instance of ``httplib2.Http`` or something that acts + like it. - Example:: + Returns: + A modified instance of http that was passed in. - h = httplib2.Http() - h = credentials.authorize(h) + Example:: - You can't create a new OAuth subclass of httplib2.Authentication - because it never gets passed the absolute URI, which is needed for - signing. So instead we have to overload 'request' with a closure - that adds in the Authorization header and then calls the original - version of 'request()'. + h = httplib2.Http() + h = credentials.authorize(h) - """ + You can't create a new OAuth subclass of httplib2.Authentication + because it never gets passed the absolute URI, which is needed for + signing. So instead we have to overload 'request' with a closure + that adds in the Authorization header and then calls the original + version of 'request()'. + """ request_orig = http.request # The closure that will replace 'httplib2.Http.request'. @@ -601,59 +604,60 @@ class OAuth2Credentials(Credentials): def refresh(self, http): """Forces a refresh of the access_token. - Args: - http: httplib2.Http, an http object to be used to make the refresh - request. - """ + Args: + http: httplib2.Http, an http object to be used to make the refresh + request. + """ self._refresh(http.request) def revoke(self, http): """Revokes a refresh_token and makes the credentials void. - Args: - http: httplib2.Http, an http object to be used to make the revoke - request. - """ + Args: + http: httplib2.Http, an http object to be used to make the revoke + request. + """ self._revoke(http.request) def apply(self, headers): """Add the authorization to the headers. - Args: - headers: dict, the headers to add the Authorization header to. - """ + Args: + headers: dict, the headers to add the Authorization header to. + """ headers['Authorization'] = 'Bearer ' + self.access_token def has_scopes(self, scopes): """Verify that the credentials are authorized for the given scopes. - Returns True if the credentials authorized scopes contain all of the scopes - given. + Returns True if the credentials authorized scopes contain all of the + scopes given. - Args: - scopes: list or string, the scopes to check. + Args: + scopes: list or string, the scopes to check. - Notes: - There are cases where the credentials are unaware of which scopes are - authorized. Notably, credentials obtained and stored before this code was - added will not have scopes, AccessTokenCredentials do not have scopes. In - both cases, you can use refresh_scopes() to obtain the canonical set of - scopes. - """ + Notes: + There are cases where the credentials are unaware of which scopes + are authorized. Notably, credentials obtained and stored before + this code was added will not have scopes, AccessTokenCredentials do + not have scopes. In both cases, you can use refresh_scopes() to + obtain the canonical set of scopes. + """ scopes = util.string_to_scopes(scopes) return set(scopes).issubset(self.scopes) def retrieve_scopes(self, http): - """Retrieves the canonical list of scopes for this access token from the - OAuth2 provider. + """Retrieves the canonical list of scopes for this access token. - Args: - http: httplib2.Http, an http object to be used to make the refresh - request. + Gets the scopes from the OAuth2 provider. - Returns: - A set of strings containing the canonical list of scopes. - """ + Args: + http: httplib2.Http, an http object to be used to make the refresh + request. + + Returns: + A set of strings containing the canonical list of scopes. + """ self._retrieve_scopes(http.request) return self.scopes @@ -662,15 +666,16 @@ class OAuth2Credentials(Credentials): @classmethod def from_json(cls, s): - """Instantiate a Credentials object from a JSON description of it. The JSON - should have been produced by calling .to_json() on the object. + """Instantiate a Credentials object from a JSON description of it. - Args: - data: dict, A deserialized JSON object. + The JSON should have been produced by calling .to_json() on the object. - Returns: - An instance of a Credentials subclass. - """ + Args: + data: dict, A deserialized JSON object. + + Returns: + An instance of a Credentials subclass. + """ s = _from_bytes(s) data = json.loads(s) if (data.get('token_expiry') and @@ -700,8 +705,8 @@ class OAuth2Credentials(Credentials): def access_token_expired(self): """True if the credential is expired or invalid. - If the token_expiry isn't set, we assume the token doesn't expire. - """ + If the token_expiry isn't set, we assume the token doesn't expire. + """ if self.invalid: return True @@ -718,9 +723,9 @@ class OAuth2Credentials(Credentials): def get_access_token(self, http=None): """Return the access token and its expiration information. - If the token does not exist, get one. - If the token expired, refresh it. - """ + If the token does not exist, get one. + If the token expired, refresh it. + """ if not self.access_token or self.access_token_expired: if not http: http = httplib2.Http() @@ -731,24 +736,25 @@ class OAuth2Credentials(Credentials): def set_store(self, store): """Set the Storage for the credential. - Args: - store: Storage, an implementation of Storage object. - This is needed to store the latest access_token if it - has expired and been refreshed. This implementation uses - locking to check for updates before updating the - access_token. - """ + Args: + store: Storage, an implementation of Storage object. + This is needed to store the latest access_token if it + has expired and been refreshed. This implementation uses + locking to check for updates before updating the + access_token. + """ self.store = store def _expires_in(self): """Return the number of seconds until this token expires. - If token_expiry is in the past, this method will return 0, meaning the - token has already expired. - If token_expiry is None, this method will return None. Note that returning - 0 in such a case would not be fair: the token may still be valid; - we just don't know anything about it. - """ + If token_expiry is in the past, this method will return 0, meaning the + token has already expired. + + If token_expiry is None, this method will return None. Note that + returning 0 in such a case would not be fair: the token may still be + valid; we just don't know anything about it. + """ if self.token_expiry: now = datetime.datetime.utcnow() if self.token_expiry > now: @@ -798,17 +804,18 @@ class OAuth2Credentials(Credentials): def _refresh(self, http_request): """Refreshes the access_token. - This method first checks by reading the Storage object if available. - If a refresh is still needed, it holds the Storage lock until the - refresh is completed. + This method first checks by reading the Storage object if available. + If a refresh is still needed, it holds the Storage lock until the + refresh is completed. - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the refresh request. + Args: + 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. - """ + Raises: + AccessTokenRefreshError: When the refresh fails. + """ if not self.store: self._do_refresh_request(http_request) else: @@ -829,13 +836,14 @@ class OAuth2Credentials(Credentials): def _do_refresh_request(self, http_request): """Refresh the access_token using the refresh_token. - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the refresh request. + Args: + 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. - """ + Raises: + AccessTokenRefreshError: When the refresh fails. + """ body = self._generate_refresh_request_body() headers = self._generate_refresh_request_headers() @@ -879,24 +887,27 @@ class OAuth2Credentials(Credentials): def _revoke(self, http_request): """Revokes this credential and deletes the stored copy (if it exists). - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the revoke request. - """ + Args: + http_request: callable, a callable that matches the method + signature of httplib2.Http.request, used to make the + revoke request. + """ self._do_revoke(http_request, self.refresh_token or self.access_token) def _do_revoke(self, http_request, token): """Revokes this credential and deletes the stored copy (if it exists). - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the refresh request. - token: A string used as the token to be revoked. Can be either an - access_token or refresh_token. + Args: + http_request: callable, a callable that matches the method + signature of httplib2.Http.request, used to make the + refresh request. + token: A string used as the token to be revoked. Can be either an + access_token or refresh_token. - Raises: - TokenRevokeError: If the revoke request does not return with a 200 OK. - """ + Raises: + TokenRevokeError: If the revoke request does not return with a + 200 OK. + """ logger.info('Revoking token') query_params = {'token': token} token_revoke_uri = _update_query_params(self.revoke_uri, query_params) @@ -919,24 +930,27 @@ class OAuth2Credentials(Credentials): def _retrieve_scopes(self, http_request): """Retrieves the list of authorized scopes from the OAuth2 provider. - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the revoke request. - """ + Args: + http_request: callable, a callable that matches the method + signature of httplib2.Http.request, used to make the + revoke request. + """ self._do_retrieve_scopes(http_request, self.access_token) def _do_retrieve_scopes(self, http_request, token): """Retrieves the list of authorized scopes from the OAuth2 provider. - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the refresh request. - token: A string used as the token to identify the credentials to the - provider. + Args: + http_request: callable, a callable that matches the method + signature of httplib2.Http.request, used to make the + refresh request. + token: A string used as the token to identify the credentials to + the provider. - Raises: - Error: When refresh fails, indicating the the access token is invalid. - """ + Raises: + Error: When refresh fails, indicating the the access token is + invalid. + """ logger.info('Refreshing scopes') query_params = {'access_token': token, 'fields': 'scope'} token_info_uri = _update_query_params(self.token_info_uri, query_params) @@ -959,41 +973,42 @@ class OAuth2Credentials(Credentials): class AccessTokenCredentials(OAuth2Credentials): """Credentials object for OAuth 2.0. - Credentials can be applied to an httplib2.Http object using the - authorize() method, which then signs each request from that object - with the OAuth 2.0 access token. This set of credentials is for the - use case where you have acquired an OAuth 2.0 access_token from - another place such as a JavaScript client or another web - application, and wish to use it from Python. Because only the - access_token is present it can not be refreshed and will in time - expire. + Credentials can be applied to an httplib2.Http object using the + authorize() method, which then signs each request from that object + with the OAuth 2.0 access token. This set of credentials is for the + use case where you have acquired an OAuth 2.0 access_token from + another place such as a JavaScript client or another web + application, and wish to use it from Python. Because only the + access_token is present it can not be refreshed and will in time + expire. - AccessTokenCredentials objects may be safely pickled and unpickled. + AccessTokenCredentials objects may be safely pickled and unpickled. - Usage:: + Usage:: - credentials = AccessTokenCredentials('', - 'my-user-agent/1.0') - http = httplib2.Http() - http = credentials.authorize(http) + credentials = AccessTokenCredentials('', + 'my-user-agent/1.0') + http = httplib2.Http() + http = credentials.authorize(http) - Raises: - AccessTokenCredentialsExpired: raised when the access_token expires or is - revoked. - """ + Raises: + AccessTokenCredentialsExpired: raised when the access_token expires or + is revoked. + """ def __init__(self, access_token, user_agent, revoke_uri=None): """Create an instance of OAuth2Credentials - This is one of the few types if Credentials that you should contrust, - Credentials objects are usually instantiated by a Flow. + This is one of the few types if Credentials that you should contrust, + Credentials objects are usually instantiated by a Flow. - Args: - access_token: string, access token. - user_agent: string, The HTTP User-Agent to provide for this application. - revoke_uri: string, URI for revoke endpoint. Defaults to None; a token - can't be revoked if this is None. - """ + Args: + access_token: string, access token. + user_agent: string, The HTTP User-Agent to provide for this + application. + revoke_uri: string, URI for revoke endpoint. Defaults to None; a + token can't be revoked if this is None. + """ super(AccessTokenCredentials, self).__init__( access_token, None, @@ -1019,23 +1034,25 @@ class AccessTokenCredentials(OAuth2Credentials): def _revoke(self, http_request): """Revokes the access_token and deletes the store if available. - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the revoke request. - """ + Args: + http_request: callable, a callable that matches the method + signature of httplib2.Http.request, used to make the + revoke request. + """ self._do_revoke(http_request, self.access_token) def _detect_gce_environment(urlopen=None): """Determine if the current environment is Compute Engine. - Args: - urlopen: Optional argument. Function used to open a connection to a URL. + Args: + urlopen: Optional argument. Function used to open a connection to a + URL. - Returns: - Boolean indicating whether or not the current environment is Google - Compute Engine. - """ + Returns: + Boolean indicating whether or not the current environment is Google + Compute Engine. + """ urlopen = urlopen or urllib.request.urlopen # Note: the explicit `timeout` below is a workaround. The underlying # issue is that resolving an unknown host on some networks will take @@ -1058,9 +1075,9 @@ def _detect_gce_environment(urlopen=None): def _in_gae_environment(): """Detects if the code is running in the App Engine environment. - Returns: - True if running in the GAE environment, False otherwise. - """ + Returns: + True if running in the GAE environment, False otherwise. + """ if SETTINGS.env_name is not None: return SETTINGS.env_name in ('GAE_PRODUCTION', 'GAE_LOCAL') @@ -1082,12 +1099,13 @@ def _in_gae_environment(): def _in_gce_environment(urlopen=None): """Detect if the code is running in the Compute Engine environment. - Args: - urlopen: Optional argument. Function used to open a connection to a URL. + Args: + urlopen: Optional argument. Function used to open a connection to a + URL. - Returns: - True if running in the GCE environment, False otherwise. - """ + Returns: + True if running in the GCE environment, False otherwise. + """ if SETTINGS.env_name is not None: return SETTINGS.env_name == 'GCE_PRODUCTION' @@ -1100,49 +1118,51 @@ def _in_gce_environment(urlopen=None): class GoogleCredentials(OAuth2Credentials): """Application Default Credentials for use in calling Google APIs. - The Application Default Credentials are being constructed as a function of - the environment where the code is being run. - More details can be found on this page: - https://developers.google.com/accounts/docs/application-default-credentials + The Application Default Credentials are being constructed as a function of + the environment where the code is being run. + More details can be found on this page: + https://developers.google.com/accounts/docs/application-default-credentials - Here is an example of how to use the Application Default Credentials for a - service that requires authentication: + Here is an example of how to use the Application Default Credentials for a + service that requires authentication:: - from googleapiclient.discovery import build - from oauth2client.client import GoogleCredentials + from googleapiclient.discovery import build + from oauth2client.client import GoogleCredentials - credentials = GoogleCredentials.get_application_default() - service = build('compute', 'v1', credentials=credentials) + credentials = GoogleCredentials.get_application_default() + service = build('compute', 'v1', credentials=credentials) - PROJECT = 'bamboo-machine-422' - ZONE = 'us-central1-a' - request = service.instances().list(project=PROJECT, zone=ZONE) - response = request.execute() + PROJECT = 'bamboo-machine-422' + ZONE = 'us-central1-a' + request = service.instances().list(project=PROJECT, zone=ZONE) + response = request.execute() - print(response) - """ + print(response) + """ def __init__(self, access_token, client_id, client_secret, refresh_token, 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 - GoogleCredentials objects are instantiated by - GoogleCredentials.from_stream() or - GoogleCredentials.get_application_default(). + This constructor is not usually called by the user, instead + GoogleCredentials objects are instantiated by + GoogleCredentials.from_stream() or + GoogleCredentials.get_application_default(). - Args: - access_token: string, access token. - client_id: string, client identifier. - client_secret: string, client secret. - refresh_token: string, refresh token. - token_expiry: datetime, when the access_token expires. - token_uri: string, URI of token endpoint. - user_agent: string, The HTTP User-Agent to provide for this application. - revoke_uri: string, URI for revoke endpoint. - Defaults to GOOGLE_REVOKE_URI; a token can't be revoked if this is None. - """ + Args: + access_token: string, access token. + client_id: string, client identifier. + client_secret: string, client secret. + refresh_token: string, refresh token. + token_expiry: datetime, when the access_token expires. + token_uri: string, URI of token endpoint. + user_agent: string, The HTTP User-Agent to provide for this + application. + revoke_uri: string, URI for revoke endpoint. Defaults to + GOOGLE_REVOKE_URI; a token can't be revoked if this + is None. + """ super(GoogleCredentials, self).__init__( access_token, client_id, client_secret, refresh_token, token_expiry, token_uri, user_agent, revoke_uri=revoke_uri) @@ -1150,21 +1170,21 @@ class GoogleCredentials(OAuth2Credentials): def create_scoped_required(self): """Whether this Credentials object is scopeless. - create_scoped(scopes) method needs to be called in order to create - a Credentials object for API calls. - """ + create_scoped(scopes) method needs to be called in order to create + a Credentials object for API calls. + """ return False def create_scoped(self, scopes): """Create a Credentials object for the given scopes. - The Credentials type is preserved. - """ + The Credentials type is preserved. + """ return self @property def serialization_data(self): - """Get the fields and their values identifying the current credentials.""" + """Get the fields and values identifying the current credentials.""" return { 'type': 'authorized_user', 'client_id': self.client_id, @@ -1176,13 +1196,14 @@ class GoogleCredentials(OAuth2Credentials): def _implicit_credentials_from_gae(): """Attempts to get implicit credentials in Google App Engine env. - If the current environment is not detected as App Engine, returns None, - indicating no Google App Engine credentials can be detected from the - current environment. + If the current environment is not detected as App Engine, returns None, + indicating no Google App Engine credentials can be detected from the + current environment. - Returns: - None, if not in GAE, else an appengine.AppAssertionCredentials object. - """ + Returns: + None, if not in GAE, else an appengine.AppAssertionCredentials + object. + """ if not _in_gae_environment(): return None @@ -1192,13 +1213,13 @@ class GoogleCredentials(OAuth2Credentials): def _implicit_credentials_from_gce(): """Attempts to get implicit credentials in Google Compute Engine env. - If the current environment is not detected as Compute Engine, returns None, - indicating no Google Compute Engine credentials can be detected from the - current environment. + If the current environment is not detected as Compute Engine, returns + None, indicating no Google Compute Engine credentials can be detected + from the current environment. - Returns: - None, if not in GCE, else a gce.AppAssertionCredentials object. - """ + Returns: + None, if not in GCE, else a gce.AppAssertionCredentials object. + """ if not _in_gce_environment(): return None @@ -1208,16 +1229,17 @@ class GoogleCredentials(OAuth2Credentials): def _implicit_credentials_from_files(): """Attempts to get implicit credentials from local credential files. - First checks if the environment variable GOOGLE_APPLICATION_CREDENTIALS - is set with a filename and then falls back to a configuration file (the - "well known" file) associated with the 'gcloud' command line tool. + First checks if the environment variable GOOGLE_APPLICATION_CREDENTIALS + is set with a filename and then falls back to a configuration file (the + "well known" file) associated with the 'gcloud' command line tool. - Returns: - Credentials object associated with the GOOGLE_APPLICATION_CREDENTIALS - file or the "well known" file if either exist. If neither file is - define, returns None, indicating no credentials from a file can - detected from the current environment. - """ + Returns: + Credentials object associated with the + GOOGLE_APPLICATION_CREDENTIALS file or the "well known" file if + either exist. If neither file is define, returns None, indicating + no credentials from a file can detected from the current + environment. + """ credentials_filename = _get_environment_variable_file() if not credentials_filename: credentials_filename = _get_well_known_file() @@ -1246,18 +1268,17 @@ class GoogleCredentials(OAuth2Credentials): def _get_implicit_credentials(cls): """Gets credentials implicitly from the environment. - Checks environment in order of precedence: - - Google App Engine (production and testing) - - Environment variable GOOGLE_APPLICATION_CREDENTIALS pointing to - a file with stored credentials information. - - Stored "well known" file associated with `gcloud` command line tool. - - Google Compute Engine production environment. - - Raises: - ApplicationDefaultCredentialsError: raised when the credentials fail - to be retrieved. - """ + Checks environment in order of precedence: + - Google App Engine (production and testing) + - Environment variable GOOGLE_APPLICATION_CREDENTIALS pointing to + a file with stored credentials information. + - Stored "well known" file associated with `gcloud` command line tool. + - Google Compute Engine production environment. + Raises: + ApplicationDefaultCredentialsError: raised when the credentials + fail to be retrieved. + """ # Environ checks (in order). environ_checkers = [ cls._implicit_credentials_from_gae, @@ -1277,27 +1298,26 @@ class GoogleCredentials(OAuth2Credentials): def get_application_default(): """Get the Application Default Credentials for the current environment. - Raises: - ApplicationDefaultCredentialsError: raised when the credentials fail - to be retrieved. - """ + Raises: + ApplicationDefaultCredentialsError: raised when the credentials + fail to be retrieved. + """ return GoogleCredentials._get_implicit_credentials() @staticmethod def from_stream(credential_filename): - """Create a Credentials object by reading the information from a given file. + """Create a Credentials object by reading information from a file. - It returns an object of type GoogleCredentials. + It returns an object of type GoogleCredentials. - Args: - credential_filename: the path to the file from where the credentials - are to be read - - Raises: - ApplicationDefaultCredentialsError: raised when the credentials fail - to be retrieved. - """ + Args: + credential_filename: the path to the file from where the + credentials are to be read + Raises: + ApplicationDefaultCredentialsError: raised when the credentials + fail to be retrieved. + """ if credential_filename and os.path.isfile(credential_filename): try: return _get_application_default_credential_from_file( @@ -1316,10 +1336,10 @@ class GoogleCredentials(OAuth2Credentials): def _save_private_file(filename, json_contents): """Saves a file with read-write permissions on for the owner. - Args: - filename: String. Absolute path to file. - json_contents: JSON serializable object to be saved. - """ + Args: + filename: String. Absolute path to file. + json_contents: JSON serializable object to be saved. + """ temp_filename = tempfile.mktemp() file_desc = os.open(temp_filename, os.O_WRONLY | os.O_CREAT, 0o600) with os.fdopen(file_desc, 'w') as file_handle: @@ -1331,14 +1351,13 @@ def _save_private_file(filename, json_contents): def save_to_well_known_file(credentials, well_known_file=None): """Save the provided GoogleCredentials to the well known file. - Args: - credentials: - the credentials to be saved to the well known file; - it should be an instance of GoogleCredentials - well_known_file: - the name of the file where the credentials are to be saved; - this parameter is supposed to be used for testing only - """ + Args: + credentials: the credentials to be saved to the well known file; + it should be an instance of GoogleCredentials + well_known_file: the name of the file where the credentials are to be + saved; this parameter is supposed to be used for + testing only + """ # TODO(orestica): move this method to tools.py # once the argparse import gets fixed (it is not present in Python 2.6) @@ -1465,13 +1484,13 @@ def _get_application_default_credential_GCE(): class AssertionCredentials(GoogleCredentials): """Abstract Credentials object used for OAuth 2.0 assertion grants. - 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. It must - be subclassed to generate the appropriate assertion string. + 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. It must + be subclassed to generate the appropriate assertion string. - AssertionCredentials objects may be safely pickled and unpickled. - """ + AssertionCredentials objects may be safely pickled and unpickled. + """ @util.positional(2) def __init__(self, assertion_type, user_agent=None, @@ -1480,14 +1499,16 @@ class AssertionCredentials(GoogleCredentials): **unused_kwargs): """Constructor for AssertionFlowCredentials. - Args: - assertion_type: string, assertion type that will be declared to the auth - server - user_agent: string, The HTTP User-Agent to provide for this application. - token_uri: string, URI for token endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - revoke_uri: string, URI for revoke endpoint. - """ + Args: + assertion_type: string, assertion type that will be declared to the + auth server + user_agent: string, The HTTP User-Agent to provide for this + application. + token_uri: string, URI for token endpoint. For convenience defaults + to Google's endpoints but any OAuth 2.0 provider can be + used. + revoke_uri: string, URI for revoke endpoint. + """ super(AssertionCredentials, self).__init__( None, None, @@ -1510,28 +1531,27 @@ class AssertionCredentials(GoogleCredentials): return body def _generate_assertion(self): - """Generate the assertion string that will be used in the access token - request. - """ + """Generate assertion string to be used in the access token request.""" _abstract() def _revoke(self, http_request): """Revokes the access_token and deletes the store if available. - Args: - http_request: callable, a callable that matches the method signature of - httplib2.Http.request, used to make the revoke request. - """ + Args: + http_request: callable, a callable that matches the method + signature of httplib2.Http.request, used to make the + revoke request. + """ self._do_revoke(http_request, self.access_token) def _RequireCryptoOrDie(): """Ensure we have a crypto library, or throw CryptoUnavailableError. - The oauth2client.crypt module requires either PyCrypto or PyOpenSSL - to be available in order to function, but these are optional - dependencies. - """ + The oauth2client.crypt module requires either PyCrypto or PyOpenSSL + to be available in order to function, but these are optional + dependencies. + """ if not HAS_CRYPTO: raise CryptoUnavailableError('No crypto library available') @@ -1539,14 +1559,14 @@ def _RequireCryptoOrDie(): class SignedJwtAssertionCredentials(AssertionCredentials): """Credentials object used for OAuth 2.0 Signed JWT assertion grants. - 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. - SignedJwtAssertionCredentials requires either PyOpenSSL, or PyCrypto - 2.6 or later. For App Engine you may also consider using - AppAssertionCredentials. - """ + SignedJwtAssertionCredentials requires either PyOpenSSL, or PyCrypto + 2.6 or later. For App Engine you may also consider using + AppAssertionCredentials. + """ MAX_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds @@ -1562,23 +1582,26 @@ class SignedJwtAssertionCredentials(AssertionCredentials): **kwargs): """Constructor for SignedJwtAssertionCredentials. - Args: - service_account_name: string, id for account, usually an email address. - private_key: string, private key in PKCS12 or PEM format. - scope: string or iterable of strings, scope(s) of the credentials being - requested. - private_key_password: string, password for private_key, unused if - private_key is in PEM format. - user_agent: string, HTTP User-Agent to provide for this application. - token_uri: string, URI for token endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - revoke_uri: string, URI for revoke endpoint. - kwargs: kwargs, Additional parameters to add to the JWT token, for - example sub=joe@xample.org. + Args: + service_account_name: string, id for account, usually an email + address. + private_key: string, private key in PKCS12 or PEM format. + scope: string or iterable of strings, scope(s) of the credentials + being requested. + private_key_password: string, password for private_key, unused if + private_key is in PEM format. + user_agent: string, HTTP User-Agent to provide for this + application. + token_uri: string, URI for token endpoint. For convenience defaults + to Google's endpoints but any OAuth 2.0 provider can be + used. + revoke_uri: string, URI for revoke endpoint. + kwargs: kwargs, Additional parameters to add to the JWT token, for + example sub=joe@xample.org. - Raises: - CryptoUnavailableError if no crypto library is available. - """ + Raises: + CryptoUnavailableError if no crypto library is available. + """ _RequireCryptoOrDie() super(SignedJwtAssertionCredentials, self).__init__( None, @@ -1639,24 +1662,24 @@ def verify_id_token(id_token, audience, http=None, cert_uri=ID_TOKEN_VERIFICATION_CERTS): """Verifies a signed JWT id_token. - This function requires PyOpenSSL and because of that it does not work on - App Engine. + This function requires PyOpenSSL and because of that it does not work on + App Engine. - Args: - id_token: string, A Signed JWT. - audience: string, The audience 'aud' that the token should be for. - http: httplib2.Http, instance to use to make the HTTP request. Callers - should supply an instance that has caching enabled. - cert_uri: string, URI of the certificates in JSON format to - verify the JWT against. + Args: + id_token: string, A Signed JWT. + audience: string, The audience 'aud' that the token should be for. + http: httplib2.Http, instance to use to make the HTTP request. Callers + should supply an instance that has caching enabled. + cert_uri: string, URI of the certificates in JSON format to + verify the JWT against. - Returns: - The deserialized JSON in the JWT. + Returns: + The deserialized JSON in the JWT. - Raises: - oauth2client.crypt.AppIdentityError: if the JWT fails to verify. - CryptoUnavailableError: if no crypto library is available. - """ + Raises: + oauth2client.crypt.AppIdentityError: if the JWT fails to verify. + CryptoUnavailableError: if no crypto library is available. + """ _RequireCryptoOrDie() if http is None: http = _cached_http @@ -1672,14 +1695,14 @@ def verify_id_token(id_token, audience, http=None, def _extract_id_token(id_token): """Extract the JSON payload from a JWT. - Does the extraction w/o checking the signature. + Does the extraction w/o checking the signature. - Args: - id_token: string or bytestring, OAuth 2.0 id_token. + Args: + id_token: string or bytestring, OAuth 2.0 id_token. - Returns: - object, The deserialized JSON payload. - """ + Returns: + object, The deserialized JSON payload. + """ if type(id_token) == bytes: segments = id_token.split(b'.') else: @@ -1695,16 +1718,16 @@ def _extract_id_token(id_token): def _parse_exchange_token_response(content): """Parses response of an exchange token request. - Most providers return JSON but some (e.g. Facebook) return a - url-encoded string. + Most providers return JSON but some (e.g. Facebook) return a + url-encoded string. - Args: - content: The body of a response + Args: + content: The body of a response - Returns: - Content as a dictionary object. Note that the dict could be empty, - i.e. {}. That basically indicates a failure. - """ + Returns: + Content as a dictionary object. Note that the dict could be empty, + i.e. {}. That basically indicates a failure. + """ resp = {} content = _from_bytes(content) try: @@ -1731,31 +1754,35 @@ def credentials_from_code(client_id, client_secret, scope, code, token_info_uri=GOOGLE_TOKEN_INFO_URI): """Exchanges an authorization code for an OAuth2Credentials object. - Args: - client_id: string, client identifier. - client_secret: string, client secret. - scope: string or iterable of strings, scope(s) to request. - code: string, An authorization code, most likely passed down from - the client - redirect_uri: string, this is generally set to 'postmessage' to match the - redirect_uri that the client specified - http: httplib2.Http, optional http instance to use to do the fetch - token_uri: string, URI for token endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - auth_uri: string, URI for authorization endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - revoke_uri: string, URI for revoke endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - device_uri: string, URI for device authorization endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. + Args: + client_id: string, client identifier. + client_secret: string, client secret. + scope: string or iterable of strings, scope(s) to request. + code: string, An authorization code, most likely passed down from + the client + redirect_uri: string, this is generally set to 'postmessage' to match + the redirect_uri that the client specified + http: httplib2.Http, optional http instance to use to do the fetch + token_uri: string, URI for token endpoint. For convenience defaults + to Google's endpoints but any OAuth 2.0 provider can be + used. + auth_uri: string, URI for authorization endpoint. For convenience + defaults to Google's endpoints but any OAuth 2.0 provider + can be used. + revoke_uri: string, URI for revoke endpoint. For convenience + defaults to Google's endpoints but any OAuth 2.0 provider + can be used. + device_uri: string, URI for device authorization endpoint. For + convenience defaults to Google's endpoints but any OAuth + 2.0 provider can be used. - Returns: - An OAuth2Credentials object. + Returns: + An OAuth2Credentials object. - Raises: - FlowExchangeError if the authorization code cannot be exchanged for an - access token - """ + Raises: + FlowExchangeError if the authorization code cannot be exchanged for an + 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, @@ -1775,35 +1802,38 @@ def credentials_from_clientsecrets_and_code(filename, scope, code, device_uri=None): """Returns OAuth2Credentials from a clientsecrets file and an auth code. - Will create the right kind of Flow based on the contents of the clientsecrets - file or will raise InvalidClientSecretsError for unknown types of Flows. + Will create the right kind of Flow based on the contents of the + clientsecrets file or will raise InvalidClientSecretsError for unknown + types of Flows. - Args: - filename: string, File name of clientsecrets. - scope: string or iterable of strings, scope(s) to request. - code: string, An authorization code, most likely passed down from - the client - message: string, A friendly string to display to the user if the - clientsecrets file is missing or invalid. If message is provided then - sys.exit will be called in the case of an error. If message in not - provided then clientsecrets.InvalidClientSecretsError will be raised. - redirect_uri: string, this is generally set to 'postmessage' to match the - redirect_uri that the client specified - http: httplib2.Http, optional http instance to use to do the fetch - cache: An optional cache service client that implements get() and set() - methods. See clientsecrets.loadfile() for details. - device_uri: string, OAuth 2.0 device authorization endpoint + Args: + filename: string, File name of clientsecrets. + scope: string or iterable of strings, scope(s) to request. + code: string, An authorization code, most likely passed down from + the client + message: string, A friendly string to display to the user if the + clientsecrets file is missing or invalid. If message is + provided then sys.exit will be called in the case of an error. + If message in not provided then + clientsecrets.InvalidClientSecretsError will be raised. + redirect_uri: string, this is generally set to 'postmessage' to match + the redirect_uri that the client specified + http: httplib2.Http, optional http instance to use to do the fetch + cache: An optional cache service client that implements get() and set() + methods. See clientsecrets.loadfile() for details. + device_uri: string, OAuth 2.0 device authorization endpoint - Returns: - An OAuth2Credentials object. + Returns: + An OAuth2Credentials object. - Raises: - FlowExchangeError if the authorization code cannot be exchanged for an - access token - UnknownClientSecretsFlowError if the file describes an unknown kind of Flow. - clientsecrets.InvalidClientSecretsError if the clientsecrets file is - invalid. - """ + Raises: + FlowExchangeError: if the authorization code cannot be exchanged for an + access token + UnknownClientSecretsFlowError: if the file describes an unknown kind + of Flow. + 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) @@ -1820,10 +1850,10 @@ class DeviceFlowInfo(collections.namedtuple('DeviceFlowInfo', ( def FromResponse(cls, response): """Create a DeviceFlowInfo from a server response. - The response should be a dict containing entries as described here: + The response should be a dict containing entries as described here: - http://tools.ietf.org/html/draft-ietf-oauth-v2-05#section-3.7.1 - """ + http://tools.ietf.org/html/draft-ietf-oauth-v2-05#section-3.7.1 + """ # device_code, user_code, and verification_url are required. kwargs = { 'device_code': response['device_code'], @@ -1852,8 +1882,8 @@ class DeviceFlowInfo(collections.namedtuple('DeviceFlowInfo', ( class OAuth2WebServerFlow(Flow): """Does the Web Server Flow for OAuth 2.0. - OAuth2WebServerFlow objects may be safely pickled and unpickled. - """ + OAuth2WebServerFlow objects may be safely pickled and unpickled. + """ @util.positional(4) def __init__(self, client_id, @@ -1871,36 +1901,42 @@ class OAuth2WebServerFlow(Flow): **kwargs): """Constructor for OAuth2WebServerFlow. - The kwargs argument is used to set extra query parameters on the - auth_uri. For example, the access_type and approval_prompt - query parameters can be set via kwargs. + The kwargs argument is used to set extra query parameters on the + auth_uri. For example, the access_type and approval_prompt + query parameters can be set via kwargs. - Args: - client_id: string, client identifier. - client_secret: string client secret. - scope: string or iterable of strings, scope(s) of the credentials being - requested. - 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. - auth_uri: string, URI for authorization endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - token_uri: string, URI for token endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - revoke_uri: string, URI for revoke endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - login_hint: string, Either an email address or domain. Passing this hint - will either pre-fill the email box on the sign-in form or select the - proper multi-login session, thereby simplifying the login flow. - device_uri: string, URI for device authorization endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - authorization_header: string, For use with OAuth 2.0 providers that - require a client to authenticate using a header value instead of passing - client_secret in the POST body. - **kwargs: dict, The keyword arguments are all optional and required - parameters for the OAuth calls. - """ + Args: + client_id: string, client identifier. + client_secret: string client secret. + scope: string or iterable of strings, scope(s) of the credentials + being requested. + 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. + auth_uri: string, URI for authorization endpoint. For convenience + defaults to Google's endpoints but any OAuth 2.0 provider + can be used. + token_uri: string, URI for token endpoint. For convenience + defaults to Google's endpoints but any OAuth 2.0 + provider can be used. + revoke_uri: string, URI for revoke endpoint. For convenience + defaults to Google's endpoints but any OAuth 2.0 + provider can be used. + login_hint: string, Either an email address or domain. Passing this + hint will either pre-fill the email box on the sign-in + form or select the proper multi-login session, thereby + simplifying the login flow. + device_uri: string, URI for device authorization endpoint. For + convenience defaults to Google's endpoints but any + OAuth 2.0 provider can be used. + authorization_header: string, For use with OAuth 2.0 providers that + require a client to authenticate using a + header value instead of passing client_secret + in the POST body. + **kwargs: dict, The keyword arguments are all optional and required + parameters for the OAuth calls. + """ # scope is a required argument, but to preserve backwards-compatibility # we don't want to rearrange the positional arguments if scope is None: @@ -1927,17 +1963,20 @@ class OAuth2WebServerFlow(Flow): def step1_get_authorize_url(self, redirect_uri=None, state=None): """Returns a URI to redirect to the provider. - Args: - 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. This parameter is deprecated, please move to - passing the redirect_uri in via the constructor. - state: string, Opaque state string which is passed through the OAuth2 flow - and returned to the client as a query parameter in the callback. + Args: + 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. + This parameter is deprecated, please move to passing + the redirect_uri in via the constructor. + state: string, Opaque state string which is passed through the + OAuth2 flow and returned to the client as a query parameter + in the callback. - Returns: - A URI as a string to redirect the user to begin the authorization flow. - """ + Returns: + A URI as a string to redirect the user to begin the authorization + flow. + """ if redirect_uri is not None: logger.warning(( 'The redirect_uri parameter for ' @@ -1964,10 +2003,10 @@ class OAuth2WebServerFlow(Flow): def step1_get_device_and_user_codes(self, http=None): """Returns a user code and the verification URL where to enter it - Returns: - A user code as a string for the user to authorize the application - An URL as a string where the user has to enter the code - """ + Returns: + A user code as a string for the user to authorize the application + An URL as a string where the user has to enter the code + """ if self.device_uri is None: raise ValueError('The value of device_uri must not be None.') @@ -2011,27 +2050,25 @@ class OAuth2WebServerFlow(Flow): def step2_exchange(self, code=None, http=None, device_flow_info=None): """Exchanges a code for OAuth2Credentials. - Args: + Args: + code: string, a dict-like object, or None. For a non-device + flow, this is either the response code as a string, or a + dictionary of query parameters to the redirect_uri. For a + device flow, this should be None. + http: httplib2.Http, optional http instance to use when fetching + credentials. + device_flow_info: DeviceFlowInfo, return value from step1 in the + case of a device flow. - code: string, a dict-like object, or None. For a non-device - flow, this is either the response code as a string, or a - dictionary of query parameters to the redirect_uri. For a - device flow, this should be None. - http: httplib2.Http, optional http instance to use when fetching - credentials. - device_flow_info: DeviceFlowInfo, return value from step1 in the - case of a device flow. + Returns: + An OAuth2Credentials object that can be used to authorize requests. - Returns: - An OAuth2Credentials object that can be used to authorize requests. - - Raises: - FlowExchangeError: if a problem occurred exchanging the code for a - refresh_token. - ValueError: if code and device_flow_info are both provided or both - missing. - - """ + Raises: + FlowExchangeError: if a problem occurred exchanging the code for a + refresh_token. + ValueError: if code and device_flow_info are both provided or both + missing. + """ if code is None and device_flow_info is None: raise ValueError('No code or device_flow_info provided.') if code is not None and device_flow_info is not None: @@ -2113,35 +2150,40 @@ def flow_from_clientsecrets(filename, scope, redirect_uri=None, device_uri=None): """Create a Flow from a clientsecrets file. - Will create the right kind of Flow based on the contents of the clientsecrets - file or will raise InvalidClientSecretsError for unknown types of Flows. + Will create the right kind of Flow based on the contents of the + clientsecrets file or will raise InvalidClientSecretsError for unknown + types of Flows. - Args: - filename: string, File name of client secrets. - scope: string or iterable of strings, scope(s) to request. - 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. - message: string, A friendly string to display to the user if the - clientsecrets file is missing or invalid. If message is provided then - sys.exit will be called in the case of an error. If message in not - provided then clientsecrets.InvalidClientSecretsError will be raised. - cache: An optional cache service client that implements get() and set() - methods. See clientsecrets.loadfile() for details. - login_hint: string, Either an email address or domain. Passing this hint - will either pre-fill the email box on the sign-in form or select the - proper multi-login session, thereby simplifying the login flow. - device_uri: string, URI for device authorization endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. + Args: + filename: string, File name of client secrets. + scope: string or iterable of strings, scope(s) to request. + 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. + message: string, A friendly string to display to the user if the + clientsecrets file is missing or invalid. If message is + provided then sys.exit will be called in the case of an error. + If message in not provided then + clientsecrets.InvalidClientSecretsError will be raised. + cache: An optional cache service client that implements get() and set() + methods. See clientsecrets.loadfile() for details. + login_hint: string, Either an email address or domain. Passing this + hint will either pre-fill the email box on the sign-in form + or select the proper multi-login session, thereby + simplifying the login flow. + device_uri: string, URI for device authorization endpoint. For + convenience defaults to Google's endpoints but any + OAuth 2.0 provider can be used. - Returns: - A Flow object. + Returns: + A Flow object. - Raises: - UnknownClientSecretsFlowError if the file describes an unknown kind of Flow. - clientsecrets.InvalidClientSecretsError if the clientsecrets file is - invalid. - """ + Raises: + UnknownClientSecretsFlowError: if the file describes an unknown kind of + Flow. + clientsecrets.InvalidClientSecretsError: if the clientsecrets file is + invalid. + """ try: client_type, client_info = clientsecrets.loadfile(filename, cache=cache) if client_type in (clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED): diff --git a/oauth2client/clientsecrets.py b/oauth2client/clientsecrets.py index efa912a..333210c 100644 --- a/oauth2client/clientsecrets.py +++ b/oauth2client/clientsecrets.py @@ -118,36 +118,36 @@ def _loadfile(filename): def loadfile(filename, cache=None): """Loading of client_secrets JSON file, optionally backed by a cache. - Typical cache storage would be App Engine memcache service, - but you can pass in any other cache client that implements - these methods: + Typical cache storage would be App Engine memcache service, + but you can pass in any other cache client that implements + these methods: - * ``get(key, namespace=ns)`` - * ``set(key, value, namespace=ns)`` + * ``get(key, namespace=ns)`` + * ``set(key, value, namespace=ns)`` - Usage:: + Usage:: - # without caching - client_type, client_info = loadfile('secrets.json') - # using App Engine memcache service - from google.appengine.api import memcache - client_type, client_info = loadfile('secrets.json', cache=memcache) + # without caching + client_type, client_info = loadfile('secrets.json') + # using App Engine memcache service + from google.appengine.api import memcache + client_type, client_info = loadfile('secrets.json', cache=memcache) - Args: - filename: string, Path to a client_secrets.json file on a filesystem. - cache: An optional cache service client that implements get() and set() - methods. If not specified, the file is always being loaded from - a filesystem. + Args: + filename: string, Path to a client_secrets.json file on a filesystem. + cache: An optional cache service client that implements get() and set() + methods. If not specified, the file is always being loaded from + a filesystem. - Raises: - InvalidClientSecretsError: In case of a validation error or some - I/O failure. Can happen only on cache miss. + Raises: + InvalidClientSecretsError: In case of a validation error or some + I/O failure. Can happen only on cache miss. - Returns: - (client_type, client_info) tuple, as _loadfile() normally would. - JSON contents is validated only during first load. Cache hits are not - validated. - """ + Returns: + (client_type, client_info) tuple, as _loadfile() normally would. + JSON contents is validated only during first load. Cache hits are not + validated. + """ _SECRET_NAMESPACE = 'oauth2client:secrets#ns' if not cache: diff --git a/oauth2client/crypt.py b/oauth2client/crypt.py index d5e18f4..8846ec4 100644 --- a/oauth2client/crypt.py +++ b/oauth2client/crypt.py @@ -71,15 +71,15 @@ else: def make_signed_jwt(signer, payload): """Make a signed JWT. - See http://self-issued.info/docs/draft-jones-json-web-token.html. + See http://self-issued.info/docs/draft-jones-json-web-token.html. - Args: - signer: crypt.Signer, Cryptographic signer. - payload: dict, Dictionary of data to convert to JSON and then sign. + Args: + signer: crypt.Signer, Cryptographic signer. + payload: dict, Dictionary of data to convert to JSON and then sign. - Returns: - string, The JWT for the payload. - """ + Returns: + string, The JWT for the payload. + """ header = {'typ': 'JWT', 'alg': 'RS256'} segments = [ @@ -99,20 +99,20 @@ def make_signed_jwt(signer, payload): def verify_signed_jwt_with_certs(jwt, certs, audience): """Verify a JWT against public certs. - See http://self-issued.info/docs/draft-jones-json-web-token.html. + See http://self-issued.info/docs/draft-jones-json-web-token.html. - Args: - jwt: string, A JWT. - certs: dict, Dictionary where values of public keys in PEM format. - audience: string, The audience, 'aud', that this JWT should contain. If - None then the JWT's 'aud' parameter is not verified. + Args: + jwt: string, A JWT. + certs: dict, Dictionary where values of public keys in PEM format. + audience: string, The audience, 'aud', that this JWT should contain. If + None then the JWT's 'aud' parameter is not verified. - Returns: - dict, The deserialized JSON payload in the JWT. + Returns: + dict, The deserialized JSON payload in the JWT. - Raises: - AppIdentityError if any checks are failed. - """ + Raises: + AppIdentityError if any checks are failed. + """ jwt = _to_bytes(jwt) segments = jwt.split(b'.') diff --git a/oauth2client/devshell.py b/oauth2client/devshell.py index 8613dc3..1ebf38c 100644 --- a/oauth2client/devshell.py +++ b/oauth2client/devshell.py @@ -43,12 +43,12 @@ CREDENTIAL_INFO_REQUEST_JSON = '[]' 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: - * 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. - """ + The credential information response from Developer Shell socket is a + 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. + """ def __init__(self, json_string): """Initialize the response data from JSON PBLite array.""" @@ -91,14 +91,15 @@ def _SendRecv(): class DevshellCredentials(client.GoogleCredentials): """Credentials object for Google Developer Shell environment. - This object will allow a Google Developer Shell session to identify its user - to Google and other OAuth 2.0 servers that can verify assertions. It can be - used for the purpose of accessing data stored under the user account. + This object will allow a Google Developer Shell session to identify its + user to Google and other OAuth 2.0 servers that can verify assertions. It + can be used for the purpose of accessing data stored under the user + account. - 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. + """ def __init__(self, user_agent=None): super(DevshellCredentials, self).__init__( diff --git a/oauth2client/django_orm.py b/oauth2client/django_orm.py index afc1e0a..d9e3fb8 100644 --- a/oauth2client/django_orm.py +++ b/oauth2client/django_orm.py @@ -79,23 +79,23 @@ class FlowField(models.Field): class Storage(BaseStorage): - """Store and retrieve a single credential to and from - the datastore. + """Store and retrieve a single credential to and from the datastore. - This Storage helper presumes the Credentials - have been stored as a CredenialsField - on a db model class. - """ + This Storage helper presumes the Credentials + have been stored as a CredenialsField + on a db model class. + """ def __init__(self, model_class, key_name, key_value, property_name): """Constructor for Storage. - 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 - property_name: string, name of the property that is an CredentialsProperty - """ + 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 + property_name: string, name of the property that is an + CredentialsProperty + """ self.model_class = model_class self.key_name = key_name self.key_value = key_value @@ -104,9 +104,9 @@ class Storage(BaseStorage): def locked_get(self): """Retrieve Credential from datastore. - Returns: - oauth2client.Credentials - """ + Returns: + oauth2client.Credentials + """ credential = None query = {self.key_name: self.key_value} @@ -120,11 +120,11 @@ class Storage(BaseStorage): def locked_put(self, credentials, overwrite=False): """Write a Credentials to the datastore. - Args: - credentials: Credentials, the credentials to store. - overwrite: Boolean, indicates whether you would like these credentials to - overwrite any existing stored credentials. - """ + Args: + credentials: Credentials, the credentials to store. + overwrite: Boolean, indicates whether you would like these + credentials to overwrite any existing stored credentials. + """ args = {self.key_name: self.key_value} if overwrite: diff --git a/oauth2client/file.py b/oauth2client/file.py index 1ed6cff..ba6070e 100644 --- a/oauth2client/file.py +++ b/oauth2client/file.py @@ -46,26 +46,27 @@ class Storage(BaseStorage): def acquire_lock(self): """Acquires any lock necessary to access this Storage. - This lock is not reentrant.""" + This lock is not reentrant. + """ self._lock.acquire() def release_lock(self): """Release the Storage lock. - Trying to release a lock that isn't held will result in a - RuntimeError. - """ + Trying to release a lock that isn't held will result in a + RuntimeError. + """ self._lock.release() def locked_get(self): """Retrieve Credential from file. - Returns: - oauth2client.client.Credentials + Returns: + oauth2client.client.Credentials - Raises: - CredentialsFileSymbolicLinkError if the file is a symbolic link. - """ + Raises: + CredentialsFileSymbolicLinkError if the file is a symbolic link. + """ credentials = None self._validate_file() try: @@ -86,9 +87,9 @@ class Storage(BaseStorage): def _create_file_if_needed(self): """Create an empty file if necessary. - This method will not initialize the file. Instead it implements a - simple version of "touch" to ensure the file has been created. - """ + This method will not initialize the file. Instead it implements a + simple version of "touch" to ensure the file has been created. + """ if not os.path.exists(self._filename): old_umask = os.umask(0o177) try: @@ -99,13 +100,12 @@ class Storage(BaseStorage): def locked_put(self, credentials): """Write Credentials to file. - Args: - credentials: Credentials, the credentials to store. - - Raises: - CredentialsFileSymbolicLinkError if the file is a symbolic link. - """ + Args: + credentials: Credentials, the credentials to store. + Raises: + CredentialsFileSymbolicLinkError if the file is a symbolic link. + """ self._create_file_if_needed() self._validate_file() f = open(self._filename, 'w') @@ -115,8 +115,7 @@ class Storage(BaseStorage): def locked_delete(self): """Delete Credentials file. - Args: - credentials: Credentials, the credentials to store. - """ - + Args: + credentials: Credentials, the credentials to store. + """ os.unlink(self._filename) diff --git a/oauth2client/flask_util.py b/oauth2client/flask_util.py index 2771f47..c67d67c 100644 --- a/oauth2client/flask_util.py +++ b/oauth2client/flask_util.py @@ -337,8 +337,10 @@ class UserOAuth2(object): return bp def authorize_view(self): - """Flask view that starts the authorization flow by redirecting the - user to the OAuth2 provider.""" + """Flask view that starts the authorization flow. + + Starts flow by redirecting the user to the OAuth2 provider. + """ args = request.args.to_dict() # Scopes will be passed as mutliple args, and to_dict() will only @@ -355,9 +357,11 @@ class UserOAuth2(object): return redirect(auth_url) def callback_view(self): - """Flask view that handles the user's return from the OAuth2 provider - and exchanges the authorization code for credentials and stores the - credentials.""" + """Flask view that handles the user's return from OAuth2 provider. + + On return, exchanges the authorization code for credentials and stores + the credentials. + """ if 'error' in request.args: reason = request.args.get( 'error_description', request.args.get('error', '')) @@ -429,8 +433,9 @@ class UserOAuth2(object): @property def user_id(self): - """Returns the a unique identifier for the user or None if there are no - credentials. + """Returns the a unique identifier for the user + + Returns None if there are no credentials. The id is provided by the current credentials' id_token. """ diff --git a/oauth2client/gce.py b/oauth2client/gce.py index 08bdecb..5cfaeac 100644 --- a/oauth2client/gce.py +++ b/oauth2client/gce.py @@ -38,24 +38,24 @@ META = ('http://metadata.google.internal/0.1/meta-data/service-accounts/' class AppAssertionCredentials(AssertionCredentials): """Credentials object for Compute Engine Assertion Grants - This object will allow a Compute Engine instance to identify itself to - Google and other OAuth 2.0 servers that can verify assertions. It can be used - for the purpose of accessing data stored under an account assigned to the - Compute Engine instance itself. + This object will allow a Compute Engine instance to identify itself to + Google and other OAuth 2.0 servers that can verify assertions. It can be + 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) def __init__(self, scope, **kwargs): """Constructor for AppAssertionCredentials - Args: - scope: string or iterable of strings, scope(s) of the credentials being - requested. - """ + Args: + scope: string or iterable of strings, scope(s) of the credentials + being requested. + """ self.scope = util.scopes_to_string(scope) self.kwargs = kwargs @@ -70,15 +70,16 @@ class AppAssertionCredentials(AssertionCredentials): def _refresh(self, http_request): """Refreshes the access_token. - Skip all the storage hoops and just refresh using the API. + 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. + Args: + 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. - """ + Raises: + AccessTokenRefreshError: When the refresh fails. + """ query = '?scope=%s' % urllib.parse.quote(self.scope, '') uri = META.replace('{?scope}', query) response, content = http_request(uri) diff --git a/oauth2client/keyring_storage.py b/oauth2client/keyring_storage.py index d2b9a9a..cb6ace2 100644 --- a/oauth2client/keyring_storage.py +++ b/oauth2client/keyring_storage.py @@ -30,32 +30,34 @@ from oauth2client.client import Storage as BaseStorage class Storage(BaseStorage): """Store and retrieve a single credential to and from the keyring. - To use this module you must have the keyring module installed. See - . This is an optional module and is not - installed with oauth2client by default because it does not work on all the - platforms that oauth2client supports, such as Google App Engine. + To use this module you must have the keyring module installed. See + . This is an optional module and is + not installed with oauth2client by default because it does not work on all + the platforms that oauth2client supports, such as Google App Engine. - The keyring module is a cross-platform - library for access the keyring capabilities of the local system. The user will - be prompted for their keyring password when this module is used, and the - manner in which the user is prompted will vary per platform. + The keyring module is a + cross-platform library for access the keyring capabilities of the local + system. The user will be prompted for their keyring password when this + module is used, and the manner in which the user is prompted will vary per + platform. - Usage: - from oauth2client.keyring_storage import Storage + Usage:: - s = Storage('name_of_application', 'user1') - credentials = s.get() + from oauth2client.keyring_storage import Storage - """ + s = Storage('name_of_application', 'user1') + credentials = s.get() + + """ def __init__(self, service_name, user_name): """Constructor. - Args: - service_name: string, The name of the service under which the credentials - are stored. - user_name: string, The name of the user to store credentials for. - """ + Args: + service_name: string, The name of the service under which the + credentials are stored. + user_name: string, The name of the user to store credentials for. + """ self._service_name = service_name self._user_name = user_name self._lock = threading.Lock() @@ -63,23 +65,24 @@ class Storage(BaseStorage): def acquire_lock(self): """Acquires any lock necessary to access this Storage. - This lock is not reentrant.""" + This lock is not reentrant. + """ self._lock.acquire() def release_lock(self): """Release the Storage lock. - Trying to release a lock that isn't held will result in a - RuntimeError. - """ + Trying to release a lock that isn't held will result in a + RuntimeError. + """ self._lock.release() def locked_get(self): """Retrieve Credential from file. - Returns: - oauth2client.client.Credentials - """ + Returns: + oauth2client.client.Credentials + """ credentials = None content = keyring.get_password(self._service_name, self._user_name) @@ -95,16 +98,16 @@ class Storage(BaseStorage): def locked_put(self, credentials): """Write Credentials to file. - Args: - credentials: Credentials, the credentials to store. - """ + Args: + credentials: Credentials, the credentials to store. + """ keyring.set_password(self._service_name, self._user_name, credentials.to_json()) def locked_delete(self): """Delete Credentials file. - Args: - credentials: Credentials, the credentials to store. - """ + Args: + credentials: Credentials, the credentials to store. + """ keyring.set_password(self._service_name, self._user_name, '') diff --git a/oauth2client/locked_file.py b/oauth2client/locked_file.py index cce3359..9455854 100644 --- a/oauth2client/locked_file.py +++ b/oauth2client/locked_file.py @@ -65,11 +65,11 @@ class _Opener(object): def __init__(self, filename, mode, fallback_mode): """Create an Opener. - Args: - filename: string, The pathname of the file. - mode: string, The preferred mode to access the file with. - fallback_mode: string, The mode to use if locking fails. - """ + Args: + filename: string, The pathname of the file. + mode: string, The preferred mode to access the file with. + fallback_mode: string, The mode to use if locking fails. + """ self._locked = False self._filename = filename self._mode = mode @@ -92,10 +92,10 @@ class _Opener(object): def open_and_lock(self, timeout, delay): """Open the file and lock it. - Args: - timeout: float, How long to try to lock for. - delay: float, How long to wait between retries. - """ + Args: + timeout: float, How long to try to lock for. + delay: float, How long to wait between retries. + """ pass def unlock_and_close(self): @@ -109,17 +109,17 @@ class _PosixOpener(_Opener): def open_and_lock(self, timeout, delay): """Open the file and lock it. - Tries to create a .lock file next to the file we're trying to open. + Tries to create a .lock file next to the file we're trying to open. - Args: - timeout: float, How long to try to lock for. - delay: float, How long to wait between retries. + Args: + timeout: float, How long to try to lock for. + delay: float, How long to wait between retries. - Raises: - AlreadyLockedException: if the lock is already acquired. - IOError: if the open fails. - CredentialsFileSymbolicLinkError if the file is a symbolic link. - """ + Raises: + AlreadyLockedException: if the lock is already acquired. + IOError: if the open fails. + CredentialsFileSymbolicLinkError if the file is a symbolic link. + """ if self._locked: raise AlreadyLockedException('File %s is already locked' % self._filename) @@ -182,15 +182,15 @@ try: def open_and_lock(self, timeout, delay): """Open the file and lock it. - Args: - timeout: float, How long to try to lock for. - delay: float, How long to wait between retries + Args: + timeout: float, How long to try to lock for. + delay: float, How long to wait between retries - Raises: - AlreadyLockedException: if the lock is already acquired. - IOError: if the open fails. - CredentialsFileSymbolicLinkError if the file is a symbolic link. - """ + Raises: + AlreadyLockedException: if the lock is already acquired. + IOError: if the open fails. + CredentialsFileSymbolicLinkError if the file is a symbolic link. + """ if self._locked: raise AlreadyLockedException('File %s is already locked' % self._filename) @@ -258,15 +258,16 @@ try: def open_and_lock(self, timeout, delay): """Open the file and lock it. - Args: - timeout: float, How long to try to lock for. - delay: float, How long to wait between retries + Args: + timeout: float, How long to try to lock for. + delay: float, How long to wait between retries - Raises: - AlreadyLockedException: if the lock is already acquired. - IOError: if the open fails. - CredentialsFileSymbolicLinkError if the file is a symbolic link. - """ + Raises: + AlreadyLockedException: if the lock is already acquired. + IOError: if the open fails. + CredentialsFileSymbolicLinkError: if the file is a symbolic + link. + """ if self._locked: raise AlreadyLockedException('File %s is already locked' % self._filename) @@ -333,12 +334,13 @@ class LockedFile(object): def __init__(self, filename, mode, fallback_mode, use_native_locking=True): """Construct a LockedFile. - Args: - filename: string, The path of the file to open. - mode: string, The mode to try to open the file with. - fallback_mode: string, The mode to use if locking fails. - use_native_locking: bool, Whether or not fcntl/win32 locking is used. - """ + Args: + filename: string, The path of the file to open. + mode: string, The mode to try to open the file with. + fallback_mode: string, The mode to use if locking fails. + use_native_locking: bool, Whether or not fcntl/win32 locking is + used. + """ opener = None if not opener and use_native_locking: if _Win32Opener: @@ -366,14 +368,14 @@ class LockedFile(object): def open_and_lock(self, timeout=0, delay=0.05): """Open the file, trying to lock it. - Args: - timeout: float, The number of seconds to try to acquire the lock. - delay: float, The number of seconds to wait between retry attempts. + Args: + timeout: float, The number of seconds to try to acquire the lock. + delay: float, The number of seconds to wait between retry attempts. - Raises: - AlreadyLockedException: if the lock is already acquired. - IOError: if the open fails. - """ + Raises: + AlreadyLockedException: if the lock is already acquired. + IOError: if the open fails. + """ self._opener.open_and_lock(timeout, delay) def unlock_and_close(self): diff --git a/oauth2client/multistore_file.py b/oauth2client/multistore_file.py index ffb9115..e813d5e 100644 --- a/oauth2client/multistore_file.py +++ b/oauth2client/multistore_file.py @@ -26,21 +26,21 @@ The credential themselves are keyed off of: The format of the stored data is like so:: - { - 'file_version': 1, - 'data': [ - { - 'key': { - 'clientId': '', - 'userAgent': '', - 'scope': '' - }, - 'credential': { - # JSON serialized Credentials. - } - } - ] - } + { + 'file_version': 1, + 'data': [ + { + 'key': { + 'clientId': '', + 'userAgent': '', + 'scope': '' + }, + 'credential': { + # JSON serialized Credentials. + } + } + ] + } """ @@ -77,17 +77,17 @@ def get_credential_storage(filename, client_id, user_agent, scope, warn_on_readonly=True): """Get a Storage instance for a credential. - Args: - filename: The JSON file storing a set of credentials - client_id: The client_id for the credential - user_agent: The user agent for the credential - scope: string or iterable of strings, Scope(s) being requested - warn_on_readonly: if True, log a warning if the store is readonly + Args: + filename: The JSON file storing a set of credentials + client_id: The client_id for the credential + user_agent: The user agent for the credential + scope: string or iterable of strings, Scope(s) being requested + warn_on_readonly: if True, log a warning if the store is readonly - Returns: - An object derived from client.Storage for getting/setting the - credential. - """ + Returns: + An object derived from client.Storage for getting/setting the + credential. + """ # Recreate the legacy key with these specific parameters key = {'clientId': client_id, 'userAgent': user_agent, 'scope': util.scopes_to_string(scope)} @@ -100,18 +100,18 @@ 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 - credential storage and retrieval. + Allows you to provide a string as a custom key that will be used for + credential storage and retrieval. - Args: - filename: The JSON file storing a set of credentials - key_string: A string to use as the key for storing this credential. - warn_on_readonly: if True, log a warning if the store is readonly + Args: + filename: The JSON file storing a set of credentials + key_string: A string to use as the key for storing this credential. + warn_on_readonly: if True, log a warning if the store is readonly - Returns: - An object derived from client.Storage for getting/setting the - credential. - """ + Returns: + An object derived from client.Storage for getting/setting the + credential. + """ # Create a key dictionary that can be used key_dict = {'key': key_string} return get_credential_storage_custom_key( @@ -123,20 +123,20 @@ 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 - credential storage and retrieval. + Allows you to provide a dictionary as a custom key that will be used for + credential storage and retrieval. - Args: - filename: The JSON file storing a set of credentials - key_dict: A dictionary to use as the key for storing this credential. There - is no ordering of the keys in the dictionary. Logically equivalent - dictionaries will produce equivalent storage keys. - warn_on_readonly: if True, log a warning if the store is readonly + Args: + filename: The JSON file storing a set of credentials + key_dict: A dictionary to use as the key for storing this credential. + There is no ordering of the keys in the dictionary. Logically + equivalent dictionaries will produce equivalent storage keys. + warn_on_readonly: if True, log a warning if the store is readonly - Returns: - An object derived from client.Storage for getting/setting the - credential. - """ + Returns: + An object derived from client.Storage for getting/setting the + credential. + """ multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly) key = util.dict_to_tuple_key(key_dict) return multistore._get_storage(key) @@ -146,15 +146,15 @@ def get_credential_storage_custom_key( def get_all_credential_keys(filename, warn_on_readonly=True): """Gets all the registered credential keys in the given Multistore. - Args: - filename: The JSON file storing a set of credentials - warn_on_readonly: if True, log a warning if the store is readonly + Args: + filename: The JSON file storing a set of credentials + warn_on_readonly: if True, log a warning if the store is readonly - Returns: - A list of the credential keys present in the file. They are returned as - dictionaries that can be passed into get_credential_storage_custom_key to - get the actual credentials. - """ + Returns: + A list of the credential keys present in the file. They are returned + as dictionaries that can be passed into + get_credential_storage_custom_key to get the actual credentials. + """ multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly) multistore._lock() try: @@ -167,13 +167,13 @@ def get_all_credential_keys(filename, warn_on_readonly=True): def _get_multistore(filename, warn_on_readonly=True): """A helper method to initialize the multistore with proper locking. - Args: - filename: The JSON file storing a set of credentials - warn_on_readonly: if True, log a warning if the store is readonly + Args: + filename: The JSON file storing a set of credentials + warn_on_readonly: if True, log a warning if the store is readonly - Returns: - A multistore object - """ + Returns: + A multistore object + """ filename = os.path.expanduser(filename) _multistores_lock.acquire() try: @@ -191,8 +191,8 @@ class _MultiStore(object): def __init__(self, filename, warn_on_readonly=True): """Initialize the class. - This will create the file if necessary. - """ + This will create the file if necessary. + """ self._file = LockedFile(filename, 'r+', 'r') self._thread_lock = threading.Lock() self._read_only = False @@ -219,26 +219,26 @@ class _MultiStore(object): def acquire_lock(self): """Acquires any lock necessary to access this Storage. - This lock is not reentrant. - """ + This lock is not reentrant. + """ self._multistore._lock() def release_lock(self): """Release the Storage lock. - Trying to release a lock that isn't held will result in a - RuntimeError. - """ + Trying to release a lock that isn't held will result in a + RuntimeError. + """ self._multistore._unlock() def locked_get(self): """Retrieve credential. - The Storage lock must be held when this is called. + The Storage lock must be held when this is called. - Returns: - oauth2client.client.Credentials - """ + Returns: + oauth2client.client.Credentials + """ credential = self._multistore._get_credential(self._key) if credential: credential.set_store(self) @@ -247,29 +247,29 @@ class _MultiStore(object): def locked_put(self, credentials): """Write a credential. - The Storage lock must be held when this is called. + The Storage lock must be held when this is called. - Args: - credentials: Credentials, the credentials to store. - """ + Args: + credentials: Credentials, the credentials to store. + """ self._multistore._update_credential(self._key, credentials) def locked_delete(self): """Delete a credential. - The Storage lock must be held when this is called. + The Storage lock must be held when this is called. - Args: - credentials: Credentials, the credentials to store. - """ + Args: + credentials: Credentials, the credentials to store. + """ self._multistore._delete_credential(self._key) def _create_file_if_needed(self): """Create an empty file if necessary. - This method will not initialize the file. Instead it implements a - simple version of "touch" to ensure the file has been created. - """ + This method will not initialize the file. Instead it implements a + simple version of "touch" to ensure the file has been created. + """ if not os.path.exists(self._file.filename()): old_umask = os.umask(0o177) try: @@ -318,11 +318,11 @@ class _MultiStore(object): def _locked_json_read(self): """Get the raw content of the multistore file. - The multistore must be locked when this is called. + The multistore must be locked when this is called. - Returns: - The contents of the multistore decoded as JSON. - """ + Returns: + The contents of the multistore decoded as JSON. + """ assert self._thread_lock.locked() self._file.file_handle().seek(0) return json.load(self._file.file_handle()) @@ -330,11 +330,11 @@ class _MultiStore(object): def _locked_json_write(self, data): """Write a JSON serializable data structure to the multistore. - The multistore must be locked when this is called. + The multistore must be locked when this is called. - Args: - data: The data to be serialized and written. - """ + Args: + data: The data to be serialized and written. + """ assert self._thread_lock.locked() if self._read_only: return @@ -345,12 +345,12 @@ class _MultiStore(object): def _refresh_data_cache(self): """Refresh the contents of the multistore. - The multistore must be locked when this is called. + The multistore must be locked when this is called. - Raises: - NewerCredentialStoreError: Raised when a newer client has written the - store. - """ + Raises: + NewerCredentialStoreError: Raised when a newer client has written + the store. + """ self._data = {} try: raw_data = self._locked_json_read() @@ -387,13 +387,13 @@ class _MultiStore(object): def _decode_credential_from_json(self, cred_entry): """Load a credential from our JSON serialization. - Args: - cred_entry: A dict entry from the data member of our format + Args: + cred_entry: A dict entry from the data member of our format - Returns: - (key, cred) where the key is the key tuple and the cred is the - OAuth2Credential object. - """ + Returns: + (key, cred) where the key is the key tuple and the cred is the + OAuth2Credential object. + """ raw_key = cred_entry['key'] key = util.dict_to_tuple_key(raw_key) credential = None @@ -403,8 +403,8 @@ class _MultiStore(object): def _write(self): """Write the cached data back out. - The multistore must be locked. - """ + The multistore must be locked. + """ raw_data = {'file_version': 1} raw_creds = [] raw_data['data'] = raw_creds @@ -417,44 +417,45 @@ class _MultiStore(object): def _get_all_credential_keys(self): """Gets all the registered credential keys in the multistore. - Returns: - A list of dictionaries corresponding to all the keys currently registered - """ + Returns: + A list of dictionaries corresponding to all the keys currently + registered + """ return [dict(key) for key in self._data.keys()] def _get_credential(self, key): """Get a credential from the multistore. - The multistore must be locked. + The multistore must be locked. - Args: - key: The key used to retrieve the credential + Args: + key: The key used to retrieve the credential - Returns: - The credential specified or None if not present - """ + Returns: + The credential specified or None if not present + """ return self._data.get(key, None) def _update_credential(self, key, cred): """Update a credential and write the multistore. - This must be called when the multistore is locked. + This must be called when the multistore is locked. - Args: - key: The key used to retrieve the credential - cred: The OAuth2Credential to update/set - """ + Args: + key: The key used to retrieve the credential + cred: The OAuth2Credential to update/set + """ self._data[key] = cred self._write() def _delete_credential(self, key): """Delete a credential and write the multistore. - This must be called when the multistore is locked. + This must be called when the multistore is locked. - Args: - key: The key used to retrieve the credential - """ + Args: + key: The key used to retrieve the credential + """ try: del self._data[key] except KeyError: @@ -464,12 +465,12 @@ class _MultiStore(object): def _get_storage(self, key): """Get a Storage object to get/set a credential. - This Storage is a 'view' into the multistore. + This Storage is a 'view' into the multistore. - Args: - key: The key used to retrieve the credential + Args: + key: The key used to retrieve the credential - Returns: - A Storage object that can be used to get/set this cred - """ + Returns: + A Storage object that can be used to get/set this cred + """ return self._Storage(self, key) diff --git a/oauth2client/old_run.py b/oauth2client/old_run.py index 2faf068..02b855d 100644 --- a/oauth2client/old_run.py +++ b/oauth2client/old_run.py @@ -49,42 +49,42 @@ gflags.DEFINE_multi_int('auth_host_port', [8080, 8090], def run(flow, storage, http=None): """Core code for a command-line application. - The ``run()`` function is called from your application and runs - through all the steps to obtain credentials. It takes a ``Flow`` - argument and attempts to open an authorization server page in the - user's default web browser. The server asks the user to grant your - application access to the user's data. If the user grants access, - the ``run()`` function returns new credentials. The new credentials - are also stored in the ``storage`` argument, which updates the file - associated with the ``Storage`` object. + The ``run()`` function is called from your application and runs + through all the steps to obtain credentials. It takes a ``Flow`` + argument and attempts to open an authorization server page in the + user's default web browser. The server asks the user to grant your + application access to the user's data. If the user grants access, + the ``run()`` function returns new credentials. The new credentials + are also stored in the ``storage`` argument, which updates the file + associated with the ``Storage`` object. - It presumes it is run from a command-line application and supports the - following flags: + It presumes it is run from a command-line application and supports the + following flags: - ``--auth_host_name`` (string, default: ``localhost``) - Host name to use when running a local web server to handle - redirects during OAuth authorization. + ``--auth_host_name`` (string, default: ``localhost``) + Host name to use when running a local web server to handle + redirects during OAuth authorization. - ``--auth_host_port`` (integer, default: ``[8080, 8090]``) - Port to use when running a local web server to handle redirects - during OAuth authorization. Repeat this option to specify a list - of values. + ``--auth_host_port`` (integer, default: ``[8080, 8090]``) + Port to use when running a local web server to handle redirects + during OAuth authorization. Repeat this option to specify a list + of values. - ``--[no]auth_local_webserver`` (boolean, default: ``True``) - Run a local web server to handle redirects during OAuth authorization. + ``--[no]auth_local_webserver`` (boolean, default: ``True``) + 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()``. + Since it uses flags make sure to initialize the ``gflags`` module before + calling ``run()``. - Args: - flow: Flow, an OAuth 2.0 Flow to step through. - storage: Storage, a ``Storage`` to store the credential in. - http: An instance of ``httplib2.Http.request`` or something that acts - like it. + Args: + flow: Flow, an OAuth 2.0 Flow to step through. + storage: Storage, a ``Storage`` to store the credential in. + http: An instance of ``httplib2.Http.request`` or something that acts + like it. - Returns: - Credentials, the obtained credential. - """ + Returns: + 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.') diff --git a/oauth2client/tools.py b/oauth2client/tools.py index bd77020..0d170a6 100644 --- a/oauth2client/tools.py +++ b/oauth2client/tools.py @@ -73,26 +73,26 @@ argparser = _CreateArgumentParser() class ClientRedirectServer(BaseHTTPServer.HTTPServer): """A server to handle OAuth 2.0 redirects back to localhost. - Waits for a single request and parses the query parameters - into query_params and then stops serving. - """ + Waits for a single request and parses the query parameters + into query_params and then stops serving. + """ query_params = {} class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler): """A handler for OAuth 2.0 redirects back to localhost. - Waits for a single request and parses the query parameters - into the servers query_params and then stops serving. - """ + Waits for a single request and parses the query parameters + into the servers query_params and then stops serving. + """ def do_GET(self): """Handle a GET request. - Parses the query parameters and prints a message - if the flow has completed. Note that we can't detect - if an error occurred. - """ + Parses the query parameters and prints a message + if the flow has completed. Note that we can't detect + if an error occurred. + """ self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() @@ -111,54 +111,53 @@ class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler): def run_flow(flow, storage, flags, http=None): """Core code for a command-line application. - The ``run()`` function is called from your application and runs - through all the steps to obtain credentials. It takes a ``Flow`` - argument and attempts to open an authorization server page in the - user's default web browser. The server asks the user to grant your - application access to the user's data. If the user grants access, - the ``run()`` function returns new credentials. The new credentials - are also stored in the ``storage`` argument, which updates the file - associated with the ``Storage`` object. + The ``run()`` function is called from your application and runs + through all the steps to obtain credentials. It takes a ``Flow`` + argument and attempts to open an authorization server page in the + user's default web browser. The server asks the user to grant your + application access to the user's data. If the user grants access, + the ``run()`` function returns new credentials. The new credentials + are also stored in the ``storage`` argument, which updates the file + associated with the ``Storage`` object. - It presumes it is run from a command-line application and supports the - following flags: + It presumes it is run from a command-line application and supports the + following flags: - ``--auth_host_name`` (string, default: ``localhost``) - Host name to use when running a local web server to handle - redirects during OAuth authorization. + ``--auth_host_name`` (string, default: ``localhost``) + Host name to use when running a local web server to handle + redirects during OAuth authorization. - ``--auth_host_port`` (integer, default: ``[8080, 8090]``) - Port to use when running a local web server to handle redirects - during OAuth authorization. Repeat this option to specify a list - of values. + ``--auth_host_port`` (integer, default: ``[8080, 8090]``) + Port to use when running a local web server to handle redirects + during OAuth authorization. Repeat this option to specify a list + of values. - ``--[no]auth_local_webserver`` (boolean, default: ``True``) - Run a local web server to handle redirects during OAuth authorization. + ``--[no]auth_local_webserver`` (boolean, default: ``True``) + 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:: + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + parents=[tools.argparser]) + flags = parser.parse_args(argv) + Args: + flow: Flow, an OAuth 2.0 Flow to step through. + storage: Storage, a ``Storage`` to store the credential in. + flags: ``argparse.Namespace``, The command-line flags. This is the + object returned from calling ``parse_args()`` on + ``argparse.ArgumentParser`` as described above. + http: An instance of ``httplib2.Http.request`` or something that + acts like it. - 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__, - formatter_class=argparse.RawDescriptionHelpFormatter, - parents=[tools.argparser]) - flags = parser.parse_args(argv) - - Args: - flow: Flow, an OAuth 2.0 Flow to step through. - storage: Storage, a ``Storage`` to store the credential in. - flags: ``argparse.Namespace``, The command-line flags. This is the - object returned from calling ``parse_args()`` on - ``argparse.ArgumentParser`` as described above. - http: An instance of ``httplib2.Http.request`` or something that - acts like it. - - Returns: - Credentials, the obtained credential. - """ + Returns: + Credentials, the obtained credential. + """ logging.getLogger().setLevel(getattr(logging, flags.logging_level)) if not flags.noauth_local_webserver: success = False diff --git a/oauth2client/util.py b/oauth2client/util.py index 75abc03..882ff10 100644 --- a/oauth2client/util.py +++ b/oauth2client/util.py @@ -52,73 +52,74 @@ positional_parameters_enforcement = POSITIONAL_WARNING def positional(max_positional_args): """A decorator to declare that only the first N arguments my be positional. - This decorator makes it easy to support Python 3 style keyword-only - parameters. For example, in Python 3 it is possible to write:: + This decorator makes it easy to support Python 3 style keyword-only + parameters. For example, in Python 3 it is possible to write:: - def fn(pos1, *, kwonly1=None, kwonly1=None): - ... + def fn(pos1, *, kwonly1=None, kwonly1=None): + ... - All named parameters after ``*`` must be a keyword:: + All named parameters after ``*`` must be a keyword:: - fn(10, 'kw1', 'kw2') # Raises exception. - fn(10, kwonly1='kw1') # Ok. + fn(10, 'kw1', 'kw2') # Raises exception. + fn(10, kwonly1='kw1') # Ok. - Example - ^^^^^^^ + Example + ^^^^^^^ - To define a function like above, do:: + To define a function like above, do:: - @positional(1) - def fn(pos1, kwonly1=None, kwonly2=None): - ... + @positional(1) + def fn(pos1, kwonly1=None, kwonly2=None): + ... - If no default value is provided to a keyword argument, it becomes a required - keyword argument:: + If no default value is provided to a keyword argument, it becomes a + required keyword argument:: - @positional(0) - def fn(required_kw): - ... + @positional(0) + def fn(required_kw): + ... - This must be called with the keyword parameter:: + This must be called with the keyword parameter:: - fn() # Raises exception. - fn(10) # Raises exception. - fn(required_kw=10) # Ok. + fn() # Raises exception. + fn(10) # Raises exception. + fn(required_kw=10) # Ok. - When defining instance or class methods always remember to account for - ``self`` and ``cls``:: + When defining instance or class methods always remember to account for + ``self`` and ``cls``:: - class MyClass(object): + class MyClass(object): - @positional(2) - def my_method(self, pos1, kwonly1=None): - ... + @positional(2) + def my_method(self, pos1, kwonly1=None): + ... - @classmethod - @positional(2) - def my_method(cls, pos1, kwonly1=None): - ... + @classmethod + @positional(2) + def my_method(cls, pos1, kwonly1=None): + ... - The positional decorator behavior is controlled by - ``util.positional_parameters_enforcement``, which may be set to - ``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or - ``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do - nothing, respectively, if a declaration is violated. + The positional decorator behavior is controlled by + ``util.positional_parameters_enforcement``, which may be set to + ``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or + ``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do + nothing, respectively, if a declaration is violated. - Args: - max_positional_arguments: Maximum number of positional arguments. All - parameters after the this index must be keyword only. + Args: + max_positional_arguments: Maximum number of positional arguments. All + parameters after the this index must be + keyword only. - Returns: - A decorator that prevents using arguments after max_positional_args from - being used as positional parameters. + Returns: + A decorator that prevents using arguments after max_positional_args + from being used as positional parameters. - Raises: - TypeError if a key-word only argument is provided as a positional - parameter, but only if util.positional_parameters_enforcement is set to - POSITIONAL_EXCEPTION. - - """ + Raises: + TypeError: if a key-word only argument is provided as a positional + parameter, but only if + util.positional_parameters_enforcement is set to + POSITIONAL_EXCEPTION. + """ def positional_decorator(wrapped): @functools.wraps(wrapped) @@ -148,16 +149,16 @@ def positional(max_positional_args): def scopes_to_string(scopes): """Converts scope value to a string. - If scopes is a string then it is simply passed through. If scopes is an - iterable then a string is returned that is all the individual scopes - concatenated with spaces. + If scopes is a string then it is simply passed through. If scopes is an + iterable then a string is returned that is all the individual scopes + concatenated with spaces. - Args: - scopes: string or iterable of strings, the scopes. + Args: + scopes: string or iterable of strings, the scopes. - Returns: - The scopes formatted as a single string. - """ + Returns: + The scopes formatted as a single string. + """ if isinstance(scopes, six.string_types): return scopes else: @@ -167,15 +168,15 @@ def scopes_to_string(scopes): def string_to_scopes(scopes): """Converts stringifed scope value to a list. - If scopes is a list then it is simply passed through. If scopes is an - string then a list of each individual scope is returned. + If scopes is a list then it is simply passed through. If scopes is an + string then a list of each individual scope is returned. - Args: - scopes: a string or iterable of strings, the scopes. + Args: + scopes: a string or iterable of strings, the scopes. - Returns: - The scopes in a list. - """ + Returns: + The scopes in a list. + """ if not scopes: return [] if isinstance(scopes, six.string_types): @@ -187,31 +188,31 @@ def string_to_scopes(scopes): def dict_to_tuple_key(dictionary): """Converts a dictionary to a tuple that can be used as an immutable key. - The resulting key is always sorted so that logically equivalent dictionaries - always produce an identical tuple for a key. + The resulting key is always sorted so that logically equivalent + dictionaries always produce an identical tuple for a key. - Args: - dictionary: the dictionary to use as the key. + Args: + dictionary: the dictionary to use as the key. - Returns: - A tuple representing the dictionary in it's naturally sorted ordering. - """ + Returns: + A tuple representing the dictionary in it's naturally sorted ordering. + """ return tuple(sorted(dictionary.items())) def _add_query_parameter(url, name, value): """Adds a query parameter to a url. - Replaces the current value if it already exists in the URL. + Replaces the current value if it already exists in the URL. - Args: - url: string, url to add the query parameter to. - name: string, query parameter name. - value: string, query parameter value. + Args: + url: string, url to add the query parameter to. + name: string, query parameter name. + value: string, query parameter value. - Returns: - Updated query parameter. Does not update the url if value is None. - """ + Returns: + Updated query parameter. Does not update the url if value is None. + """ if value is None: return url else: diff --git a/oauth2client/xsrfutil.py b/oauth2client/xsrfutil.py index 9cd59d6..013db11 100644 --- a/oauth2client/xsrfutil.py +++ b/oauth2client/xsrfutil.py @@ -47,17 +47,17 @@ def _force_bytes(s): def generate_token(key, user_id, action_id="", when=None): """Generates a URL-safe token for the given user, action, time tuple. - Args: - key: secret key to use. - user_id: the user ID of the authenticated user. - action_id: a string identifier of the action they requested - authorization for. - when: the time in seconds since the epoch at which the user was - authorized for this action. If not set the current time is used. + Args: + key: secret key to use. + user_id: the user ID of the authenticated user. + action_id: a string identifier of the action they requested + authorization for. + when: the time in seconds since the epoch at which the user was + authorized for this action. If not set the current time is used. - Returns: - A string XSRF protection token. - """ + Returns: + A string XSRF protection token. + """ when = _force_bytes(when or int(time.time())) digester = hmac.new(_force_bytes(key)) digester.update(_force_bytes(user_id)) @@ -75,20 +75,20 @@ def generate_token(key, user_id, action_id="", when=None): def validate_token(key, token, user_id, action_id="", current_time=None): """Validates that the given token authorizes the user for the action. - Tokens are invalid if the time of issue is too old or if the token - does not match what generateToken outputs (i.e. the token was forged). + Tokens are invalid if the time of issue is too old or if the token + does not match what generateToken outputs (i.e. the token was forged). - Args: - key: secret key to use. - token: a string of the token generated by generateToken. - user_id: the user ID of the authenticated user. - action_id: a string identifier of the action they requested - authorization for. + Args: + key: secret key to use. + token: a string of the token generated by generateToken. + user_id: the user ID of the authenticated user. + action_id: a string identifier of the action they requested + authorization for. - Returns: - A boolean - True if the user is authorized for the action, False - otherwise. - """ + Returns: + A boolean - True if the user is authorized for the action, False + otherwise. + """ if not token: return False try: