Docstring pass after pep8ify in oauth2client/
This commit is contained in:
@@ -21,15 +21,17 @@ import six
|
|||||||
def _parse_pem_key(raw_key_input):
|
def _parse_pem_key(raw_key_input):
|
||||||
"""Identify and extract PEM keys.
|
"""Identify and extract PEM keys.
|
||||||
|
|
||||||
Determines whether the given key is in the format of PEM key, and extracts
|
Determines whether the given key is in the format of PEM key, and extracts
|
||||||
the relevant part of the key if it is.
|
the relevant part of the key if it is.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
raw_key_input: The contents of a private key file (either PEM or PKCS12).
|
raw_key_input: The contents of a private key file (either PEM or
|
||||||
|
PKCS12).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
string, The actual key if the contents are from a PEM file, or else None.
|
string, The actual key if the contents are from a PEM file, or
|
||||||
"""
|
else None.
|
||||||
|
"""
|
||||||
offset = raw_key_input.find(b'-----BEGIN ')
|
offset = raw_key_input.find(b'-----BEGIN ')
|
||||||
if offset != -1:
|
if offset != -1:
|
||||||
return raw_key_input[offset:]
|
return raw_key_input[offset:]
|
||||||
@@ -42,24 +44,24 @@ def _json_encode(data):
|
|||||||
def _to_bytes(value, encoding='ascii'):
|
def _to_bytes(value, encoding='ascii'):
|
||||||
"""Converts a string value to bytes, if necessary.
|
"""Converts a string value to bytes, if necessary.
|
||||||
|
|
||||||
Unfortunately, ``six.b`` is insufficient for this task since in
|
Unfortunately, ``six.b`` is insufficient for this task since in
|
||||||
Python2 it does not modify ``unicode`` objects.
|
Python2 it does not modify ``unicode`` objects.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: The string/bytes value to be converted.
|
value: The string/bytes value to be converted.
|
||||||
encoding: The encoding to use to convert unicode to bytes. Defaults
|
encoding: The encoding to use to convert unicode to bytes. Defaults
|
||||||
to "ascii", which will not allow any characters from ordinals
|
to "ascii", which will not allow any characters from ordinals
|
||||||
larger than 127. Other useful values are "latin-1", which
|
larger than 127. Other useful values are "latin-1", which
|
||||||
which will only allows byte ordinals (up to 255) and "utf-8",
|
which will only allows byte ordinals (up to 255) and "utf-8",
|
||||||
which will encode any unicode that needs to be.
|
which will encode any unicode that needs to be.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The original value converted to bytes (if unicode) or as passed in
|
The original value converted to bytes (if unicode) or as passed in
|
||||||
if it started out as bytes.
|
if it started out as bytes.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError if the value could not be converted to bytes.
|
ValueError if the value could not be converted to bytes.
|
||||||
"""
|
"""
|
||||||
result = (value.encode(encoding)
|
result = (value.encode(encoding)
|
||||||
if isinstance(value, six.text_type) else value)
|
if isinstance(value, six.text_type) else value)
|
||||||
if isinstance(result, six.binary_type):
|
if isinstance(result, six.binary_type):
|
||||||
@@ -71,16 +73,16 @@ def _to_bytes(value, encoding='ascii'):
|
|||||||
def _from_bytes(value):
|
def _from_bytes(value):
|
||||||
"""Converts bytes to a string value, if necessary.
|
"""Converts bytes to a string value, if necessary.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: The string/bytes value to be converted.
|
value: The string/bytes value to be converted.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The original value converted to unicode (if bytes) or as passed in
|
The original value converted to unicode (if bytes) or as passed in
|
||||||
if it started out as unicode.
|
if it started out as unicode.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError if the value could not be converted to unicode.
|
ValueError if the value could not be converted to unicode.
|
||||||
"""
|
"""
|
||||||
result = (value.decode('utf-8')
|
result = (value.decode('utf-8')
|
||||||
if isinstance(value, six.binary_type) else value)
|
if isinstance(value, six.binary_type) else value)
|
||||||
if isinstance(result, six.text_type):
|
if isinstance(result, six.text_type):
|
||||||
|
|||||||
@@ -27,24 +27,24 @@ class OpenSSLVerifier(object):
|
|||||||
def __init__(self, pubkey):
|
def __init__(self, pubkey):
|
||||||
"""Constructor.
|
"""Constructor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pubkey, OpenSSL.crypto.PKey, The public key to verify with.
|
pubkey: OpenSSL.crypto.PKey, The public key to verify with.
|
||||||
"""
|
"""
|
||||||
self._pubkey = pubkey
|
self._pubkey = pubkey
|
||||||
|
|
||||||
def verify(self, message, signature):
|
def verify(self, message, signature):
|
||||||
"""Verifies a message against a signature.
|
"""Verifies a message against a signature.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: string or bytes, The message to verify. If string, will be
|
message: string or bytes, The message to verify. If string, will be
|
||||||
encoded to bytes as utf-8.
|
encoded to bytes as utf-8.
|
||||||
signature: string or bytes, The signature on the message. If string,
|
signature: string or bytes, The signature on the message. If string,
|
||||||
will be encoded to bytes as utf-8.
|
will be encoded to bytes as utf-8.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if message was signed by the private key associated with the public
|
True if message was signed by the private key associated with the
|
||||||
key that this object was constructed with.
|
public key that this object was constructed with.
|
||||||
"""
|
"""
|
||||||
message = _to_bytes(message, encoding='utf-8')
|
message = _to_bytes(message, encoding='utf-8')
|
||||||
signature = _to_bytes(signature, encoding='utf-8')
|
signature = _to_bytes(signature, encoding='utf-8')
|
||||||
try:
|
try:
|
||||||
@@ -57,17 +57,17 @@ class OpenSSLVerifier(object):
|
|||||||
def from_string(key_pem, is_x509_cert):
|
def from_string(key_pem, is_x509_cert):
|
||||||
"""Construct a Verified instance from a string.
|
"""Construct a Verified instance from a string.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key_pem: string, public key in PEM format.
|
key_pem: string, public key in PEM format.
|
||||||
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it is
|
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it
|
||||||
expected to be an RSA key in PEM format.
|
is expected to be an RSA key in PEM format.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Verifier instance.
|
Verifier instance.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
OpenSSL.crypto.Error if the key_pem can't be parsed.
|
OpenSSL.crypto.Error: if the key_pem can't be parsed.
|
||||||
"""
|
"""
|
||||||
if is_x509_cert:
|
if is_x509_cert:
|
||||||
pubkey = crypto.load_certificate(crypto.FILETYPE_PEM, key_pem)
|
pubkey = crypto.load_certificate(crypto.FILETYPE_PEM, key_pem)
|
||||||
else:
|
else:
|
||||||
@@ -81,20 +81,20 @@ class OpenSSLSigner(object):
|
|||||||
def __init__(self, pkey):
|
def __init__(self, pkey):
|
||||||
"""Constructor.
|
"""Constructor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with.
|
pkey: OpenSSL.crypto.PKey (or equiv), The private key to sign with.
|
||||||
"""
|
"""
|
||||||
self._key = pkey
|
self._key = pkey
|
||||||
|
|
||||||
def sign(self, message):
|
def sign(self, message):
|
||||||
"""Signs a message.
|
"""Signs a message.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: bytes, Message to be signed.
|
message: bytes, Message to be signed.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
string, The signature of the message for the given key.
|
string, The signature of the message for the given key.
|
||||||
"""
|
"""
|
||||||
message = _to_bytes(message, encoding='utf-8')
|
message = _to_bytes(message, encoding='utf-8')
|
||||||
return crypto.sign(self._key, message, 'sha256')
|
return crypto.sign(self._key, message, 'sha256')
|
||||||
|
|
||||||
@@ -102,16 +102,16 @@ class OpenSSLSigner(object):
|
|||||||
def from_string(key, password=b'notasecret'):
|
def from_string(key, password=b'notasecret'):
|
||||||
"""Construct a Signer instance from a string.
|
"""Construct a Signer instance from a string.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key: string, private key in PKCS12 or PEM format.
|
key: string, private key in PKCS12 or PEM format.
|
||||||
password: string, password for the private key file.
|
password: string, password for the private key file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Signer instance.
|
Signer instance.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
OpenSSL.crypto.Error if the key can't be parsed.
|
OpenSSL.crypto.Error if the key can't be parsed.
|
||||||
"""
|
"""
|
||||||
parsed_pem_key = _parse_pem_key(key)
|
parsed_pem_key = _parse_pem_key(key)
|
||||||
if parsed_pem_key:
|
if parsed_pem_key:
|
||||||
pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, 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):
|
def pkcs12_key_as_pem(private_key_text, private_key_password):
|
||||||
"""Convert the contents of a PKCS12 key to PEM using OpenSSL.
|
"""Convert the contents of a PKCS12 key to PEM using OpenSSL.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
private_key_text: String. Private key.
|
private_key_text: String. Private key.
|
||||||
private_key_password: String. Password for PKCS12.
|
private_key_password: String. Password for PKCS12.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
String. PEM contents of ``private_key_text``.
|
String. PEM contents of ``private_key_text``.
|
||||||
"""
|
"""
|
||||||
decoded_body = base64.b64decode(private_key_text)
|
decoded_body = base64.b64decode(private_key_text)
|
||||||
private_key_password = _to_bytes(private_key_password)
|
private_key_password = _to_bytes(private_key_password)
|
||||||
|
|
||||||
|
|||||||
@@ -30,23 +30,24 @@ class PyCryptoVerifier(object):
|
|||||||
def __init__(self, pubkey):
|
def __init__(self, pubkey):
|
||||||
"""Constructor.
|
"""Constructor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pubkey, OpenSSL.crypto.PKey (or equiv), The public key to verify with.
|
pubkey: OpenSSL.crypto.PKey (or equiv), The public key to verify
|
||||||
"""
|
with.
|
||||||
|
"""
|
||||||
self._pubkey = pubkey
|
self._pubkey = pubkey
|
||||||
|
|
||||||
def verify(self, message, signature):
|
def verify(self, message, signature):
|
||||||
"""Verifies a message against a signature.
|
"""Verifies a message against a signature.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: string or bytes, The message to verify. If string, will be
|
message: string or bytes, The message to verify. If string, will be
|
||||||
encoded to bytes as utf-8.
|
encoded to bytes as utf-8.
|
||||||
signature: string or bytes, The signature on the message.
|
signature: string or bytes, The signature on the message.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if message was signed by the private key associated with the public
|
True if message was signed by the private key associated with the
|
||||||
key that this object was constructed with.
|
public key that this object was constructed with.
|
||||||
"""
|
"""
|
||||||
message = _to_bytes(message, encoding='utf-8')
|
message = _to_bytes(message, encoding='utf-8')
|
||||||
return PKCS1_v1_5.new(self._pubkey).verify(
|
return PKCS1_v1_5.new(self._pubkey).verify(
|
||||||
SHA256.new(message), signature)
|
SHA256.new(message), signature)
|
||||||
@@ -55,14 +56,14 @@ class PyCryptoVerifier(object):
|
|||||||
def from_string(key_pem, is_x509_cert):
|
def from_string(key_pem, is_x509_cert):
|
||||||
"""Construct a Verified instance from a string.
|
"""Construct a Verified instance from a string.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key_pem: string, public key in PEM format.
|
key_pem: string, public key in PEM format.
|
||||||
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it is
|
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it is
|
||||||
expected to be an RSA key in PEM format.
|
expected to be an RSA key in PEM format.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Verifier instance.
|
Verifier instance.
|
||||||
"""
|
"""
|
||||||
if is_x509_cert:
|
if is_x509_cert:
|
||||||
key_pem = _to_bytes(key_pem)
|
key_pem = _to_bytes(key_pem)
|
||||||
pemLines = key_pem.replace(b' ', b'').split()
|
pemLines = key_pem.replace(b' ', b'').split()
|
||||||
@@ -83,20 +84,20 @@ class PyCryptoSigner(object):
|
|||||||
def __init__(self, pkey):
|
def __init__(self, pkey):
|
||||||
"""Constructor.
|
"""Constructor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with.
|
pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with.
|
||||||
"""
|
"""
|
||||||
self._key = pkey
|
self._key = pkey
|
||||||
|
|
||||||
def sign(self, message):
|
def sign(self, message):
|
||||||
"""Signs a message.
|
"""Signs a message.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: string, Message to be signed.
|
message: string, Message to be signed.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
string, The signature of the message for the given key.
|
string, The signature of the message for the given key.
|
||||||
"""
|
"""
|
||||||
message = _to_bytes(message, encoding='utf-8')
|
message = _to_bytes(message, encoding='utf-8')
|
||||||
return PKCS1_v1_5.new(self._key).sign(SHA256.new(message))
|
return PKCS1_v1_5.new(self._key).sign(SHA256.new(message))
|
||||||
|
|
||||||
@@ -104,16 +105,17 @@ class PyCryptoSigner(object):
|
|||||||
def from_string(key, password='notasecret'):
|
def from_string(key, password='notasecret'):
|
||||||
"""Construct a Signer instance from a string.
|
"""Construct a Signer instance from a string.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key: string, private key in PEM format.
|
key: string, private key in PEM format.
|
||||||
password: string, password for private key file. Unused for PEM files.
|
password: string, password for private key file. Unused for PEM
|
||||||
|
files.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Signer instance.
|
Signer instance.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
NotImplementedError if the key isn't in PEM format.
|
NotImplementedError if the key isn't in PEM format.
|
||||||
"""
|
"""
|
||||||
parsed_pem_key = _parse_pem_key(key)
|
parsed_pem_key = _parse_pem_key(key)
|
||||||
if parsed_pem_key:
|
if parsed_pem_key:
|
||||||
pkey = RSA.importKey(parsed_pem_key)
|
pkey = RSA.importKey(parsed_pem_key)
|
||||||
|
|||||||
@@ -65,12 +65,12 @@ XSRF_MEMCACHE_ID = 'xsrf_secret_key'
|
|||||||
def _safe_html(s):
|
def _safe_html(s):
|
||||||
"""Escape text to make it safe to display.
|
"""Escape text to make it safe to display.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
s: string, The text to escape.
|
s: string, The text to escape.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The escaped text as a string.
|
The escaped text as a string.
|
||||||
"""
|
"""
|
||||||
return cgi.escape(s, quote=1).replace("'", ''')
|
return cgi.escape(s, quote=1).replace("'", ''')
|
||||||
|
|
||||||
|
|
||||||
@@ -85,22 +85,22 @@ class InvalidXsrfTokenError(Exception):
|
|||||||
class SiteXsrfSecretKey(db.Model):
|
class SiteXsrfSecretKey(db.Model):
|
||||||
"""Storage for the sites XSRF secret key.
|
"""Storage for the sites XSRF secret key.
|
||||||
|
|
||||||
There will only be one instance stored of this model, the one used for the
|
There will only be one instance stored of this model, the one used for the
|
||||||
site.
|
site.
|
||||||
"""
|
"""
|
||||||
secret = db.StringProperty()
|
secret = db.StringProperty()
|
||||||
|
|
||||||
if ndb is not None:
|
if ndb is not None:
|
||||||
class SiteXsrfSecretKeyNDB(ndb.Model):
|
class SiteXsrfSecretKeyNDB(ndb.Model):
|
||||||
"""NDB Model for storage for the sites XSRF secret key.
|
"""NDB Model for storage for the sites XSRF secret key.
|
||||||
|
|
||||||
Since this model uses the same kind as SiteXsrfSecretKey, it can be used
|
Since this model uses the same kind as SiteXsrfSecretKey, it can be
|
||||||
interchangeably. This simply provides an NDB model for interacting with the
|
used interchangeably. This simply provides an NDB model for interacting
|
||||||
same data the DB model interacts with.
|
with the same data the DB model interacts with.
|
||||||
|
|
||||||
There should only be one instance stored of this model, the one used for the
|
There should only be one instance stored of this model, the one used
|
||||||
site.
|
for the site.
|
||||||
"""
|
"""
|
||||||
secret = ndb.StringProperty()
|
secret = ndb.StringProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -110,20 +110,19 @@ if ndb is not None:
|
|||||||
|
|
||||||
|
|
||||||
def _generate_new_xsrf_secret_key():
|
def _generate_new_xsrf_secret_key():
|
||||||
"""Returns a random XSRF secret key.
|
"""Returns a random XSRF secret key."""
|
||||||
"""
|
|
||||||
return os.urandom(16).encode("hex")
|
return os.urandom(16).encode("hex")
|
||||||
|
|
||||||
|
|
||||||
def xsrf_secret_key():
|
def xsrf_secret_key():
|
||||||
"""Return the secret key for use for XSRF protection.
|
"""Return the secret key for use for XSRF protection.
|
||||||
|
|
||||||
If the Site entity does not have a secret key, this method will also create
|
If the Site entity does not have a secret key, this method will also create
|
||||||
one and persist it.
|
one and persist it.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The secret key.
|
The secret key.
|
||||||
"""
|
"""
|
||||||
secret = memcache.get(XSRF_MEMCACHE_ID, namespace=OAUTH2CLIENT_NAMESPACE)
|
secret = memcache.get(XSRF_MEMCACHE_ID, namespace=OAUTH2CLIENT_NAMESPACE)
|
||||||
if not secret:
|
if not secret:
|
||||||
# Load the one and only instance of SiteXsrfSecretKey.
|
# Load the one and only instance of SiteXsrfSecretKey.
|
||||||
@@ -140,27 +139,28 @@ def xsrf_secret_key():
|
|||||||
class AppAssertionCredentials(AssertionCredentials):
|
class AppAssertionCredentials(AssertionCredentials):
|
||||||
"""Credentials object for App Engine Assertion Grants
|
"""Credentials object for App Engine Assertion Grants
|
||||||
|
|
||||||
This object will allow an App Engine application to identify itself to Google
|
This object will allow an App Engine application to identify itself to
|
||||||
and other OAuth 2.0 servers that can verify assertions. It can be used for the
|
Google and other OAuth 2.0 servers that can verify assertions. It can be
|
||||||
purpose of accessing data stored under an account assigned to the App Engine
|
used for the purpose of accessing data stored under an account assigned to
|
||||||
application itself.
|
the App Engine application itself.
|
||||||
|
|
||||||
This credential does not require a flow to instantiate because it represents
|
This credential does not require a flow to instantiate because it
|
||||||
a two legged flow, and therefore has all of the required information to
|
represents a two legged flow, and therefore has all of the required
|
||||||
generate and refresh its own access tokens.
|
information to generate and refresh its own access tokens.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@util.positional(2)
|
@util.positional(2)
|
||||||
def __init__(self, scope, **kwargs):
|
def __init__(self, scope, **kwargs):
|
||||||
"""Constructor for AppAssertionCredentials
|
"""Constructor for AppAssertionCredentials
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
scope: string or iterable of strings, scope(s) of the credentials being
|
scope: string or iterable of strings, scope(s) of the credentials
|
||||||
requested.
|
being requested.
|
||||||
**kwargs: optional keyword args, including:
|
**kwargs: optional keyword args, including:
|
||||||
service_account_id: service account id of the application. If None or
|
service_account_id: service account id of the application. If None
|
||||||
unspecified, the default service account for the app is used.
|
or unspecified, the default service account for
|
||||||
"""
|
the app is used.
|
||||||
|
"""
|
||||||
self.scope = util.scopes_to_string(scope)
|
self.scope = util.scopes_to_string(scope)
|
||||||
self._kwargs = kwargs
|
self._kwargs = kwargs
|
||||||
self.service_account_id = kwargs.get('service_account_id', None)
|
self.service_account_id = kwargs.get('service_account_id', None)
|
||||||
@@ -176,17 +176,18 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
def _refresh(self, http_request):
|
def _refresh(self, http_request):
|
||||||
"""Refreshes the access_token.
|
"""Refreshes the access_token.
|
||||||
|
|
||||||
Since the underlying App Engine app_identity implementation does its own
|
Since the underlying App Engine app_identity implementation does its
|
||||||
caching we can skip all the storage hoops and just to a refresh using the
|
own caching we can skip all the storage hoops and just to a refresh
|
||||||
API.
|
using the API.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
http_request: callable, a callable that matches the method signature of
|
http_request: callable, a callable that matches the method
|
||||||
httplib2.Http.request, used to make the refresh request.
|
signature of httplib2.Http.request, used to make the
|
||||||
|
refresh request.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AccessTokenRefreshError: When the refresh fails.
|
AccessTokenRefreshError: When the refresh fails.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
scopes = self.scope.split()
|
scopes = self.scope.split()
|
||||||
(token, _) = app_identity.get_access_token(
|
(token, _) = app_identity.get_access_token(
|
||||||
@@ -209,8 +210,9 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
class FlowProperty(db.Property):
|
class FlowProperty(db.Property):
|
||||||
"""App Engine datastore Property for Flow.
|
"""App Engine datastore Property for Flow.
|
||||||
|
|
||||||
Utility property that allows easy storage and retrieval of an
|
Utility property that allows easy storage and retrieval of an
|
||||||
oauth2client.Flow"""
|
oauth2client.Flow
|
||||||
|
"""
|
||||||
|
|
||||||
# Tell what the user type is.
|
# Tell what the user type is.
|
||||||
data_type = Flow
|
data_type = Flow
|
||||||
@@ -242,23 +244,24 @@ if ndb is not None:
|
|||||||
class FlowNDBProperty(ndb.PickleProperty):
|
class FlowNDBProperty(ndb.PickleProperty):
|
||||||
"""App Engine NDB datastore Property for Flow.
|
"""App Engine NDB datastore Property for Flow.
|
||||||
|
|
||||||
Serves the same purpose as the DB FlowProperty, but for NDB models. Since
|
Serves the same purpose as the DB FlowProperty, but for NDB models.
|
||||||
PickleProperty inherits from BlobProperty, the underlying representation of
|
Since PickleProperty inherits from BlobProperty, the underlying
|
||||||
the data in the datastore will be the same as in the DB case.
|
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
|
Utility property that allows easy storage and retrieval of an
|
||||||
oauth2client.Flow
|
oauth2client.Flow
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _validate(self, value):
|
def _validate(self, value):
|
||||||
"""Validates a value as a proper Flow object.
|
"""Validates a value as a proper Flow object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: A value to be set on the property.
|
value: A value to be set on the property.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError if the value is not an instance of Flow.
|
TypeError if the value is not an instance of Flow.
|
||||||
"""
|
"""
|
||||||
logger.info('validate: Got type %s', type(value))
|
logger.info('validate: Got type %s', type(value))
|
||||||
if value is not None and not isinstance(value, Flow):
|
if value is not None and not isinstance(value, Flow):
|
||||||
raise TypeError('Property %s must be convertible to a flow '
|
raise TypeError('Property %s must be convertible to a flow '
|
||||||
@@ -268,9 +271,9 @@ if ndb is not None:
|
|||||||
class CredentialsProperty(db.Property):
|
class CredentialsProperty(db.Property):
|
||||||
"""App Engine datastore Property for Credentials.
|
"""App Engine datastore Property for Credentials.
|
||||||
|
|
||||||
Utility property that allows easy storage and retrieval of
|
Utility property that allows easy storage and retrieval of
|
||||||
oath2client.Credentials
|
oath2client.Credentials
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Tell what the user type is.
|
# Tell what the user type is.
|
||||||
data_type = Credentials
|
data_type = Credentials
|
||||||
@@ -320,23 +323,24 @@ if ndb is not None:
|
|||||||
class CredentialsNDBProperty(ndb.BlobProperty):
|
class CredentialsNDBProperty(ndb.BlobProperty):
|
||||||
"""App Engine NDB datastore Property for Credentials.
|
"""App Engine NDB datastore Property for Credentials.
|
||||||
|
|
||||||
Serves the same purpose as the DB CredentialsProperty, but for NDB models.
|
Serves the same purpose as the DB CredentialsProperty, but for NDB
|
||||||
Since CredentialsProperty stores data as a blob and this inherits from
|
models. Since CredentialsProperty stores data as a blob and this
|
||||||
BlobProperty, the data in the datastore will be the same as in the DB case.
|
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
|
Utility property that allows easy storage and retrieval of Credentials
|
||||||
subclasses.
|
and subclasses.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _validate(self, value):
|
def _validate(self, value):
|
||||||
"""Validates a value as a proper credentials object.
|
"""Validates a value as a proper credentials object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: A value to be set on the property.
|
value: A value to be set on the property.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError if the value is not an instance of Credentials.
|
TypeError if the value is not an instance of Credentials.
|
||||||
"""
|
"""
|
||||||
logger.info('validate: Got type %s', type(value))
|
logger.info('validate: Got type %s', type(value))
|
||||||
if value is not None and not isinstance(value, Credentials):
|
if value is not None and not isinstance(value, Credentials):
|
||||||
raise TypeError('Property %s must be convertible to a 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):
|
def _to_base_type(self, value):
|
||||||
"""Converts our validated value to a JSON serialized string.
|
"""Converts our validated value to a JSON serialized string.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: A value to be set in the datastore.
|
value: A value to be set in the datastore.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A JSON serialized version of the credential, else '' if value is None.
|
A JSON serialized version of the credential, else '' if value
|
||||||
"""
|
is None.
|
||||||
|
"""
|
||||||
if value is None:
|
if value is None:
|
||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
@@ -359,13 +364,14 @@ if ndb is not None:
|
|||||||
def _from_base_type(self, value):
|
def _from_base_type(self, value):
|
||||||
"""Converts our stored JSON string back to the desired type.
|
"""Converts our stored JSON string back to the desired type.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: A value from the datastore to be converted to the desired type.
|
value: A value from the datastore to be converted to the
|
||||||
|
desired type.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A deserialized Credentials (or subclass) object, else None if the
|
A deserialized Credentials (or subclass) object, else None if
|
||||||
value can't be parsed.
|
the value can't be parsed.
|
||||||
"""
|
"""
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
@@ -379,26 +385,27 @@ if ndb is not None:
|
|||||||
class StorageByKeyName(Storage):
|
class StorageByKeyName(Storage):
|
||||||
"""Store and retrieve a credential to and from the App Engine datastore.
|
"""Store and retrieve a credential to and from the App Engine datastore.
|
||||||
|
|
||||||
This Storage helper presumes the Credentials have been stored as a
|
This Storage helper presumes the Credentials have been stored as a
|
||||||
CredentialsProperty or CredentialsNDBProperty on a datastore model class, and
|
CredentialsProperty or CredentialsNDBProperty on a datastore model class,
|
||||||
that entities are stored by key_name.
|
and that entities are stored by key_name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@util.positional(4)
|
@util.positional(4)
|
||||||
def __init__(self, model, key_name, property_name, cache=None, user=None):
|
def __init__(self, model, key_name, property_name, cache=None, user=None):
|
||||||
"""Constructor for Storage.
|
"""Constructor for Storage.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
model: db.Model or ndb.Model, model class
|
model: db.Model or ndb.Model, model class
|
||||||
key_name: string, key name for the entity that has the credentials
|
key_name: string, key name for the entity that has the credentials
|
||||||
property_name: string, name of the property that is a CredentialsProperty
|
property_name: string, name of the property that is a
|
||||||
or CredentialsNDBProperty.
|
CredentialsProperty or CredentialsNDBProperty.
|
||||||
cache: memcache, a write-through cache to put in front of the datastore.
|
cache: memcache, a write-through cache to put in front of the
|
||||||
If the model you are using is an NDB model, using a cache will be
|
datastore. If the model you are using is an NDB model, using
|
||||||
redundant since the model uses an instance cache and memcache for you.
|
a cache will be redundant since the model uses an instance
|
||||||
user: users.User object, optional. Can be used to grab user ID as a
|
cache and memcache for you.
|
||||||
key_name if no key name is specified.
|
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 key_name is None:
|
||||||
if user is None:
|
if user is None:
|
||||||
raise ValueError('StorageByKeyName called with no key name or user.')
|
raise ValueError('StorageByKeyName called with no key name or user.')
|
||||||
@@ -412,9 +419,9 @@ class StorageByKeyName(Storage):
|
|||||||
def _is_ndb(self):
|
def _is_ndb(self):
|
||||||
"""Determine whether the model of the instance is an NDB model.
|
"""Determine whether the model of the instance is an NDB model.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Boolean indicating whether or not the model is an NDB or DB model.
|
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
|
# 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
|
# worry about new-style classes since ndb and db models are new-style
|
||||||
if isinstance(self._model, type):
|
if isinstance(self._model, type):
|
||||||
@@ -428,12 +435,12 @@ class StorageByKeyName(Storage):
|
|||||||
def _get_entity(self):
|
def _get_entity(self):
|
||||||
"""Retrieve entity from datastore.
|
"""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:
|
Returns:
|
||||||
Instance of the model corresponding to the current storage object
|
Instance of the model corresponding to the current storage object
|
||||||
and stored using the key name of the storage object.
|
and stored using the key name of the storage object.
|
||||||
"""
|
"""
|
||||||
if self._is_ndb():
|
if self._is_ndb():
|
||||||
return self._model.get_by_id(self._key_name)
|
return self._model.get_by_id(self._key_name)
|
||||||
else:
|
else:
|
||||||
@@ -442,9 +449,9 @@ class StorageByKeyName(Storage):
|
|||||||
def _delete_entity(self):
|
def _delete_entity(self):
|
||||||
"""Delete entity from datastore.
|
"""Delete entity from datastore.
|
||||||
|
|
||||||
Attempts to delete using the key_name stored on the object, whether or not
|
Attempts to delete using the key_name stored on the object, whether or
|
||||||
the given key is in the datastore.
|
not the given key is in the datastore.
|
||||||
"""
|
"""
|
||||||
if self._is_ndb():
|
if self._is_ndb():
|
||||||
ndb.Key(self._model, self._key_name).delete()
|
ndb.Key(self._model, self._key_name).delete()
|
||||||
else:
|
else:
|
||||||
@@ -455,9 +462,9 @@ class StorageByKeyName(Storage):
|
|||||||
def locked_get(self):
|
def locked_get(self):
|
||||||
"""Retrieve Credential from datastore.
|
"""Retrieve Credential from datastore.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
oauth2client.Credentials
|
oauth2client.Credentials
|
||||||
"""
|
"""
|
||||||
credentials = None
|
credentials = None
|
||||||
if self._cache:
|
if self._cache:
|
||||||
json = self._cache.get(self._key_name)
|
json = self._cache.get(self._key_name)
|
||||||
@@ -478,9 +485,9 @@ class StorageByKeyName(Storage):
|
|||||||
def locked_put(self, credentials):
|
def locked_put(self, credentials):
|
||||||
"""Write a Credentials to the datastore.
|
"""Write a Credentials to the datastore.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
"""
|
"""
|
||||||
entity = self._model.get_or_insert(self._key_name)
|
entity = self._model.get_or_insert(self._key_name)
|
||||||
setattr(entity, self._property_name, credentials)
|
setattr(entity, self._property_name, credentials)
|
||||||
entity.put()
|
entity.put()
|
||||||
@@ -500,8 +507,8 @@ class StorageByKeyName(Storage):
|
|||||||
class CredentialsModel(db.Model):
|
class CredentialsModel(db.Model):
|
||||||
"""Storage for OAuth 2.0 Credentials
|
"""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()
|
credentials = CredentialsProperty()
|
||||||
|
|
||||||
|
|
||||||
@@ -509,14 +516,14 @@ if ndb is not None:
|
|||||||
class CredentialsNDBModel(ndb.Model):
|
class CredentialsNDBModel(ndb.Model):
|
||||||
"""NDB Model for storage of OAuth 2.0 Credentials
|
"""NDB Model for storage of OAuth 2.0 Credentials
|
||||||
|
|
||||||
Since this model uses the same kind as CredentialsModel and has a property
|
Since this model uses the same kind as CredentialsModel and has a
|
||||||
which can serialize and deserialize Credentials correctly, it can be used
|
property which can serialize and deserialize Credentials correctly, it
|
||||||
interchangeably with a CredentialsModel to access, insert and delete the
|
can be used interchangeably with a CredentialsModel to access, insert
|
||||||
same entities. This simply provides an NDB model for interacting with the
|
and delete the same entities. This simply provides an NDB model for
|
||||||
same data the DB model interacts with.
|
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()
|
credentials = CredentialsNDBProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -528,16 +535,16 @@ if ndb is not None:
|
|||||||
def _build_state_value(request_handler, user):
|
def _build_state_value(request_handler, user):
|
||||||
"""Composes the value for the 'state' parameter.
|
"""Composes the value for the 'state' parameter.
|
||||||
|
|
||||||
Packs the current request URI and an XSRF token into an opaque string that
|
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.
|
can be passed to the authentication server via the 'state' parameter.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request_handler: webapp.RequestHandler, The request.
|
request_handler: webapp.RequestHandler, The request.
|
||||||
user: google.appengine.api.users.User, The current user.
|
user: google.appengine.api.users.User, The current user.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The state value as a string.
|
The state value as a string.
|
||||||
"""
|
"""
|
||||||
uri = request_handler.request.url
|
uri = request_handler.request.url
|
||||||
token = xsrfutil.generate_token(xsrf_secret_key(), user.user_id(),
|
token = xsrfutil.generate_token(xsrf_secret_key(), user.user_id(),
|
||||||
action_id=str(uri))
|
action_id=str(uri))
|
||||||
@@ -547,18 +554,18 @@ def _build_state_value(request_handler, user):
|
|||||||
def _parse_state_value(state, user):
|
def _parse_state_value(state, user):
|
||||||
"""Parse the value of the 'state' parameter.
|
"""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:
|
Args:
|
||||||
state: string, The value of the state parameter.
|
state: string, The value of the state parameter.
|
||||||
user: google.appengine.api.users.User, The current user.
|
user: google.appengine.api.users.User, The current user.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
InvalidXsrfTokenError: if the XSRF token is invalid.
|
InvalidXsrfTokenError: if the XSRF token is invalid.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The redirect URI.
|
The redirect URI.
|
||||||
"""
|
"""
|
||||||
uri, token = state.rsplit(':', 1)
|
uri, token = state.rsplit(':', 1)
|
||||||
if not xsrfutil.validate_token(xsrf_secret_key(), token, user.user_id(),
|
if not xsrfutil.validate_token(xsrf_secret_key(), token, user.user_id(),
|
||||||
action_id=uri):
|
action_id=uri):
|
||||||
@@ -570,24 +577,24 @@ def _parse_state_value(state, user):
|
|||||||
class OAuth2Decorator(object):
|
class OAuth2Decorator(object):
|
||||||
"""Utility for making OAuth 2.0 easier.
|
"""Utility for making OAuth 2.0 easier.
|
||||||
|
|
||||||
Instantiate and then use with oauth_required or oauth_aware
|
Instantiate and then use with oauth_required or oauth_aware
|
||||||
as decorators on webapp.RequestHandler methods.
|
as decorators on webapp.RequestHandler methods.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
decorator = OAuth2Decorator(
|
decorator = OAuth2Decorator(
|
||||||
client_id='837...ent.com',
|
client_id='837...ent.com',
|
||||||
client_secret='Qh...wwI',
|
client_secret='Qh...wwI',
|
||||||
scope='https://www.googleapis.com/auth/plus')
|
scope='https://www.googleapis.com/auth/plus')
|
||||||
|
|
||||||
class MainHandler(webapp.RequestHandler):
|
class MainHandler(webapp.RequestHandler):
|
||||||
@decorator.oauth_required
|
@decorator.oauth_required
|
||||||
def get(self):
|
def get(self):
|
||||||
http = decorator.http()
|
http = decorator.http()
|
||||||
# http is authorized with the user's Credentials and can be used
|
# http is authorized with the user's Credentials and can be
|
||||||
# in API calls
|
# used in API calls
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def set_credentials(self, credentials):
|
def set_credentials(self, credentials):
|
||||||
self._tls.credentials = credentials
|
self._tls.credentials = credentials
|
||||||
@@ -595,11 +602,11 @@ class OAuth2Decorator(object):
|
|||||||
def get_credentials(self):
|
def get_credentials(self):
|
||||||
"""A thread local Credentials object.
|
"""A thread local Credentials object.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A client.Credentials object, or None if credentials hasn't been set in
|
A client.Credentials object, or None if credentials hasn't been set
|
||||||
this thread yet, which may happen when calling has_credentials inside
|
in this thread yet, which may happen when calling has_credentials
|
||||||
oauth_aware.
|
inside oauth_aware.
|
||||||
"""
|
"""
|
||||||
return getattr(self._tls, 'credentials', None)
|
return getattr(self._tls, 'credentials', None)
|
||||||
|
|
||||||
credentials = property(get_credentials, set_credentials)
|
credentials = property(get_credentials, set_credentials)
|
||||||
@@ -610,11 +617,11 @@ class OAuth2Decorator(object):
|
|||||||
def get_flow(self):
|
def get_flow(self):
|
||||||
"""A thread local Flow object.
|
"""A thread local Flow object.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A credentials.Flow object, or None if the flow hasn't been set in this
|
A credentials.Flow object, or None if the flow hasn't been set in
|
||||||
thread yet, which happens in _create_flow() since Flows are created
|
this thread yet, which happens in _create_flow() since Flows are
|
||||||
lazily.
|
created lazily.
|
||||||
"""
|
"""
|
||||||
return getattr(self._tls, 'flow', None)
|
return getattr(self._tls, 'flow', None)
|
||||||
|
|
||||||
flow = property(get_flow, set_flow)
|
flow = property(get_flow, set_flow)
|
||||||
@@ -635,42 +642,52 @@ class OAuth2Decorator(object):
|
|||||||
|
|
||||||
"""Constructor for OAuth2Decorator
|
"""Constructor for OAuth2Decorator
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
client_id: string, client identifier.
|
client_id: string, client identifier.
|
||||||
client_secret: string client secret.
|
client_secret: string client secret.
|
||||||
scope: string or iterable of strings, scope(s) of the credentials being
|
scope: string or iterable of strings, scope(s) of the credentials
|
||||||
requested.
|
being requested.
|
||||||
auth_uri: string, URI for authorization endpoint. For convenience
|
auth_uri: string, URI for authorization endpoint. For convenience
|
||||||
defaults to Google's endpoints but any OAuth 2.0 provider can be used.
|
defaults to Google's endpoints but any OAuth 2.0 provider
|
||||||
token_uri: string, URI for token endpoint. For convenience
|
can be used.
|
||||||
defaults to Google's endpoints but any OAuth 2.0 provider can be used.
|
token_uri: string, URI for token endpoint. For convenience defaults
|
||||||
revoke_uri: string, URI for revoke endpoint. For convenience
|
to Google's endpoints but any OAuth 2.0 provider can be
|
||||||
defaults to Google's endpoints but any OAuth 2.0 provider can be used.
|
used.
|
||||||
user_agent: string, User agent of your application, default to None.
|
revoke_uri: string, URI for revoke endpoint. For convenience
|
||||||
message: Message to display if there are problems with the OAuth 2.0
|
defaults to Google's endpoints but any OAuth 2.0
|
||||||
configuration. The message may contain HTML and will be presented on the
|
provider can be used.
|
||||||
web interface for any method that uses the decorator.
|
user_agent: string, User agent of your application, default to
|
||||||
callback_path: string, The absolute path to use as the callback URI. Note
|
None.
|
||||||
that this must match up with the URI given when registering the
|
message: Message to display if there are problems with the OAuth 2.0
|
||||||
application in the APIs Console.
|
configuration. The message may contain HTML and will be
|
||||||
token_response_param: string. If provided, the full JSON response
|
presented on the web interface for any method that uses
|
||||||
to the access token request will be encoded and included in this query
|
the decorator.
|
||||||
parameter in the callback URI. This is useful with providers (e.g.
|
callback_path: string, The absolute path to use as the callback
|
||||||
wordpress.com) that include extra fields that the client may want.
|
URI. Note that this must match up with the URI given
|
||||||
_storage_class: "Protected" keyword argument not typically provided to
|
when registering the application in the APIs Console.
|
||||||
this constructor. A storage class to aid in storing a Credentials object
|
token_response_param: string. If provided, the full JSON response
|
||||||
for a user in the datastore. Defaults to StorageByKeyName.
|
to the access token request will be encoded
|
||||||
_credentials_class: "Protected" keyword argument not typically provided to
|
and included in this query parameter in the
|
||||||
this constructor. A db or ndb Model class to hold credentials. Defaults
|
callback URI. This is useful with providers
|
||||||
to CredentialsModel.
|
(e.g. wordpress.com) that include extra
|
||||||
_credentials_property_name: "Protected" keyword argument not typically
|
fields that the client may want.
|
||||||
provided to this constructor. A string indicating the name of the field
|
_storage_class: "Protected" keyword argument not typically provided
|
||||||
on the _credentials_class where a Credentials object will be stored.
|
to this constructor. A storage class to aid in
|
||||||
Defaults to 'credentials'.
|
storing a Credentials object for a user in the
|
||||||
**kwargs: dict, Keyword arguments are passed along as kwargs to
|
datastore. Defaults to StorageByKeyName.
|
||||||
the OAuth2WebServerFlow constructor.
|
_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._tls = threading.local()
|
||||||
self.flow = None
|
self.flow = None
|
||||||
self.credentials = None
|
self.credentials = None
|
||||||
@@ -698,13 +715,13 @@ class OAuth2Decorator(object):
|
|||||||
def oauth_required(self, method):
|
def oauth_required(self, method):
|
||||||
"""Decorator that starts the OAuth 2.0 dance.
|
"""Decorator that starts the OAuth 2.0 dance.
|
||||||
|
|
||||||
Starts the OAuth dance for the logged in user if they haven't already
|
Starts the OAuth dance for the logged in user if they haven't already
|
||||||
granted access for this application.
|
granted access for this application.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
method: callable, to be decorated method of a webapp.RequestHandler
|
method: callable, to be decorated method of a webapp.RequestHandler
|
||||||
instance.
|
instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def check_oauth(request_handler, *args, **kwargs):
|
def check_oauth(request_handler, *args, **kwargs):
|
||||||
if self._in_error:
|
if self._in_error:
|
||||||
@@ -741,13 +758,13 @@ class OAuth2Decorator(object):
|
|||||||
def _create_flow(self, request_handler):
|
def _create_flow(self, request_handler):
|
||||||
"""Create the Flow object.
|
"""Create the Flow object.
|
||||||
|
|
||||||
The Flow is calculated lazily since we don't know where this app is
|
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
|
running until it receives a request, at which point redirect_uri can be
|
||||||
calculated and then the Flow object can be constructed.
|
calculated and then the Flow object can be constructed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request_handler: webapp.RequestHandler, the request handler.
|
request_handler: webapp.RequestHandler, the request handler.
|
||||||
"""
|
"""
|
||||||
if self.flow is None:
|
if self.flow is None:
|
||||||
redirect_uri = request_handler.request.relative_url(
|
redirect_uri = request_handler.request.relative_url(
|
||||||
self._callback_path) # Usually /oauth2callback
|
self._callback_path) # Usually /oauth2callback
|
||||||
@@ -762,16 +779,16 @@ class OAuth2Decorator(object):
|
|||||||
def oauth_aware(self, method):
|
def oauth_aware(self, method):
|
||||||
"""Decorator that sets up for OAuth 2.0 dance, but doesn't do it.
|
"""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.
|
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
|
This decorator is useful if you want to create a page that knows
|
||||||
whether or not the user has granted access to this application.
|
whether or not the user has granted access to this application.
|
||||||
From within a method decorated with @oauth_aware the has_credentials()
|
From within a method decorated with @oauth_aware the has_credentials()
|
||||||
and authorize_url() methods can be called.
|
and authorize_url() methods can be called.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
method: callable, to be decorated method of a webapp.RequestHandler
|
method: callable, to be decorated method of a webapp.RequestHandler
|
||||||
instance.
|
instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setup_oauth(request_handler, *args, **kwargs):
|
def setup_oauth(request_handler, *args, **kwargs):
|
||||||
if self._in_error:
|
if self._in_error:
|
||||||
@@ -801,61 +818,61 @@ class OAuth2Decorator(object):
|
|||||||
def has_credentials(self):
|
def has_credentials(self):
|
||||||
"""True if for the logged in user there are valid access Credentials.
|
"""True if for the logged in user there are valid access Credentials.
|
||||||
|
|
||||||
Must only be called from with a webapp.RequestHandler subclassed method
|
Must only be called from with a webapp.RequestHandler subclassed method
|
||||||
that had been decorated with either @oauth_required or @oauth_aware.
|
that had been decorated with either @oauth_required or @oauth_aware.
|
||||||
"""
|
"""
|
||||||
return self.credentials is not None and not self.credentials.invalid
|
return self.credentials is not None and not self.credentials.invalid
|
||||||
|
|
||||||
def authorize_url(self):
|
def authorize_url(self):
|
||||||
"""Returns the URL to start the OAuth dance.
|
"""Returns the URL to start the OAuth dance.
|
||||||
|
|
||||||
Must only be called from with a webapp.RequestHandler subclassed method
|
Must only be called from with a webapp.RequestHandler subclassed method
|
||||||
that had been decorated with either @oauth_required or @oauth_aware.
|
that had been decorated with either @oauth_required or @oauth_aware.
|
||||||
"""
|
"""
|
||||||
url = self.flow.step1_get_authorize_url()
|
url = self.flow.step1_get_authorize_url()
|
||||||
return str(url)
|
return str(url)
|
||||||
|
|
||||||
def http(self, *args, **kwargs):
|
def http(self, *args, **kwargs):
|
||||||
"""Returns an authorized http instance.
|
"""Returns an authorized http instance.
|
||||||
|
|
||||||
Must only be called from within an @oauth_required decorated method, or
|
Must only be called from within an @oauth_required decorated method, or
|
||||||
from within an @oauth_aware decorated method where has_credentials()
|
from within an @oauth_aware decorated method where has_credentials()
|
||||||
returns True.
|
returns True.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*args: Positional arguments passed to httplib2.Http constructor.
|
*args: Positional arguments passed to httplib2.Http constructor.
|
||||||
**kwargs: Positional arguments passed to httplib2.Http constructor.
|
**kwargs: Positional arguments passed to httplib2.Http constructor.
|
||||||
"""
|
"""
|
||||||
return self.credentials.authorize(httplib2.Http(*args, **kwargs))
|
return self.credentials.authorize(httplib2.Http(*args, **kwargs))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def callback_path(self):
|
def callback_path(self):
|
||||||
"""The absolute path where the callback will occur.
|
"""The absolute path where the callback will occur.
|
||||||
|
|
||||||
Note this is the absolute path, not the absolute URI, that will be
|
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
|
calculated by the decorator at runtime. See callback_handler() for how
|
||||||
should be used.
|
this should be used.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The callback path as a string.
|
The callback path as a string.
|
||||||
"""
|
"""
|
||||||
return self._callback_path
|
return self._callback_path
|
||||||
|
|
||||||
def callback_handler(self):
|
def callback_handler(self):
|
||||||
"""RequestHandler for the OAuth 2.0 redirect callback.
|
"""RequestHandler for the OAuth 2.0 redirect callback.
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
|
||||||
app = webapp.WSGIApplication([
|
app = webapp.WSGIApplication([
|
||||||
('/index', MyIndexHandler),
|
('/index', MyIndexHandler),
|
||||||
...,
|
...,
|
||||||
(decorator.callback_path, decorator.callback_handler())
|
(decorator.callback_path, decorator.callback_handler())
|
||||||
])
|
])
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A webapp.RequestHandler that handles the redirect back from the
|
A webapp.RequestHandler that handles the redirect back from the
|
||||||
server during the OAuth 2.0 dance.
|
server during the OAuth 2.0 dance.
|
||||||
"""
|
"""
|
||||||
decorator = self
|
decorator = self
|
||||||
|
|
||||||
class OAuth2Handler(webapp.RequestHandler):
|
class OAuth2Handler(webapp.RequestHandler):
|
||||||
@@ -890,13 +907,13 @@ class OAuth2Decorator(object):
|
|||||||
def callback_application(self):
|
def callback_application(self):
|
||||||
"""WSGI application for handling the OAuth 2.0 redirect callback.
|
"""WSGI application for handling the OAuth 2.0 redirect callback.
|
||||||
|
|
||||||
If you need finer grained control use `callback_handler` which returns just
|
If you need finer grained control use `callback_handler` which returns
|
||||||
the webapp.RequestHandler.
|
just the webapp.RequestHandler.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A webapp.WSGIApplication that handles the redirect back from the
|
A webapp.WSGIApplication that handles the redirect back from the
|
||||||
server during the OAuth 2.0 dance.
|
server during the OAuth 2.0 dance.
|
||||||
"""
|
"""
|
||||||
return webapp.WSGIApplication([
|
return webapp.WSGIApplication([
|
||||||
(self.callback_path, self.callback_handler())
|
(self.callback_path, self.callback_handler())
|
||||||
])
|
])
|
||||||
@@ -905,41 +922,42 @@ class OAuth2Decorator(object):
|
|||||||
class OAuth2DecoratorFromClientSecrets(OAuth2Decorator):
|
class OAuth2DecoratorFromClientSecrets(OAuth2Decorator):
|
||||||
"""An OAuth2Decorator that builds from a clientsecrets file.
|
"""An OAuth2Decorator that builds from a clientsecrets file.
|
||||||
|
|
||||||
Uses a clientsecrets file as the source for all the information when
|
Uses a clientsecrets file as the source for all the information when
|
||||||
constructing an OAuth2Decorator.
|
constructing an OAuth2Decorator.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
decorator = OAuth2DecoratorFromClientSecrets(
|
decorator = OAuth2DecoratorFromClientSecrets(
|
||||||
os.path.join(os.path.dirname(__file__), 'client_secrets.json')
|
os.path.join(os.path.dirname(__file__), 'client_secrets.json')
|
||||||
scope='https://www.googleapis.com/auth/plus')
|
scope='https://www.googleapis.com/auth/plus')
|
||||||
|
|
||||||
class MainHandler(webapp.RequestHandler):
|
class MainHandler(webapp.RequestHandler):
|
||||||
@decorator.oauth_required
|
@decorator.oauth_required
|
||||||
def get(self):
|
def get(self):
|
||||||
http = decorator.http()
|
http = decorator.http()
|
||||||
# http is authorized with the user's Credentials and can be used
|
# http is authorized with the user's Credentials and can be
|
||||||
# in API calls
|
# used in API calls
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@util.positional(3)
|
@util.positional(3)
|
||||||
def __init__(self, filename, scope, message=None, cache=None, **kwargs):
|
def __init__(self, filename, scope, message=None, cache=None, **kwargs):
|
||||||
"""Constructor
|
"""Constructor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: string, File name of client secrets.
|
filename: string, File name of client secrets.
|
||||||
scope: string or iterable of strings, scope(s) of the credentials being
|
scope: string or iterable of strings, scope(s) of the credentials
|
||||||
requested.
|
being requested.
|
||||||
message: string, A friendly string to display to the user if the
|
message: string, A friendly string to display to the user if the
|
||||||
clientsecrets file is missing or invalid. The message may contain HTML
|
clientsecrets file is missing or invalid. The message may
|
||||||
and will be presented on the web interface for any method that uses the
|
contain HTML and will be presented on the web interface
|
||||||
decorator.
|
for any method that uses the decorator.
|
||||||
cache: An optional cache service client that implements get() and set()
|
cache: An optional cache service client that implements get() and
|
||||||
methods. See clientsecrets.loadfile() for details.
|
set()
|
||||||
**kwargs: dict, Keyword arguments are passed along as kwargs to
|
methods. See clientsecrets.loadfile() for details.
|
||||||
the OAuth2WebServerFlow constructor.
|
**kwargs: dict, Keyword arguments are passed along as kwargs to
|
||||||
"""
|
the OAuth2WebServerFlow constructor.
|
||||||
|
"""
|
||||||
client_type, client_info = clientsecrets.loadfile(filename, cache=cache)
|
client_type, client_info = clientsecrets.loadfile(filename, cache=cache)
|
||||||
if client_type not in [
|
if client_type not in [
|
||||||
clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED]:
|
clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED]:
|
||||||
@@ -968,19 +986,18 @@ def oauth2decorator_from_clientsecrets(filename, scope,
|
|||||||
message=None, cache=None):
|
message=None, cache=None):
|
||||||
"""Creates an OAuth2Decorator populated from a clientsecrets file.
|
"""Creates an OAuth2Decorator populated from a clientsecrets file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: string, File name of client secrets.
|
filename: string, File name of client secrets.
|
||||||
scope: string or list of strings, scope(s) of the credentials being
|
scope: string or list of strings, scope(s) of the credentials being
|
||||||
requested.
|
requested.
|
||||||
message: string, A friendly string to display to the user if the
|
message: string, A friendly string to display to the user if the
|
||||||
clientsecrets file is missing or invalid. The message may contain HTML and
|
clientsecrets file is missing or invalid. The message may
|
||||||
will be presented on the web interface for any method that uses the
|
contain HTML and will be presented on the web interface for
|
||||||
decorator.
|
any method that uses the decorator.
|
||||||
cache: An optional cache service client that implements get() and set()
|
cache: An optional cache service client that implements get() and set()
|
||||||
methods. See clientsecrets.loadfile() for details.
|
methods. See clientsecrets.loadfile() for details.
|
||||||
|
|
||||||
Returns: An OAuth2Decorator
|
Returns: An OAuth2Decorator
|
||||||
|
"""
|
||||||
"""
|
|
||||||
return OAuth2DecoratorFromClientSecrets(filename, scope,
|
return OAuth2DecoratorFromClientSecrets(filename, scope,
|
||||||
message=message, cache=cache)
|
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):
|
def loadfile(filename, cache=None):
|
||||||
"""Loading of client_secrets JSON file, optionally backed by a cache.
|
"""Loading of client_secrets JSON file, optionally backed by a cache.
|
||||||
|
|
||||||
Typical cache storage would be App Engine memcache service,
|
Typical cache storage would be App Engine memcache service,
|
||||||
but you can pass in any other cache client that implements
|
but you can pass in any other cache client that implements
|
||||||
these methods:
|
these methods:
|
||||||
|
|
||||||
* ``get(key, namespace=ns)``
|
* ``get(key, namespace=ns)``
|
||||||
* ``set(key, value, namespace=ns)``
|
* ``set(key, value, namespace=ns)``
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
|
||||||
# without caching
|
# without caching
|
||||||
client_type, client_info = loadfile('secrets.json')
|
client_type, client_info = loadfile('secrets.json')
|
||||||
# using App Engine memcache service
|
# using App Engine memcache service
|
||||||
from google.appengine.api import memcache
|
from google.appengine.api import memcache
|
||||||
client_type, client_info = loadfile('secrets.json', cache=memcache)
|
client_type, client_info = loadfile('secrets.json', cache=memcache)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: string, Path to a client_secrets.json file on a filesystem.
|
filename: string, Path to a client_secrets.json file on a filesystem.
|
||||||
cache: An optional cache service client that implements get() and set()
|
cache: An optional cache service client that implements get() and set()
|
||||||
methods. If not specified, the file is always being loaded from
|
methods. If not specified, the file is always being loaded from
|
||||||
a filesystem.
|
a filesystem.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
InvalidClientSecretsError: In case of a validation error or some
|
InvalidClientSecretsError: In case of a validation error or some
|
||||||
I/O failure. Can happen only on cache miss.
|
I/O failure. Can happen only on cache miss.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(client_type, client_info) tuple, as _loadfile() normally would.
|
(client_type, client_info) tuple, as _loadfile() normally would.
|
||||||
JSON contents is validated only during first load. Cache hits are not
|
JSON contents is validated only during first load. Cache hits are not
|
||||||
validated.
|
validated.
|
||||||
"""
|
"""
|
||||||
_SECRET_NAMESPACE = 'oauth2client:secrets#ns'
|
_SECRET_NAMESPACE = 'oauth2client:secrets#ns'
|
||||||
|
|
||||||
if not cache:
|
if not cache:
|
||||||
|
|||||||
@@ -71,15 +71,15 @@ else:
|
|||||||
def make_signed_jwt(signer, payload):
|
def make_signed_jwt(signer, payload):
|
||||||
"""Make a signed JWT.
|
"""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:
|
Args:
|
||||||
signer: crypt.Signer, Cryptographic signer.
|
signer: crypt.Signer, Cryptographic signer.
|
||||||
payload: dict, Dictionary of data to convert to JSON and then sign.
|
payload: dict, Dictionary of data to convert to JSON and then sign.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
string, The JWT for the payload.
|
string, The JWT for the payload.
|
||||||
"""
|
"""
|
||||||
header = {'typ': 'JWT', 'alg': 'RS256'}
|
header = {'typ': 'JWT', 'alg': 'RS256'}
|
||||||
|
|
||||||
segments = [
|
segments = [
|
||||||
@@ -99,20 +99,20 @@ def make_signed_jwt(signer, payload):
|
|||||||
def verify_signed_jwt_with_certs(jwt, certs, audience):
|
def verify_signed_jwt_with_certs(jwt, certs, audience):
|
||||||
"""Verify a JWT against public certs.
|
"""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:
|
Args:
|
||||||
jwt: string, A JWT.
|
jwt: string, A JWT.
|
||||||
certs: dict, Dictionary where values of public keys in PEM format.
|
certs: dict, Dictionary where values of public keys in PEM format.
|
||||||
audience: string, The audience, 'aud', that this JWT should contain. If
|
audience: string, The audience, 'aud', that this JWT should contain. If
|
||||||
None then the JWT's 'aud' parameter is not verified.
|
None then the JWT's 'aud' parameter is not verified.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict, The deserialized JSON payload in the JWT.
|
dict, The deserialized JSON payload in the JWT.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AppIdentityError if any checks are failed.
|
AppIdentityError if any checks are failed.
|
||||||
"""
|
"""
|
||||||
jwt = _to_bytes(jwt)
|
jwt = _to_bytes(jwt)
|
||||||
segments = jwt.split(b'.')
|
segments = jwt.split(b'.')
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,12 @@ CREDENTIAL_INFO_REQUEST_JSON = '[]'
|
|||||||
class CredentialInfoResponse(object):
|
class CredentialInfoResponse(object):
|
||||||
"""Credential information response from Developer Shell server.
|
"""Credential information response from Developer Shell server.
|
||||||
|
|
||||||
The credential information response from Developer Shell socket is a
|
The credential information response from Developer Shell socket is a
|
||||||
PBLite-formatted JSON array with fields encoded by their index in the array:
|
PBLite-formatted JSON array with fields encoded by their index in the array:
|
||||||
* Index 0 - user email
|
* Index 0 - user email
|
||||||
* Index 1 - default project ID. None if the project context is not known.
|
* 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.
|
* Index 2 - OAuth2 access token. None if there is no valid auth context.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, json_string):
|
def __init__(self, json_string):
|
||||||
"""Initialize the response data from JSON PBLite array."""
|
"""Initialize the response data from JSON PBLite array."""
|
||||||
@@ -91,14 +91,15 @@ def _SendRecv():
|
|||||||
class DevshellCredentials(client.GoogleCredentials):
|
class DevshellCredentials(client.GoogleCredentials):
|
||||||
"""Credentials object for Google Developer Shell environment.
|
"""Credentials object for Google Developer Shell environment.
|
||||||
|
|
||||||
This object will allow a Google Developer Shell session to identify its user
|
This object will allow a Google Developer Shell session to identify its
|
||||||
to Google and other OAuth 2.0 servers that can verify assertions. It can be
|
user to Google and other OAuth 2.0 servers that can verify assertions. It
|
||||||
used for the purpose of accessing data stored under the user account.
|
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
|
This credential does not require a flow to instantiate because it
|
||||||
a two legged flow, and therefore has all of the required information to
|
represents a two legged flow, and therefore has all of the required
|
||||||
generate and refresh its own access tokens.
|
information to generate and refresh its own access tokens.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, user_agent=None):
|
def __init__(self, user_agent=None):
|
||||||
super(DevshellCredentials, self).__init__(
|
super(DevshellCredentials, self).__init__(
|
||||||
|
|||||||
@@ -79,23 +79,23 @@ class FlowField(models.Field):
|
|||||||
|
|
||||||
|
|
||||||
class Storage(BaseStorage):
|
class Storage(BaseStorage):
|
||||||
"""Store and retrieve a single credential to and from
|
"""Store and retrieve a single credential to and from the datastore.
|
||||||
the datastore.
|
|
||||||
|
|
||||||
This Storage helper presumes the Credentials
|
This Storage helper presumes the Credentials
|
||||||
have been stored as a CredenialsField
|
have been stored as a CredenialsField
|
||||||
on a db model class.
|
on a db model class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, model_class, key_name, key_value, property_name):
|
def __init__(self, model_class, key_name, key_value, property_name):
|
||||||
"""Constructor for Storage.
|
"""Constructor for Storage.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
model: db.Model, model class
|
model: db.Model, model class
|
||||||
key_name: string, key name for the entity that has the credentials
|
key_name: string, key name for the entity that has the credentials
|
||||||
key_value: string, key value for the entity that has the credentials
|
key_value: string, key value for the entity that has the credentials
|
||||||
property_name: string, name of the property that is an CredentialsProperty
|
property_name: string, name of the property that is an
|
||||||
"""
|
CredentialsProperty
|
||||||
|
"""
|
||||||
self.model_class = model_class
|
self.model_class = model_class
|
||||||
self.key_name = key_name
|
self.key_name = key_name
|
||||||
self.key_value = key_value
|
self.key_value = key_value
|
||||||
@@ -104,9 +104,9 @@ class Storage(BaseStorage):
|
|||||||
def locked_get(self):
|
def locked_get(self):
|
||||||
"""Retrieve Credential from datastore.
|
"""Retrieve Credential from datastore.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
oauth2client.Credentials
|
oauth2client.Credentials
|
||||||
"""
|
"""
|
||||||
credential = None
|
credential = None
|
||||||
|
|
||||||
query = {self.key_name: self.key_value}
|
query = {self.key_name: self.key_value}
|
||||||
@@ -120,11 +120,11 @@ class Storage(BaseStorage):
|
|||||||
def locked_put(self, credentials, overwrite=False):
|
def locked_put(self, credentials, overwrite=False):
|
||||||
"""Write a Credentials to the datastore.
|
"""Write a Credentials to the datastore.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
overwrite: Boolean, indicates whether you would like these credentials to
|
overwrite: Boolean, indicates whether you would like these
|
||||||
overwrite any existing stored credentials.
|
credentials to overwrite any existing stored credentials.
|
||||||
"""
|
"""
|
||||||
args = {self.key_name: self.key_value}
|
args = {self.key_name: self.key_value}
|
||||||
|
|
||||||
if overwrite:
|
if overwrite:
|
||||||
|
|||||||
@@ -46,26 +46,27 @@ class Storage(BaseStorage):
|
|||||||
def acquire_lock(self):
|
def acquire_lock(self):
|
||||||
"""Acquires any lock necessary to access this Storage.
|
"""Acquires any lock necessary to access this Storage.
|
||||||
|
|
||||||
This lock is not reentrant."""
|
This lock is not reentrant.
|
||||||
|
"""
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
|
|
||||||
def release_lock(self):
|
def release_lock(self):
|
||||||
"""Release the Storage lock.
|
"""Release the Storage lock.
|
||||||
|
|
||||||
Trying to release a lock that isn't held will result in a
|
Trying to release a lock that isn't held will result in a
|
||||||
RuntimeError.
|
RuntimeError.
|
||||||
"""
|
"""
|
||||||
self._lock.release()
|
self._lock.release()
|
||||||
|
|
||||||
def locked_get(self):
|
def locked_get(self):
|
||||||
"""Retrieve Credential from file.
|
"""Retrieve Credential from file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
oauth2client.client.Credentials
|
oauth2client.client.Credentials
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
||||||
"""
|
"""
|
||||||
credentials = None
|
credentials = None
|
||||||
self._validate_file()
|
self._validate_file()
|
||||||
try:
|
try:
|
||||||
@@ -86,9 +87,9 @@ class Storage(BaseStorage):
|
|||||||
def _create_file_if_needed(self):
|
def _create_file_if_needed(self):
|
||||||
"""Create an empty file if necessary.
|
"""Create an empty file if necessary.
|
||||||
|
|
||||||
This method will not initialize the file. Instead it implements a
|
This method will not initialize the file. Instead it implements a
|
||||||
simple version of "touch" to ensure the file has been created.
|
simple version of "touch" to ensure the file has been created.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(self._filename):
|
if not os.path.exists(self._filename):
|
||||||
old_umask = os.umask(0o177)
|
old_umask = os.umask(0o177)
|
||||||
try:
|
try:
|
||||||
@@ -99,13 +100,12 @@ class Storage(BaseStorage):
|
|||||||
def locked_put(self, credentials):
|
def locked_put(self, credentials):
|
||||||
"""Write Credentials to file.
|
"""Write Credentials to file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
|
|
||||||
Raises:
|
|
||||||
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
||||||
|
"""
|
||||||
self._create_file_if_needed()
|
self._create_file_if_needed()
|
||||||
self._validate_file()
|
self._validate_file()
|
||||||
f = open(self._filename, 'w')
|
f = open(self._filename, 'w')
|
||||||
@@ -115,8 +115,7 @@ class Storage(BaseStorage):
|
|||||||
def locked_delete(self):
|
def locked_delete(self):
|
||||||
"""Delete Credentials file.
|
"""Delete Credentials file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
os.unlink(self._filename)
|
os.unlink(self._filename)
|
||||||
|
|||||||
@@ -337,8 +337,10 @@ class UserOAuth2(object):
|
|||||||
return bp
|
return bp
|
||||||
|
|
||||||
def authorize_view(self):
|
def authorize_view(self):
|
||||||
"""Flask view that starts the authorization flow by redirecting the
|
"""Flask view that starts the authorization flow.
|
||||||
user to the OAuth2 provider."""
|
|
||||||
|
Starts flow by redirecting the user to the OAuth2 provider.
|
||||||
|
"""
|
||||||
args = request.args.to_dict()
|
args = request.args.to_dict()
|
||||||
|
|
||||||
# Scopes will be passed as mutliple args, and to_dict() will only
|
# Scopes will be passed as mutliple args, and to_dict() will only
|
||||||
@@ -355,9 +357,11 @@ class UserOAuth2(object):
|
|||||||
return redirect(auth_url)
|
return redirect(auth_url)
|
||||||
|
|
||||||
def callback_view(self):
|
def callback_view(self):
|
||||||
"""Flask view that handles the user's return from the OAuth2 provider
|
"""Flask view that handles the user's return from OAuth2 provider.
|
||||||
and exchanges the authorization code for credentials and stores the
|
|
||||||
credentials."""
|
On return, exchanges the authorization code for credentials and stores
|
||||||
|
the credentials.
|
||||||
|
"""
|
||||||
if 'error' in request.args:
|
if 'error' in request.args:
|
||||||
reason = request.args.get(
|
reason = request.args.get(
|
||||||
'error_description', request.args.get('error', ''))
|
'error_description', request.args.get('error', ''))
|
||||||
@@ -429,8 +433,9 @@ class UserOAuth2(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def user_id(self):
|
def user_id(self):
|
||||||
"""Returns the a unique identifier for the user or None if there are no
|
"""Returns the a unique identifier for the user
|
||||||
credentials.
|
|
||||||
|
Returns None if there are no credentials.
|
||||||
|
|
||||||
The id is provided by the current credentials' id_token.
|
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):
|
class AppAssertionCredentials(AssertionCredentials):
|
||||||
"""Credentials object for Compute Engine Assertion Grants
|
"""Credentials object for Compute Engine Assertion Grants
|
||||||
|
|
||||||
This object will allow a Compute Engine instance to identify itself to
|
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
|
Google and other OAuth 2.0 servers that can verify assertions. It can be
|
||||||
for the purpose of accessing data stored under an account assigned to the
|
used for the purpose of accessing data stored under an account assigned to
|
||||||
Compute Engine instance itself.
|
the Compute Engine instance itself.
|
||||||
|
|
||||||
This credential does not require a flow to instantiate because it represents
|
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
|
a two legged flow, and therefore has all of the required information to
|
||||||
generate and refresh its own access tokens.
|
generate and refresh its own access tokens.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@util.positional(2)
|
@util.positional(2)
|
||||||
def __init__(self, scope, **kwargs):
|
def __init__(self, scope, **kwargs):
|
||||||
"""Constructor for AppAssertionCredentials
|
"""Constructor for AppAssertionCredentials
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
scope: string or iterable of strings, scope(s) of the credentials being
|
scope: string or iterable of strings, scope(s) of the credentials
|
||||||
requested.
|
being requested.
|
||||||
"""
|
"""
|
||||||
self.scope = util.scopes_to_string(scope)
|
self.scope = util.scopes_to_string(scope)
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
@@ -70,15 +70,16 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
def _refresh(self, http_request):
|
def _refresh(self, http_request):
|
||||||
"""Refreshes the access_token.
|
"""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:
|
Args:
|
||||||
http_request: callable, a callable that matches the method signature of
|
http_request: callable, a callable that matches the method signature
|
||||||
httplib2.Http.request, used to make the refresh request.
|
of httplib2.Http.request, used to make the refresh
|
||||||
|
request.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AccessTokenRefreshError: When the refresh fails.
|
AccessTokenRefreshError: When the refresh fails.
|
||||||
"""
|
"""
|
||||||
query = '?scope=%s' % urllib.parse.quote(self.scope, '')
|
query = '?scope=%s' % urllib.parse.quote(self.scope, '')
|
||||||
uri = META.replace('{?scope}', query)
|
uri = META.replace('{?scope}', query)
|
||||||
response, content = http_request(uri)
|
response, content = http_request(uri)
|
||||||
|
|||||||
@@ -30,32 +30,34 @@ from oauth2client.client import Storage as BaseStorage
|
|||||||
class Storage(BaseStorage):
|
class Storage(BaseStorage):
|
||||||
"""Store and retrieve a single credential to and from the keyring.
|
"""Store and retrieve a single credential to and from the keyring.
|
||||||
|
|
||||||
To use this module you must have the keyring module installed. See
|
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
|
<http://pypi.python.org/pypi/keyring/>. This is an optional module and is
|
||||||
installed with oauth2client by default because it does not work on all the
|
not installed with oauth2client by default because it does not work on all
|
||||||
platforms that oauth2client supports, such as Google App Engine.
|
the platforms that oauth2client supports, such as Google App Engine.
|
||||||
|
|
||||||
The keyring module <http://pypi.python.org/pypi/keyring/> is a cross-platform
|
The keyring module <http://pypi.python.org/pypi/keyring/> is a
|
||||||
library for access the keyring capabilities of the local system. The user will
|
cross-platform library for access the keyring capabilities of the local
|
||||||
be prompted for their keyring password when this module is used, and the
|
system. The user will be prompted for their keyring password when this
|
||||||
manner in which the user is prompted will vary per platform.
|
module is used, and the manner in which the user is prompted will vary per
|
||||||
|
platform.
|
||||||
|
|
||||||
Usage:
|
Usage::
|
||||||
from oauth2client.keyring_storage import Storage
|
|
||||||
|
|
||||||
s = Storage('name_of_application', 'user1')
|
from oauth2client.keyring_storage import Storage
|
||||||
credentials = s.get()
|
|
||||||
|
|
||||||
"""
|
s = Storage('name_of_application', 'user1')
|
||||||
|
credentials = s.get()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, service_name, user_name):
|
def __init__(self, service_name, user_name):
|
||||||
"""Constructor.
|
"""Constructor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
service_name: string, The name of the service under which the credentials
|
service_name: string, The name of the service under which the
|
||||||
are stored.
|
credentials are stored.
|
||||||
user_name: string, The name of the user to store credentials for.
|
user_name: string, The name of the user to store credentials for.
|
||||||
"""
|
"""
|
||||||
self._service_name = service_name
|
self._service_name = service_name
|
||||||
self._user_name = user_name
|
self._user_name = user_name
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
@@ -63,23 +65,24 @@ class Storage(BaseStorage):
|
|||||||
def acquire_lock(self):
|
def acquire_lock(self):
|
||||||
"""Acquires any lock necessary to access this Storage.
|
"""Acquires any lock necessary to access this Storage.
|
||||||
|
|
||||||
This lock is not reentrant."""
|
This lock is not reentrant.
|
||||||
|
"""
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
|
|
||||||
def release_lock(self):
|
def release_lock(self):
|
||||||
"""Release the Storage lock.
|
"""Release the Storage lock.
|
||||||
|
|
||||||
Trying to release a lock that isn't held will result in a
|
Trying to release a lock that isn't held will result in a
|
||||||
RuntimeError.
|
RuntimeError.
|
||||||
"""
|
"""
|
||||||
self._lock.release()
|
self._lock.release()
|
||||||
|
|
||||||
def locked_get(self):
|
def locked_get(self):
|
||||||
"""Retrieve Credential from file.
|
"""Retrieve Credential from file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
oauth2client.client.Credentials
|
oauth2client.client.Credentials
|
||||||
"""
|
"""
|
||||||
credentials = None
|
credentials = None
|
||||||
content = keyring.get_password(self._service_name, self._user_name)
|
content = keyring.get_password(self._service_name, self._user_name)
|
||||||
|
|
||||||
@@ -95,16 +98,16 @@ class Storage(BaseStorage):
|
|||||||
def locked_put(self, credentials):
|
def locked_put(self, credentials):
|
||||||
"""Write Credentials to file.
|
"""Write Credentials to file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
"""
|
"""
|
||||||
keyring.set_password(self._service_name, self._user_name,
|
keyring.set_password(self._service_name, self._user_name,
|
||||||
credentials.to_json())
|
credentials.to_json())
|
||||||
|
|
||||||
def locked_delete(self):
|
def locked_delete(self):
|
||||||
"""Delete Credentials file.
|
"""Delete Credentials file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
"""
|
"""
|
||||||
keyring.set_password(self._service_name, self._user_name, '')
|
keyring.set_password(self._service_name, self._user_name, '')
|
||||||
|
|||||||
@@ -65,11 +65,11 @@ class _Opener(object):
|
|||||||
def __init__(self, filename, mode, fallback_mode):
|
def __init__(self, filename, mode, fallback_mode):
|
||||||
"""Create an Opener.
|
"""Create an Opener.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: string, The pathname of the file.
|
filename: string, The pathname of the file.
|
||||||
mode: string, The preferred mode to access the file with.
|
mode: string, The preferred mode to access the file with.
|
||||||
fallback_mode: string, The mode to use if locking fails.
|
fallback_mode: string, The mode to use if locking fails.
|
||||||
"""
|
"""
|
||||||
self._locked = False
|
self._locked = False
|
||||||
self._filename = filename
|
self._filename = filename
|
||||||
self._mode = mode
|
self._mode = mode
|
||||||
@@ -92,10 +92,10 @@ class _Opener(object):
|
|||||||
def open_and_lock(self, timeout, delay):
|
def open_and_lock(self, timeout, delay):
|
||||||
"""Open the file and lock it.
|
"""Open the file and lock it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
timeout: float, How long to try to lock for.
|
timeout: float, How long to try to lock for.
|
||||||
delay: float, How long to wait between retries.
|
delay: float, How long to wait between retries.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def unlock_and_close(self):
|
def unlock_and_close(self):
|
||||||
@@ -109,17 +109,17 @@ class _PosixOpener(_Opener):
|
|||||||
def open_and_lock(self, timeout, delay):
|
def open_and_lock(self, timeout, delay):
|
||||||
"""Open the file and lock it.
|
"""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:
|
Args:
|
||||||
timeout: float, How long to try to lock for.
|
timeout: float, How long to try to lock for.
|
||||||
delay: float, How long to wait between retries.
|
delay: float, How long to wait between retries.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AlreadyLockedException: if the lock is already acquired.
|
AlreadyLockedException: if the lock is already acquired.
|
||||||
IOError: if the open fails.
|
IOError: if the open fails.
|
||||||
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
||||||
"""
|
"""
|
||||||
if self._locked:
|
if self._locked:
|
||||||
raise AlreadyLockedException('File %s is already locked' %
|
raise AlreadyLockedException('File %s is already locked' %
|
||||||
self._filename)
|
self._filename)
|
||||||
@@ -182,15 +182,15 @@ try:
|
|||||||
def open_and_lock(self, timeout, delay):
|
def open_and_lock(self, timeout, delay):
|
||||||
"""Open the file and lock it.
|
"""Open the file and lock it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
timeout: float, How long to try to lock for.
|
timeout: float, How long to try to lock for.
|
||||||
delay: float, How long to wait between retries
|
delay: float, How long to wait between retries
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AlreadyLockedException: if the lock is already acquired.
|
AlreadyLockedException: if the lock is already acquired.
|
||||||
IOError: if the open fails.
|
IOError: if the open fails.
|
||||||
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
||||||
"""
|
"""
|
||||||
if self._locked:
|
if self._locked:
|
||||||
raise AlreadyLockedException('File %s is already locked' %
|
raise AlreadyLockedException('File %s is already locked' %
|
||||||
self._filename)
|
self._filename)
|
||||||
@@ -258,15 +258,16 @@ try:
|
|||||||
def open_and_lock(self, timeout, delay):
|
def open_and_lock(self, timeout, delay):
|
||||||
"""Open the file and lock it.
|
"""Open the file and lock it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
timeout: float, How long to try to lock for.
|
timeout: float, How long to try to lock for.
|
||||||
delay: float, How long to wait between retries
|
delay: float, How long to wait between retries
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AlreadyLockedException: if the lock is already acquired.
|
AlreadyLockedException: if the lock is already acquired.
|
||||||
IOError: if the open fails.
|
IOError: if the open fails.
|
||||||
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
CredentialsFileSymbolicLinkError: if the file is a symbolic
|
||||||
"""
|
link.
|
||||||
|
"""
|
||||||
if self._locked:
|
if self._locked:
|
||||||
raise AlreadyLockedException('File %s is already locked' %
|
raise AlreadyLockedException('File %s is already locked' %
|
||||||
self._filename)
|
self._filename)
|
||||||
@@ -333,12 +334,13 @@ class LockedFile(object):
|
|||||||
def __init__(self, filename, mode, fallback_mode, use_native_locking=True):
|
def __init__(self, filename, mode, fallback_mode, use_native_locking=True):
|
||||||
"""Construct a LockedFile.
|
"""Construct a LockedFile.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: string, The path of the file to open.
|
filename: string, The path of the file to open.
|
||||||
mode: string, The mode to try to open the file with.
|
mode: string, The mode to try to open the file with.
|
||||||
fallback_mode: string, The mode to use if locking fails.
|
fallback_mode: string, The mode to use if locking fails.
|
||||||
use_native_locking: bool, Whether or not fcntl/win32 locking is used.
|
use_native_locking: bool, Whether or not fcntl/win32 locking is
|
||||||
"""
|
used.
|
||||||
|
"""
|
||||||
opener = None
|
opener = None
|
||||||
if not opener and use_native_locking:
|
if not opener and use_native_locking:
|
||||||
if _Win32Opener:
|
if _Win32Opener:
|
||||||
@@ -366,14 +368,14 @@ class LockedFile(object):
|
|||||||
def open_and_lock(self, timeout=0, delay=0.05):
|
def open_and_lock(self, timeout=0, delay=0.05):
|
||||||
"""Open the file, trying to lock it.
|
"""Open the file, trying to lock it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
timeout: float, The number of seconds to try to acquire the lock.
|
timeout: float, The number of seconds to try to acquire the lock.
|
||||||
delay: float, The number of seconds to wait between retry attempts.
|
delay: float, The number of seconds to wait between retry attempts.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AlreadyLockedException: if the lock is already acquired.
|
AlreadyLockedException: if the lock is already acquired.
|
||||||
IOError: if the open fails.
|
IOError: if the open fails.
|
||||||
"""
|
"""
|
||||||
self._opener.open_and_lock(timeout, delay)
|
self._opener.open_and_lock(timeout, delay)
|
||||||
|
|
||||||
def unlock_and_close(self):
|
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::
|
The format of the stored data is like so::
|
||||||
|
|
||||||
{
|
{
|
||||||
'file_version': 1,
|
'file_version': 1,
|
||||||
'data': [
|
'data': [
|
||||||
{
|
{
|
||||||
'key': {
|
'key': {
|
||||||
'clientId': '<client id>',
|
'clientId': '<client id>',
|
||||||
'userAgent': '<user agent>',
|
'userAgent': '<user agent>',
|
||||||
'scope': '<scope>'
|
'scope': '<scope>'
|
||||||
},
|
},
|
||||||
'credential': {
|
'credential': {
|
||||||
# JSON serialized Credentials.
|
# JSON serialized Credentials.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -77,17 +77,17 @@ def get_credential_storage(filename, client_id, user_agent, scope,
|
|||||||
warn_on_readonly=True):
|
warn_on_readonly=True):
|
||||||
"""Get a Storage instance for a credential.
|
"""Get a Storage instance for a credential.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: The JSON file storing a set of credentials
|
filename: The JSON file storing a set of credentials
|
||||||
client_id: The client_id for the credential
|
client_id: The client_id for the credential
|
||||||
user_agent: The user agent for the credential
|
user_agent: The user agent for the credential
|
||||||
scope: string or iterable of strings, Scope(s) being requested
|
scope: string or iterable of strings, Scope(s) being requested
|
||||||
warn_on_readonly: if True, log a warning if the store is readonly
|
warn_on_readonly: if True, log a warning if the store is readonly
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An object derived from client.Storage for getting/setting the
|
An object derived from client.Storage for getting/setting the
|
||||||
credential.
|
credential.
|
||||||
"""
|
"""
|
||||||
# Recreate the legacy key with these specific parameters
|
# Recreate the legacy key with these specific parameters
|
||||||
key = {'clientId': client_id, 'userAgent': user_agent,
|
key = {'clientId': client_id, 'userAgent': user_agent,
|
||||||
'scope': util.scopes_to_string(scope)}
|
'scope': util.scopes_to_string(scope)}
|
||||||
@@ -100,18 +100,18 @@ def get_credential_storage_custom_string_key(
|
|||||||
filename, key_string, warn_on_readonly=True):
|
filename, key_string, warn_on_readonly=True):
|
||||||
"""Get a Storage instance for a credential using a single string as a key.
|
"""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
|
Allows you to provide a string as a custom key that will be used for
|
||||||
credential storage and retrieval.
|
credential storage and retrieval.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: The JSON file storing a set of credentials
|
filename: The JSON file storing a set of credentials
|
||||||
key_string: A string to use as the key for storing this credential.
|
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
|
warn_on_readonly: if True, log a warning if the store is readonly
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An object derived from client.Storage for getting/setting the
|
An object derived from client.Storage for getting/setting the
|
||||||
credential.
|
credential.
|
||||||
"""
|
"""
|
||||||
# Create a key dictionary that can be used
|
# Create a key dictionary that can be used
|
||||||
key_dict = {'key': key_string}
|
key_dict = {'key': key_string}
|
||||||
return get_credential_storage_custom_key(
|
return get_credential_storage_custom_key(
|
||||||
@@ -123,20 +123,20 @@ def get_credential_storage_custom_key(
|
|||||||
filename, key_dict, warn_on_readonly=True):
|
filename, key_dict, warn_on_readonly=True):
|
||||||
"""Get a Storage instance for a credential using a dictionary as a key.
|
"""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
|
Allows you to provide a dictionary as a custom key that will be used for
|
||||||
credential storage and retrieval.
|
credential storage and retrieval.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: The JSON file storing a set of credentials
|
filename: The JSON file storing a set of credentials
|
||||||
key_dict: A dictionary to use as the key for storing this credential. There
|
key_dict: A dictionary to use as the key for storing this credential.
|
||||||
is no ordering of the keys in the dictionary. Logically equivalent
|
There is no ordering of the keys in the dictionary. Logically
|
||||||
dictionaries will produce equivalent storage keys.
|
equivalent dictionaries will produce equivalent storage keys.
|
||||||
warn_on_readonly: if True, log a warning if the store is readonly
|
warn_on_readonly: if True, log a warning if the store is readonly
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An object derived from client.Storage for getting/setting the
|
An object derived from client.Storage for getting/setting the
|
||||||
credential.
|
credential.
|
||||||
"""
|
"""
|
||||||
multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly)
|
multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly)
|
||||||
key = util.dict_to_tuple_key(key_dict)
|
key = util.dict_to_tuple_key(key_dict)
|
||||||
return multistore._get_storage(key)
|
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):
|
def get_all_credential_keys(filename, warn_on_readonly=True):
|
||||||
"""Gets all the registered credential keys in the given Multistore.
|
"""Gets all the registered credential keys in the given Multistore.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: The JSON file storing a set of credentials
|
filename: The JSON file storing a set of credentials
|
||||||
warn_on_readonly: if True, log a warning if the store is readonly
|
warn_on_readonly: if True, log a warning if the store is readonly
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A list of the credential keys present in the file. They are returned as
|
A list of the credential keys present in the file. They are returned
|
||||||
dictionaries that can be passed into get_credential_storage_custom_key to
|
as dictionaries that can be passed into
|
||||||
get the actual credentials.
|
get_credential_storage_custom_key to get the actual credentials.
|
||||||
"""
|
"""
|
||||||
multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly)
|
multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly)
|
||||||
multistore._lock()
|
multistore._lock()
|
||||||
try:
|
try:
|
||||||
@@ -167,13 +167,13 @@ def get_all_credential_keys(filename, warn_on_readonly=True):
|
|||||||
def _get_multistore(filename, warn_on_readonly=True):
|
def _get_multistore(filename, warn_on_readonly=True):
|
||||||
"""A helper method to initialize the multistore with proper locking.
|
"""A helper method to initialize the multistore with proper locking.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: The JSON file storing a set of credentials
|
filename: The JSON file storing a set of credentials
|
||||||
warn_on_readonly: if True, log a warning if the store is readonly
|
warn_on_readonly: if True, log a warning if the store is readonly
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A multistore object
|
A multistore object
|
||||||
"""
|
"""
|
||||||
filename = os.path.expanduser(filename)
|
filename = os.path.expanduser(filename)
|
||||||
_multistores_lock.acquire()
|
_multistores_lock.acquire()
|
||||||
try:
|
try:
|
||||||
@@ -191,8 +191,8 @@ class _MultiStore(object):
|
|||||||
def __init__(self, filename, warn_on_readonly=True):
|
def __init__(self, filename, warn_on_readonly=True):
|
||||||
"""Initialize the class.
|
"""Initialize the class.
|
||||||
|
|
||||||
This will create the file if necessary.
|
This will create the file if necessary.
|
||||||
"""
|
"""
|
||||||
self._file = LockedFile(filename, 'r+', 'r')
|
self._file = LockedFile(filename, 'r+', 'r')
|
||||||
self._thread_lock = threading.Lock()
|
self._thread_lock = threading.Lock()
|
||||||
self._read_only = False
|
self._read_only = False
|
||||||
@@ -219,26 +219,26 @@ class _MultiStore(object):
|
|||||||
def acquire_lock(self):
|
def acquire_lock(self):
|
||||||
"""Acquires any lock necessary to access this Storage.
|
"""Acquires any lock necessary to access this Storage.
|
||||||
|
|
||||||
This lock is not reentrant.
|
This lock is not reentrant.
|
||||||
"""
|
"""
|
||||||
self._multistore._lock()
|
self._multistore._lock()
|
||||||
|
|
||||||
def release_lock(self):
|
def release_lock(self):
|
||||||
"""Release the Storage lock.
|
"""Release the Storage lock.
|
||||||
|
|
||||||
Trying to release a lock that isn't held will result in a
|
Trying to release a lock that isn't held will result in a
|
||||||
RuntimeError.
|
RuntimeError.
|
||||||
"""
|
"""
|
||||||
self._multistore._unlock()
|
self._multistore._unlock()
|
||||||
|
|
||||||
def locked_get(self):
|
def locked_get(self):
|
||||||
"""Retrieve credential.
|
"""Retrieve credential.
|
||||||
|
|
||||||
The Storage lock must be held when this is called.
|
The Storage lock must be held when this is called.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
oauth2client.client.Credentials
|
oauth2client.client.Credentials
|
||||||
"""
|
"""
|
||||||
credential = self._multistore._get_credential(self._key)
|
credential = self._multistore._get_credential(self._key)
|
||||||
if credential:
|
if credential:
|
||||||
credential.set_store(self)
|
credential.set_store(self)
|
||||||
@@ -247,29 +247,29 @@ class _MultiStore(object):
|
|||||||
def locked_put(self, credentials):
|
def locked_put(self, credentials):
|
||||||
"""Write a credential.
|
"""Write a credential.
|
||||||
|
|
||||||
The Storage lock must be held when this is called.
|
The Storage lock must be held when this is called.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
"""
|
"""
|
||||||
self._multistore._update_credential(self._key, credentials)
|
self._multistore._update_credential(self._key, credentials)
|
||||||
|
|
||||||
def locked_delete(self):
|
def locked_delete(self):
|
||||||
"""Delete a credential.
|
"""Delete a credential.
|
||||||
|
|
||||||
The Storage lock must be held when this is called.
|
The Storage lock must be held when this is called.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
"""
|
"""
|
||||||
self._multistore._delete_credential(self._key)
|
self._multistore._delete_credential(self._key)
|
||||||
|
|
||||||
def _create_file_if_needed(self):
|
def _create_file_if_needed(self):
|
||||||
"""Create an empty file if necessary.
|
"""Create an empty file if necessary.
|
||||||
|
|
||||||
This method will not initialize the file. Instead it implements a
|
This method will not initialize the file. Instead it implements a
|
||||||
simple version of "touch" to ensure the file has been created.
|
simple version of "touch" to ensure the file has been created.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(self._file.filename()):
|
if not os.path.exists(self._file.filename()):
|
||||||
old_umask = os.umask(0o177)
|
old_umask = os.umask(0o177)
|
||||||
try:
|
try:
|
||||||
@@ -318,11 +318,11 @@ class _MultiStore(object):
|
|||||||
def _locked_json_read(self):
|
def _locked_json_read(self):
|
||||||
"""Get the raw content of the multistore file.
|
"""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:
|
Returns:
|
||||||
The contents of the multistore decoded as JSON.
|
The contents of the multistore decoded as JSON.
|
||||||
"""
|
"""
|
||||||
assert self._thread_lock.locked()
|
assert self._thread_lock.locked()
|
||||||
self._file.file_handle().seek(0)
|
self._file.file_handle().seek(0)
|
||||||
return json.load(self._file.file_handle())
|
return json.load(self._file.file_handle())
|
||||||
@@ -330,11 +330,11 @@ class _MultiStore(object):
|
|||||||
def _locked_json_write(self, data):
|
def _locked_json_write(self, data):
|
||||||
"""Write a JSON serializable data structure to the multistore.
|
"""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:
|
Args:
|
||||||
data: The data to be serialized and written.
|
data: The data to be serialized and written.
|
||||||
"""
|
"""
|
||||||
assert self._thread_lock.locked()
|
assert self._thread_lock.locked()
|
||||||
if self._read_only:
|
if self._read_only:
|
||||||
return
|
return
|
||||||
@@ -345,12 +345,12 @@ class _MultiStore(object):
|
|||||||
def _refresh_data_cache(self):
|
def _refresh_data_cache(self):
|
||||||
"""Refresh the contents of the multistore.
|
"""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:
|
Raises:
|
||||||
NewerCredentialStoreError: Raised when a newer client has written the
|
NewerCredentialStoreError: Raised when a newer client has written
|
||||||
store.
|
the store.
|
||||||
"""
|
"""
|
||||||
self._data = {}
|
self._data = {}
|
||||||
try:
|
try:
|
||||||
raw_data = self._locked_json_read()
|
raw_data = self._locked_json_read()
|
||||||
@@ -387,13 +387,13 @@ class _MultiStore(object):
|
|||||||
def _decode_credential_from_json(self, cred_entry):
|
def _decode_credential_from_json(self, cred_entry):
|
||||||
"""Load a credential from our JSON serialization.
|
"""Load a credential from our JSON serialization.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cred_entry: A dict entry from the data member of our format
|
cred_entry: A dict entry from the data member of our format
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(key, cred) where the key is the key tuple and the cred is the
|
(key, cred) where the key is the key tuple and the cred is the
|
||||||
OAuth2Credential object.
|
OAuth2Credential object.
|
||||||
"""
|
"""
|
||||||
raw_key = cred_entry['key']
|
raw_key = cred_entry['key']
|
||||||
key = util.dict_to_tuple_key(raw_key)
|
key = util.dict_to_tuple_key(raw_key)
|
||||||
credential = None
|
credential = None
|
||||||
@@ -403,8 +403,8 @@ class _MultiStore(object):
|
|||||||
def _write(self):
|
def _write(self):
|
||||||
"""Write the cached data back out.
|
"""Write the cached data back out.
|
||||||
|
|
||||||
The multistore must be locked.
|
The multistore must be locked.
|
||||||
"""
|
"""
|
||||||
raw_data = {'file_version': 1}
|
raw_data = {'file_version': 1}
|
||||||
raw_creds = []
|
raw_creds = []
|
||||||
raw_data['data'] = raw_creds
|
raw_data['data'] = raw_creds
|
||||||
@@ -417,44 +417,45 @@ class _MultiStore(object):
|
|||||||
def _get_all_credential_keys(self):
|
def _get_all_credential_keys(self):
|
||||||
"""Gets all the registered credential keys in the multistore.
|
"""Gets all the registered credential keys in the multistore.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A list of dictionaries corresponding to all the keys currently registered
|
A list of dictionaries corresponding to all the keys currently
|
||||||
"""
|
registered
|
||||||
|
"""
|
||||||
return [dict(key) for key in self._data.keys()]
|
return [dict(key) for key in self._data.keys()]
|
||||||
|
|
||||||
def _get_credential(self, key):
|
def _get_credential(self, key):
|
||||||
"""Get a credential from the multistore.
|
"""Get a credential from the multistore.
|
||||||
|
|
||||||
The multistore must be locked.
|
The multistore must be locked.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key: The key used to retrieve the credential
|
key: The key used to retrieve the credential
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The credential specified or None if not present
|
The credential specified or None if not present
|
||||||
"""
|
"""
|
||||||
return self._data.get(key, None)
|
return self._data.get(key, None)
|
||||||
|
|
||||||
def _update_credential(self, key, cred):
|
def _update_credential(self, key, cred):
|
||||||
"""Update a credential and write the multistore.
|
"""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:
|
Args:
|
||||||
key: The key used to retrieve the credential
|
key: The key used to retrieve the credential
|
||||||
cred: The OAuth2Credential to update/set
|
cred: The OAuth2Credential to update/set
|
||||||
"""
|
"""
|
||||||
self._data[key] = cred
|
self._data[key] = cred
|
||||||
self._write()
|
self._write()
|
||||||
|
|
||||||
def _delete_credential(self, key):
|
def _delete_credential(self, key):
|
||||||
"""Delete a credential and write the multistore.
|
"""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:
|
Args:
|
||||||
key: The key used to retrieve the credential
|
key: The key used to retrieve the credential
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
del self._data[key]
|
del self._data[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -464,12 +465,12 @@ class _MultiStore(object):
|
|||||||
def _get_storage(self, key):
|
def _get_storage(self, key):
|
||||||
"""Get a Storage object to get/set a credential.
|
"""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:
|
Args:
|
||||||
key: The key used to retrieve the credential
|
key: The key used to retrieve the credential
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A Storage object that can be used to get/set this cred
|
A Storage object that can be used to get/set this cred
|
||||||
"""
|
"""
|
||||||
return self._Storage(self, key)
|
return self._Storage(self, key)
|
||||||
|
|||||||
@@ -49,42 +49,42 @@ gflags.DEFINE_multi_int('auth_host_port', [8080, 8090],
|
|||||||
def run(flow, storage, http=None):
|
def run(flow, storage, http=None):
|
||||||
"""Core code for a command-line application.
|
"""Core code for a command-line application.
|
||||||
|
|
||||||
The ``run()`` function is called from your application and runs
|
The ``run()`` function is called from your application and runs
|
||||||
through all the steps to obtain credentials. It takes a ``Flow``
|
through all the steps to obtain credentials. It takes a ``Flow``
|
||||||
argument and attempts to open an authorization server page in the
|
argument and attempts to open an authorization server page in the
|
||||||
user's default web browser. The server asks the user to grant your
|
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,
|
application access to the user's data. If the user grants access,
|
||||||
the ``run()`` function returns new credentials. The new credentials
|
the ``run()`` function returns new credentials. The new credentials
|
||||||
are also stored in the ``storage`` argument, which updates the file
|
are also stored in the ``storage`` argument, which updates the file
|
||||||
associated with the ``Storage`` object.
|
associated with the ``Storage`` object.
|
||||||
|
|
||||||
It presumes it is run from a command-line application and supports the
|
It presumes it is run from a command-line application and supports the
|
||||||
following flags:
|
following flags:
|
||||||
|
|
||||||
``--auth_host_name`` (string, default: ``localhost``)
|
``--auth_host_name`` (string, default: ``localhost``)
|
||||||
Host name to use when running a local web server to handle
|
Host name to use when running a local web server to handle
|
||||||
redirects during OAuth authorization.
|
redirects during OAuth authorization.
|
||||||
|
|
||||||
``--auth_host_port`` (integer, default: ``[8080, 8090]``)
|
``--auth_host_port`` (integer, default: ``[8080, 8090]``)
|
||||||
Port to use when running a local web server to handle redirects
|
Port to use when running a local web server to handle redirects
|
||||||
during OAuth authorization. Repeat this option to specify a list
|
during OAuth authorization. Repeat this option to specify a list
|
||||||
of values.
|
of values.
|
||||||
|
|
||||||
``--[no]auth_local_webserver`` (boolean, default: ``True``)
|
``--[no]auth_local_webserver`` (boolean, default: ``True``)
|
||||||
Run a local web server to handle redirects during OAuth authorization.
|
Run a local web server to handle redirects during OAuth authorization.
|
||||||
|
|
||||||
Since it uses flags make sure to initialize the ``gflags`` module before
|
Since it uses flags make sure to initialize the ``gflags`` module before
|
||||||
calling ``run()``.
|
calling ``run()``.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
flow: Flow, an OAuth 2.0 Flow to step through.
|
flow: Flow, an OAuth 2.0 Flow to step through.
|
||||||
storage: Storage, a ``Storage`` to store the credential in.
|
storage: Storage, a ``Storage`` to store the credential in.
|
||||||
http: An instance of ``httplib2.Http.request`` or something that acts
|
http: An instance of ``httplib2.Http.request`` or something that acts
|
||||||
like it.
|
like it.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Credentials, the obtained credential.
|
Credentials, the obtained credential.
|
||||||
"""
|
"""
|
||||||
logging.warning('This function, oauth2client.tools.run(), and the use of '
|
logging.warning('This function, oauth2client.tools.run(), and the use of '
|
||||||
'the gflags library are deprecated and will be removed in a future '
|
'the gflags library are deprecated and will be removed in a future '
|
||||||
'version of the library.')
|
'version of the library.')
|
||||||
|
|||||||
@@ -73,26 +73,26 @@ argparser = _CreateArgumentParser()
|
|||||||
class ClientRedirectServer(BaseHTTPServer.HTTPServer):
|
class ClientRedirectServer(BaseHTTPServer.HTTPServer):
|
||||||
"""A server to handle OAuth 2.0 redirects back to localhost.
|
"""A server to handle OAuth 2.0 redirects back to localhost.
|
||||||
|
|
||||||
Waits for a single request and parses the query parameters
|
Waits for a single request and parses the query parameters
|
||||||
into query_params and then stops serving.
|
into query_params and then stops serving.
|
||||||
"""
|
"""
|
||||||
query_params = {}
|
query_params = {}
|
||||||
|
|
||||||
|
|
||||||
class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
"""A handler for OAuth 2.0 redirects back to localhost.
|
"""A handler for OAuth 2.0 redirects back to localhost.
|
||||||
|
|
||||||
Waits for a single request and parses the query parameters
|
Waits for a single request and parses the query parameters
|
||||||
into the servers query_params and then stops serving.
|
into the servers query_params and then stops serving.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
"""Handle a GET request.
|
"""Handle a GET request.
|
||||||
|
|
||||||
Parses the query parameters and prints a message
|
Parses the query parameters and prints a message
|
||||||
if the flow has completed. Note that we can't detect
|
if the flow has completed. Note that we can't detect
|
||||||
if an error occurred.
|
if an error occurred.
|
||||||
"""
|
"""
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header("Content-type", "text/html")
|
self.send_header("Content-type", "text/html")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
@@ -111,54 +111,53 @@ class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||||||
def run_flow(flow, storage, flags, http=None):
|
def run_flow(flow, storage, flags, http=None):
|
||||||
"""Core code for a command-line application.
|
"""Core code for a command-line application.
|
||||||
|
|
||||||
The ``run()`` function is called from your application and runs
|
The ``run()`` function is called from your application and runs
|
||||||
through all the steps to obtain credentials. It takes a ``Flow``
|
through all the steps to obtain credentials. It takes a ``Flow``
|
||||||
argument and attempts to open an authorization server page in the
|
argument and attempts to open an authorization server page in the
|
||||||
user's default web browser. The server asks the user to grant your
|
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,
|
application access to the user's data. If the user grants access,
|
||||||
the ``run()`` function returns new credentials. The new credentials
|
the ``run()`` function returns new credentials. The new credentials
|
||||||
are also stored in the ``storage`` argument, which updates the file
|
are also stored in the ``storage`` argument, which updates the file
|
||||||
associated with the ``Storage`` object.
|
associated with the ``Storage`` object.
|
||||||
|
|
||||||
It presumes it is run from a command-line application and supports the
|
It presumes it is run from a command-line application and supports the
|
||||||
following flags:
|
following flags:
|
||||||
|
|
||||||
``--auth_host_name`` (string, default: ``localhost``)
|
``--auth_host_name`` (string, default: ``localhost``)
|
||||||
Host name to use when running a local web server to handle
|
Host name to use when running a local web server to handle
|
||||||
redirects during OAuth authorization.
|
redirects during OAuth authorization.
|
||||||
|
|
||||||
``--auth_host_port`` (integer, default: ``[8080, 8090]``)
|
``--auth_host_port`` (integer, default: ``[8080, 8090]``)
|
||||||
Port to use when running a local web server to handle redirects
|
Port to use when running a local web server to handle redirects
|
||||||
during OAuth authorization. Repeat this option to specify a list
|
during OAuth authorization. Repeat this option to specify a list
|
||||||
of values.
|
of values.
|
||||||
|
|
||||||
``--[no]auth_local_webserver`` (boolean, default: ``True``)
|
``--[no]auth_local_webserver`` (boolean, default: ``True``)
|
||||||
Run a local web server to handle redirects during OAuth authorization.
|
Run a local web server to handle redirects during OAuth
|
||||||
|
authorization.
|
||||||
|
|
||||||
|
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
|
Returns:
|
||||||
definitions that ``run()`` requires. You can pass that ``ArgumentParser`` to your
|
Credentials, the obtained credential.
|
||||||
``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.
|
|
||||||
"""
|
|
||||||
logging.getLogger().setLevel(getattr(logging, flags.logging_level))
|
logging.getLogger().setLevel(getattr(logging, flags.logging_level))
|
||||||
if not flags.noauth_local_webserver:
|
if not flags.noauth_local_webserver:
|
||||||
success = False
|
success = False
|
||||||
|
|||||||
@@ -52,73 +52,74 @@ positional_parameters_enforcement = POSITIONAL_WARNING
|
|||||||
def positional(max_positional_args):
|
def positional(max_positional_args):
|
||||||
"""A decorator to declare that only the first N arguments my be positional.
|
"""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
|
This decorator makes it easy to support Python 3 style keyword-only
|
||||||
parameters. For example, in Python 3 it is possible to write::
|
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, 'kw1', 'kw2') # Raises exception.
|
||||||
fn(10, kwonly1='kw1') # Ok.
|
fn(10, kwonly1='kw1') # Ok.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
To define a function like above, do::
|
To define a function like above, do::
|
||||||
|
|
||||||
@positional(1)
|
@positional(1)
|
||||||
def fn(pos1, kwonly1=None, kwonly2=None):
|
def fn(pos1, kwonly1=None, kwonly2=None):
|
||||||
...
|
...
|
||||||
|
|
||||||
If no default value is provided to a keyword argument, it becomes a required
|
If no default value is provided to a keyword argument, it becomes a
|
||||||
keyword argument::
|
required keyword argument::
|
||||||
|
|
||||||
@positional(0)
|
@positional(0)
|
||||||
def fn(required_kw):
|
def fn(required_kw):
|
||||||
...
|
...
|
||||||
|
|
||||||
This must be called with the keyword parameter::
|
This must be called with the keyword parameter::
|
||||||
|
|
||||||
fn() # Raises exception.
|
fn() # Raises exception.
|
||||||
fn(10) # Raises exception.
|
fn(10) # Raises exception.
|
||||||
fn(required_kw=10) # Ok.
|
fn(required_kw=10) # Ok.
|
||||||
|
|
||||||
When defining instance or class methods always remember to account for
|
When defining instance or class methods always remember to account for
|
||||||
``self`` and ``cls``::
|
``self`` and ``cls``::
|
||||||
|
|
||||||
class MyClass(object):
|
class MyClass(object):
|
||||||
|
|
||||||
@positional(2)
|
@positional(2)
|
||||||
def my_method(self, pos1, kwonly1=None):
|
def my_method(self, pos1, kwonly1=None):
|
||||||
...
|
...
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@positional(2)
|
@positional(2)
|
||||||
def my_method(cls, pos1, kwonly1=None):
|
def my_method(cls, pos1, kwonly1=None):
|
||||||
...
|
...
|
||||||
|
|
||||||
The positional decorator behavior is controlled by
|
The positional decorator behavior is controlled by
|
||||||
``util.positional_parameters_enforcement``, which may be set to
|
``util.positional_parameters_enforcement``, which may be set to
|
||||||
``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or
|
``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or
|
||||||
``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do
|
``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do
|
||||||
nothing, respectively, if a declaration is violated.
|
nothing, respectively, if a declaration is violated.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
max_positional_arguments: Maximum number of positional arguments. All
|
max_positional_arguments: Maximum number of positional arguments. All
|
||||||
parameters after the this index must be keyword only.
|
parameters after the this index must be
|
||||||
|
keyword only.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A decorator that prevents using arguments after max_positional_args from
|
A decorator that prevents using arguments after max_positional_args
|
||||||
being used as positional parameters.
|
from being used as positional parameters.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError if a key-word only argument is provided as a positional
|
TypeError: if a key-word only argument is provided as a positional
|
||||||
parameter, but only if util.positional_parameters_enforcement is set to
|
parameter, but only if
|
||||||
POSITIONAL_EXCEPTION.
|
util.positional_parameters_enforcement is set to
|
||||||
|
POSITIONAL_EXCEPTION.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def positional_decorator(wrapped):
|
def positional_decorator(wrapped):
|
||||||
@functools.wraps(wrapped)
|
@functools.wraps(wrapped)
|
||||||
@@ -148,16 +149,16 @@ def positional(max_positional_args):
|
|||||||
def scopes_to_string(scopes):
|
def scopes_to_string(scopes):
|
||||||
"""Converts scope value to a string.
|
"""Converts scope value to a string.
|
||||||
|
|
||||||
If scopes is a string then it is simply passed through. If scopes is an
|
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
|
iterable then a string is returned that is all the individual scopes
|
||||||
concatenated with spaces.
|
concatenated with spaces.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
scopes: string or iterable of strings, the scopes.
|
scopes: string or iterable of strings, the scopes.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The scopes formatted as a single string.
|
The scopes formatted as a single string.
|
||||||
"""
|
"""
|
||||||
if isinstance(scopes, six.string_types):
|
if isinstance(scopes, six.string_types):
|
||||||
return scopes
|
return scopes
|
||||||
else:
|
else:
|
||||||
@@ -167,15 +168,15 @@ def scopes_to_string(scopes):
|
|||||||
def string_to_scopes(scopes):
|
def string_to_scopes(scopes):
|
||||||
"""Converts stringifed scope value to a list.
|
"""Converts stringifed scope value to a list.
|
||||||
|
|
||||||
If scopes is a list then it is simply passed through. If scopes is an
|
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.
|
string then a list of each individual scope is returned.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
scopes: a string or iterable of strings, the scopes.
|
scopes: a string or iterable of strings, the scopes.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The scopes in a list.
|
The scopes in a list.
|
||||||
"""
|
"""
|
||||||
if not scopes:
|
if not scopes:
|
||||||
return []
|
return []
|
||||||
if isinstance(scopes, six.string_types):
|
if isinstance(scopes, six.string_types):
|
||||||
@@ -187,31 +188,31 @@ def string_to_scopes(scopes):
|
|||||||
def dict_to_tuple_key(dictionary):
|
def dict_to_tuple_key(dictionary):
|
||||||
"""Converts a dictionary to a tuple that can be used as an immutable key.
|
"""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
|
The resulting key is always sorted so that logically equivalent
|
||||||
always produce an identical tuple for a key.
|
dictionaries always produce an identical tuple for a key.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dictionary: the dictionary to use as the key.
|
dictionary: the dictionary to use as the key.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A tuple representing the dictionary in it's naturally sorted ordering.
|
A tuple representing the dictionary in it's naturally sorted ordering.
|
||||||
"""
|
"""
|
||||||
return tuple(sorted(dictionary.items()))
|
return tuple(sorted(dictionary.items()))
|
||||||
|
|
||||||
|
|
||||||
def _add_query_parameter(url, name, value):
|
def _add_query_parameter(url, name, value):
|
||||||
"""Adds a query parameter to a url.
|
"""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:
|
Args:
|
||||||
url: string, url to add the query parameter to.
|
url: string, url to add the query parameter to.
|
||||||
name: string, query parameter name.
|
name: string, query parameter name.
|
||||||
value: string, query parameter value.
|
value: string, query parameter value.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Updated query parameter. Does not update the url if value is None.
|
Updated query parameter. Does not update the url if value is None.
|
||||||
"""
|
"""
|
||||||
if value is None:
|
if value is None:
|
||||||
return url
|
return url
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -47,17 +47,17 @@ def _force_bytes(s):
|
|||||||
def generate_token(key, user_id, action_id="", when=None):
|
def generate_token(key, user_id, action_id="", when=None):
|
||||||
"""Generates a URL-safe token for the given user, action, time tuple.
|
"""Generates a URL-safe token for the given user, action, time tuple.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key: secret key to use.
|
key: secret key to use.
|
||||||
user_id: the user ID of the authenticated user.
|
user_id: the user ID of the authenticated user.
|
||||||
action_id: a string identifier of the action they requested
|
action_id: a string identifier of the action they requested
|
||||||
authorization for.
|
authorization for.
|
||||||
when: the time in seconds since the epoch at which the user was
|
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.
|
authorized for this action. If not set the current time is used.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A string XSRF protection token.
|
A string XSRF protection token.
|
||||||
"""
|
"""
|
||||||
when = _force_bytes(when or int(time.time()))
|
when = _force_bytes(when or int(time.time()))
|
||||||
digester = hmac.new(_force_bytes(key))
|
digester = hmac.new(_force_bytes(key))
|
||||||
digester.update(_force_bytes(user_id))
|
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):
|
def validate_token(key, token, user_id, action_id="", current_time=None):
|
||||||
"""Validates that the given token authorizes the user for the action.
|
"""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
|
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).
|
does not match what generateToken outputs (i.e. the token was forged).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key: secret key to use.
|
key: secret key to use.
|
||||||
token: a string of the token generated by generateToken.
|
token: a string of the token generated by generateToken.
|
||||||
user_id: the user ID of the authenticated user.
|
user_id: the user ID of the authenticated user.
|
||||||
action_id: a string identifier of the action they requested
|
action_id: a string identifier of the action they requested
|
||||||
authorization for.
|
authorization for.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A boolean - True if the user is authorized for the action, False
|
A boolean - True if the user is authorized for the action, False
|
||||||
otherwise.
|
otherwise.
|
||||||
"""
|
"""
|
||||||
if not token:
|
if not token:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user