Library update
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
oauthlib.oauth2.draft_25
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module is an implementation of various logic needed
|
||||
for signing and checking OAuth 2.0 draft 25 requests.
|
||||
"""
|
||||
|
||||
class Client(object):
|
||||
pass
|
||||
|
||||
class Server(object):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
from __future__ import absolute_import
|
||||
"""
|
||||
oauthlib.oauth2.draft25.tokens
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains methods for adding two types of access tokens to requests.
|
||||
|
||||
- Bearer http://tools.ietf.org/html/draft-ietf-oauth-saml2-bearer-08
|
||||
- MAC http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-00
|
||||
|
||||
"""
|
||||
from binascii import b2a_base64
|
||||
import hashlib
|
||||
import hmac
|
||||
from urlparse import urlparse
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
def prepare_mac_header(token, uri, key, http_method, nonce=None, headers=None,
|
||||
body=None, ext=u'', hash_algorithm=u'hmac-sha-1'):
|
||||
"""Add an `MAC Access Authentication`_ signature to headers.
|
||||
|
||||
Unlike OAuth 1, this HMAC signature does not require inclusion of the request
|
||||
payload/body, neither does it use a combination of client_secret and
|
||||
token_secret but rather a mac_key provided together with the access token.
|
||||
|
||||
Currently two algorithms are supported, "hmac-sha-1" and "hmac-sha-256",
|
||||
`extension algorithms`_ are not supported.
|
||||
|
||||
Example MAC Authorization header, linebreaks added for clarity
|
||||
|
||||
Authorization: MAC id="h480djs93hd8",
|
||||
nonce="1336363200:dj83hs9s",
|
||||
mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
|
||||
|
||||
.. _`MAC Access Authentication`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
|
||||
.. _`extension algorithms`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-7.1
|
||||
|
||||
:param uri: Request URI.
|
||||
:param headers: Request headers as a dictionary.
|
||||
:param http_method: HTTP Request method.
|
||||
:param key: MAC given provided by token endpoint.
|
||||
:param algorithm: HMAC algorithm provided by token endpoint.
|
||||
:return: headers dictionary with the authorization field added.
|
||||
"""
|
||||
http_method = http_method.upper()
|
||||
host, port = utils.host_from_uri(uri)
|
||||
|
||||
if hash_algorithm.lower() == u'hmac-sha-1':
|
||||
h = hashlib.sha1
|
||||
else:
|
||||
h = hashlib.sha256
|
||||
|
||||
nonce = nonce or u'{0}:{1}'.format(utils.generate_nonce(), utils.generate_timestamp())
|
||||
sch, net, path, par, query, fra = urlparse(uri)
|
||||
|
||||
if query:
|
||||
request_uri = path + u'?' + query
|
||||
else:
|
||||
request_uri = path
|
||||
|
||||
# Hash the body/payload
|
||||
if body is not None:
|
||||
bodyhash = b2a_base64(h(body).digest())[:-1].decode('utf-8')
|
||||
else:
|
||||
bodyhash = u''
|
||||
|
||||
# Create the normalized base string
|
||||
base = []
|
||||
base.append(nonce)
|
||||
base.append(http_method.upper())
|
||||
base.append(request_uri)
|
||||
base.append(host)
|
||||
base.append(port)
|
||||
base.append(bodyhash)
|
||||
base.append(ext)
|
||||
base_string = '\n'.join(base) + u'\n'
|
||||
|
||||
# hmac struggles with unicode strings - http://bugs.python.org/issue5285
|
||||
if isinstance(key, unicode):
|
||||
key = key.encode('utf-8')
|
||||
sign = hmac.new(key, base_string, h)
|
||||
sign = b2a_base64(sign.digest())[:-1].decode('utf-8')
|
||||
|
||||
header = []
|
||||
header.append(u'MAC id="%s"' % token)
|
||||
header.append(u'nonce="%s"' % nonce)
|
||||
if bodyhash:
|
||||
header.append(u'bodyhash="%s"' % bodyhash)
|
||||
if ext:
|
||||
header.append(u'ext="%s"' % ext)
|
||||
header.append(u'mac="%s"' % sign)
|
||||
|
||||
headers = headers or {}
|
||||
headers[u'Authorization'] = u', '.join(header)
|
||||
return headers
|
||||
|
||||
|
||||
def prepare_bearer_uri(token, uri):
|
||||
"""Add a `Bearer Token`_ to the request URI.
|
||||
Not recommended, use only if client can't use authorization header or body.
|
||||
|
||||
http://www.example.com/path?access_token=h480djs93hd8
|
||||
|
||||
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
|
||||
"""
|
||||
return utils.add_params_to_uri(uri, [((u'access_token', token))])
|
||||
|
||||
|
||||
def prepare_bearer_headers(token, headers=None):
|
||||
"""Add a `Bearer Token`_ to the request URI.
|
||||
Recommended method of passing bearer tokens.
|
||||
|
||||
Authorization: Bearer h480djs93hd8
|
||||
|
||||
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
|
||||
"""
|
||||
headers = headers or {}
|
||||
headers[u'Authorization'] = u'Bearer %s' % token
|
||||
return headers
|
||||
|
||||
|
||||
def prepare_bearer_body(token, body=u''):
|
||||
"""Add a `Bearer Token`_ to the request body.
|
||||
|
||||
access_token=h480djs93hd8
|
||||
|
||||
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
|
||||
"""
|
||||
return utils.add_params_to_qs(body, [((u'access_token', token))])
|
||||
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
oauthlib.utils
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module contains utility methods used by various parts of the OAuth 2 spec.
|
||||
"""
|
||||
|
||||
import random
|
||||
import string
|
||||
import time
|
||||
import urllib
|
||||
from urlparse import urlparse, urlunparse, parse_qsl
|
||||
|
||||
UNICODE_ASCII_CHARACTER_SET = (string.ascii_letters.decode('ascii') +
|
||||
string.digits.decode('ascii'))
|
||||
|
||||
def add_params_to_qs(query, params):
|
||||
"""Extend a query with a list of two-tuples.
|
||||
|
||||
:param query: Query string.
|
||||
:param params: List of two-tuples.
|
||||
:return: extended query
|
||||
"""
|
||||
queryparams = parse_qsl(query, keep_blank_values=True)
|
||||
queryparams.extend(params)
|
||||
return urlencode(queryparams)
|
||||
|
||||
|
||||
def add_params_to_uri(uri, params):
|
||||
"""Add a list of two-tuples to the uri query components.
|
||||
|
||||
:param uri: Full URI.
|
||||
:param params: List of two-tuples.
|
||||
:return: uri with extended query
|
||||
"""
|
||||
sch, net, path, par, query, fra = urlparse(uri)
|
||||
query = add_params_to_qs(query, params)
|
||||
return urlunparse((sch, net, path, par, query, fra))
|
||||
|
||||
|
||||
def escape(u):
|
||||
"""Escape a string in an OAuth-compatible fashion.
|
||||
|
||||
Per `section 3.6`_ of the spec.
|
||||
|
||||
.. _`section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
|
||||
|
||||
"""
|
||||
if not isinstance(u, unicode):
|
||||
raise ValueError('Only unicode objects are escapable.')
|
||||
return urllib.quote(u.encode('utf-8'), safe='~')
|
||||
|
||||
|
||||
def generate_nonce():
|
||||
"""Generate pseudorandom nonce that is unlikely to repeat.
|
||||
|
||||
Per `section 3.2.1`_ of the MAC Access Authentication spec.
|
||||
|
||||
A random 64-bit number is appended to the epoch timestamp for both
|
||||
randomness and to decrease the likelihood of collisions.
|
||||
|
||||
.. _`section 3.2.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
|
||||
"""
|
||||
return unicode(unicode(random.getrandbits(64)) + generate_timestamp())
|
||||
|
||||
|
||||
def generate_timestamp():
|
||||
"""Get seconds since epoch (UTC).
|
||||
|
||||
Per `section 3.2.1`_ of the MAC Access Authentication spec.
|
||||
|
||||
.. _`section 3.2.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
|
||||
"""
|
||||
return unicode(int(time.time()))
|
||||
|
||||
|
||||
def generate_token(length=20, chars=UNICODE_ASCII_CHARACTER_SET):
|
||||
"""Generates a generic OAuth 2 token
|
||||
|
||||
According to `section 1.4`_ and `section 1.5` of the spec, the method of token
|
||||
construction is undefined. This implementation is simply a random selection
|
||||
of `length` choices from `chars`. SystemRandom is used since it provides
|
||||
higher entropy than random.choice.
|
||||
|
||||
.. _`section 1.4`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-1.4
|
||||
.. _`section 1.5`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-1.5
|
||||
"""
|
||||
rand = random.SystemRandom()
|
||||
return u''.join(rand.choice(chars) for x in range(length))
|
||||
|
||||
|
||||
def host_from_uri(uri):
|
||||
"""Extract hostname and port from URI.
|
||||
|
||||
Will use default port for HTTP and HTTPS if none is present in the URI.
|
||||
|
||||
>>> host_from_uri(u'https://www.example.com/path?query')
|
||||
u'www.example.com', u'443'
|
||||
>>> host_from_uri(u'http://www.example.com:8080/path?query')
|
||||
u'www.example.com', u'8080'
|
||||
|
||||
:param uri: Full URI.
|
||||
:param http_method: HTTP request method.
|
||||
:return: hostname, port
|
||||
"""
|
||||
default_ports = {
|
||||
u'HTTP' : u'80',
|
||||
u'HTTPS' : u'443',
|
||||
}
|
||||
|
||||
sch, netloc, path, par, query, fra = urlparse(uri)
|
||||
if u':' in netloc:
|
||||
netloc, port = netloc.split(u':', 1)
|
||||
else:
|
||||
port = default_ports.get(sch.upper())
|
||||
|
||||
return netloc, port
|
||||
|
||||
|
||||
def urlencode(query):
|
||||
"""Encode a sequence of two-element tuples or dictionary into a URL query string.
|
||||
|
||||
Operates using an OAuth-safe escape() method, in contrast to urllib.urlenocde.
|
||||
"""
|
||||
# Convert dictionaries to list of tuples
|
||||
if isinstance(query, dict):
|
||||
query = query.items()
|
||||
return "&".join(['='.join([escape(k), escape(v)]) for k, v in query])
|
||||
Reference in New Issue
Block a user