You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

141 lines
4.2 KiB

# -*- coding: utf-8 -*-
"""
oauthlib.utils
~~~~~~~~~~~~~~
This module contains utility methods used by various parts of the OAuth
spec.
"""
import string
import time
import urllib2
from random import getrandbits, choice
from oauthlib.common import quote, unquote
UNICODE_ASCII_CHARACTER_SET = (string.ascii_letters.decode('ascii') +
string.digits.decode('ascii'))
def filter_params(target):
"""Decorator which filters params to remove non-oauth_* parameters
Assumes the decorated method takes a params dict or list of tuples as its
first argument.
"""
def wrapper(params, *args, **kwargs):
params = filter_oauth_params(params)
return target(params, *args, **kwargs)
wrapper.__doc__ = target.__doc__
return wrapper
def filter_oauth_params(params):
"""Removes all non oauth parameters from a dict or a list of params."""
is_oauth = lambda kv: kv[0].startswith(u"oauth_")
if isinstance(params, dict):
return filter(is_oauth, params.items())
else:
return filter(is_oauth, params)
def generate_timestamp():
"""Get seconds since epoch (UTC).
Per `section 3.3`_ of the spec.
.. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
"""
return unicode(int(time.time()))
def generate_nonce():
"""Generate pseudorandom nonce that is unlikely to repeat.
Per `section 3.3`_ of the spec.
A random 64-bit number is appended to the epoch timestamp for both
randomness and to decrease the likelihood of collisions.
.. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
"""
return unicode(getrandbits(64)) + generate_timestamp()
def generate_token(length=20, chars=UNICODE_ASCII_CHARACTER_SET):
"""Generates a generic OAuth token
According to `section 2`_ of the spec, the method of token
construction is undefined. This implementation is simply a random selection
of `length` choices from `chars`.
Credit to Ignacio Vazquez-Abrams for his excellent `Stackoverflow answer`_
.. _`Stackoverflow answer` : http://stackoverflow.com/questions/2257441/
python-random-string-generation-with-upper-case-letters-and-digits
"""
return u''.join(choice(chars) for x in range(length))
def escape(u):
"""Escape a unicode 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.')
# Letters, digits, and the characters '_.-' are already treated as safe
# by urllib.quote(). We need to add '~' to fully support rfc5849.
return quote(u, safe='~')
def unescape(u):
if not isinstance(u, unicode):
raise ValueError('Only unicode objects are unescapable.')
return unquote(u)
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.urlencode.
"""
# Convert dictionaries to list of tuples
if isinstance(query, dict):
query = query.items()
return u"&".join([u'='.join([escape(k), escape(v)]) for k, v in query])
def parse_keqv_list(l):
"""A unicode-safe version of urllib2.parse_keqv_list"""
encoded_list = [u.encode('utf-8') for u in l]
encoded_parsed = urllib2.parse_keqv_list(encoded_list)
return dict((k.decode('utf-8'),
v.decode('utf-8')) for k,v in encoded_parsed.items())
def parse_http_list(u):
"""A unicode-safe version of urllib2.parse_http_list"""
encoded_str = u.encode('utf-8')
encoded_list = urllib2.parse_http_list(encoded_str)
return [s.decode('utf-8') for s in encoded_list]
def parse_authorization_header(authorization_header):
"""Parse an OAuth authorization header into a list of 2-tuples"""
auth_scheme = u'OAuth '
if authorization_header.startswith(auth_scheme):
authorization_header = authorization_header.replace(auth_scheme, u'', 1)
items = parse_http_list(authorization_header)
try:
return parse_keqv_list(items).items()
except ValueError:
raise ValueError('Malformed authorization header')