Docstring pass after pep8ify in oauth2client/
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||
|
||||
@@ -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'.')
|
||||
|
||||
|
||||
@@ -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__(
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
<http://pypi.python.org/pypi/keyring/>. 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
|
||||
<http://pypi.python.org/pypi/keyring/>. 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 <http://pypi.python.org/pypi/keyring/> 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 <http://pypi.python.org/pypi/keyring/> 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, '')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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': '<client id>',
|
||||
'userAgent': '<user agent>',
|
||||
'scope': '<scope>'
|
||||
},
|
||||
'credential': {
|
||||
# JSON serialized Credentials.
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
'file_version': 1,
|
||||
'data': [
|
||||
{
|
||||
'key': {
|
||||
'clientId': '<client id>',
|
||||
'userAgent': '<user agent>',
|
||||
'scope': '<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)
|
||||
|
||||
@@ -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.')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user