summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIb Lundgren <ib.lundgren@gmail.com>2012-06-13 17:34:09 +0200
committerIb Lundgren <ib.lundgren@gmail.com>2012-06-13 17:34:09 +0200
commit1edaaf2f5e92744e4fd06c908e507a47279b3db9 (patch)
tree0ad2594b925f3abc64765496652f3ff7db3ed5e3
parent44ba3fd479aeb2eac28814054bb123a71204ed27 (diff)
downloadoauthlib-1edaaf2f5e92744e4fd06c908e507a47279b3db9.tar.gz
Move shared functionality (#30)
-rw-r--r--oauthlib/common.py58
-rw-r--r--oauthlib/oauth1/rfc5849/__init__.py7
-rw-r--r--oauthlib/oauth1/rfc5849/utils.py44
-rw-r--r--oauthlib/oauth2/draft25/tokens.py5
-rw-r--r--oauthlib/oauth2/draft25/utils.py113
-rw-r--r--tests/oauth1/rfc5849/test_utils.py29
-rw-r--r--tests/oauth2/draft25/test_utils.py33
-rw-r--r--tests/test_common.py25
8 files changed, 104 insertions, 210 deletions
diff --git a/oauthlib/common.py b/oauthlib/common.py
index 4cdfd0d..a2a30b3 100644
--- a/oauthlib/common.py
+++ b/oauthlib/common.py
@@ -9,10 +9,15 @@ This module provides data structures and utilities common
to all implementations of OAuth.
"""
+import random
import re
+import string
+import time
import urllib
import urlparse
+UNICODE_ASCII_CHARACTER_SET = (string.ascii_letters.decode('ascii') +
+ string.digits.decode('ascii'))
always_safe = (u'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
u'abcdefghijklmnopqrstuvwxyz'
@@ -123,6 +128,59 @@ def extract_params(raw):
return params
+def generate_nonce():
+ """Generate pseudorandom nonce that is unlikely to repeat.
+
+ Per `section 3.3`_ of the OAuth 1 RFC 5849 spec.
+ 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
+ .. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
+ """
+ return unicode(unicode(random.getrandbits(64)) + generate_timestamp())
+
+
+def generate_timestamp():
+ """Get seconds since epoch (UTC).
+
+ Per `section 3.3`_ of the OAuth 1 RFC 5849 spec.
+ 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
+ .. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
+ """
+ return unicode(int(time.time()))
+
+
+def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET):
+ """Generates a non-guessable OAuth token
+
+ OAuth (1 and 2) does not specify the format of tokens except that they
+ should be strings of random characters. Tokens should not be guessable
+ and entropy when generating the random characters is important. Which is
+ why SystemRandom is used instead of the default random.choice method.
+ """
+ rand = random.SystemRandom()
+ return u''.join(rand.choice(chars) for x in range(length))
+
+
+def add_params_to_qs(query, params):
+ """Extend a query with a list of two-tuples."""
+ queryparams = urlparse.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."""
+ sch, net, path, par, query, fra = urlparse.urlparse(uri)
+ query = add_params_to_qs(query, params)
+ return urlparse.urlunparse((sch, net, path, par, query, fra))
+
+
class Request(object):
"""A malleable representation of a signable HTTP request.
diff --git a/oauthlib/oauth1/rfc5849/__init__.py b/oauthlib/oauth1/rfc5849/__init__.py
index 03fb8b2..165a007 100644
--- a/oauthlib/oauth1/rfc5849/__init__.py
+++ b/oauthlib/oauth1/rfc5849/__init__.py
@@ -12,7 +12,8 @@ for signing and checking OAuth 1.0 RFC 5849 requests.
import logging
import urlparse
-from oauthlib.common import Request, urlencode
+from oauthlib.common import Request, urlencode, generate_nonce
+from oauthlib.common import generate_timestamp
from . import parameters, signature, utils
SIGNATURE_HMAC = u"HMAC-SHA1"
@@ -92,8 +93,8 @@ class Client(object):
"""Get the basic OAuth parameters to be used in generating a signature.
"""
params = [
- (u'oauth_nonce', utils.generate_nonce()),
- (u'oauth_timestamp', utils.generate_timestamp()),
+ (u'oauth_nonce', generate_nonce()),
+ (u'oauth_timestamp', generate_timestamp()),
(u'oauth_version', u'1.0'),
(u'oauth_signature_method', self.signature_method),
(u'oauth_consumer_key', self.client_key),
diff --git a/oauthlib/oauth1/rfc5849/utils.py b/oauthlib/oauth1/rfc5849/utils.py
index 6db446f..8fb0e77 100644
--- a/oauthlib/oauth1/rfc5849/utils.py
+++ b/oauthlib/oauth1/rfc5849/utils.py
@@ -9,9 +9,7 @@ spec.
"""
import string
-import time
import urllib2
-from random import getrandbits, choice
from oauthlib.common import quote, unquote
@@ -42,45 +40,6 @@ def filter_oauth_params(params):
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.
@@ -118,7 +77,7 @@ def parse_keqv_list(l):
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())
+ v.decode('utf-8')) for k, v in encoded_parsed.items())
def parse_http_list(u):
@@ -138,4 +97,3 @@ def parse_authorization_header(authorization_header):
return parse_keqv_list(items).items()
except ValueError:
raise ValueError('Malformed authorization header')
-
diff --git a/oauthlib/oauth2/draft25/tokens.py b/oauthlib/oauth2/draft25/tokens.py
index 9b5f586..74491fb 100644
--- a/oauthlib/oauth2/draft25/tokens.py
+++ b/oauthlib/oauth2/draft25/tokens.py
@@ -14,6 +14,7 @@ import hashlib
import hmac
from urlparse import urlparse
+from oauthlib.common import add_params_to_uri, add_params_to_qs
from . import utils
@@ -105,7 +106,7 @@ def prepare_bearer_uri(token, uri):
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
"""
- return utils.add_params_to_uri(uri, [((u'access_token', token))])
+ return add_params_to_uri(uri, [((u'access_token', token))])
def prepare_bearer_headers(token, headers=None):
@@ -128,4 +129,4 @@ def prepare_bearer_body(token, body=u''):
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
"""
- return utils.add_params_to_qs(body, [((u'access_token', token))])
+ return add_params_to_qs(body, [((u'access_token', token))])
diff --git a/oauthlib/oauth2/draft25/utils.py b/oauthlib/oauth2/draft25/utils.py
index 48b4ea1..75d5fcc 100644
--- a/oauthlib/oauth2/draft25/utils.py
+++ b/oauthlib/oauth2/draft25/utils.py
@@ -5,110 +5,21 @@ 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))
+import urlparse
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
+ Will use default port for HTTP and HTTPS if none is present in the URI.
"""
default_ports = {
- u'HTTP' : u'80',
- u'HTTPS' : u'443',
+ u'HTTP': u'80',
+ u'HTTPS': u'443',
}
- sch, netloc, path, par, query, fra = urlparse(uri)
+ sch, netloc, path, par, query, fra = urlparse.urlparse(uri)
if u':' in netloc:
netloc, port = netloc.split(u':', 1)
else:
@@ -117,12 +28,12 @@ def host_from_uri(uri):
return netloc, port
-def urlencode(query):
- """Encode a sequence of two-element tuples or dictionary into a URL query string.
+def escape(u):
+ """Escape a string in an OAuth-compatible fashion.
+
+ TODO: verify whether this can in fact be used for OAuth 2
- 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])
+ if not isinstance(u, unicode):
+ raise ValueError('Only unicode objects are escapable.')
+ return urllib.quote(u.encode('utf-8'), safe='~')
diff --git a/tests/oauth1/rfc5849/test_utils.py b/tests/oauth1/rfc5849/test_utils.py
index 8abdb38..54abf9d 100644
--- a/tests/oauth1/rfc5849/test_utils.py
+++ b/tests/oauth1/rfc5849/test_utils.py
@@ -90,31 +90,6 @@ class UtilsTests(TestCase):
self.assertTrue(filtered_params[0][0].startswith('oauth'))
self.assertTrue(filtered_params[1][0].startswith('oauth'))
- def test_generate_timestamp(self):
- """ TODO: Better test here """
- timestamp = generate_timestamp()
- self.assertIsInstance(timestamp, unicode)
- self.assertTrue(int(timestamp))
- self.assertGreater(int(timestamp), 1331672335) # is this increasing?
-
- def test_generate_nonce(self):
- """ TODO: better test here """
-
- nonce = generate_nonce()
- for i in range(50):
- self.assertNotEqual(nonce, generate_nonce())
-
- def test_generate_token(self):
- token = generate_token()
- self.assertEqual(len(token), 20)
-
- token = generate_token(length=44)
- self.assertEqual(len(token), 44)
-
- token = generate_token(length=6, chars="python")
- self.assertEqual(len(token), 6)
- self.assertNotIn("a", token)
-
def test_escape(self):
self.assertRaises(ValueError, escape, "I am a string type. Not a unicode type.")
self.assertEqual(escape(u"I am a unicode type."), u"I%20am%20a%20unicode%20type.")
@@ -125,10 +100,6 @@ class UtilsTests(TestCase):
self.assertEqual(unescape(u"I%20am%20a%20unicode%20type."), u'I am a unicode type.')
self.assertIsInstance(unescape(u"I%20am%20a%20unicode%20type."), unicode)
- def test_urlencode(self):
- self.assertEqual(urlencode(self.sample_params_unicode_list), "notoauth=shouldnotbehere&oauth_consumer_key=9djdj82h48djs9d2&oauth_token=kkk9d7dh3k39sjv7&notoautheither=shouldnotbehere")
- self.assertEqual(urlencode(self.sample_params_unicode_dict), "notoauth=shouldnotbehere&oauth_consumer_key=9djdj82h48djs9d2&oauth_token=kkk9d7dh3k39sjv7&notoautheither=shouldnotbehere")
-
def test_parse_authorization_header(self):
# make us some headers
authorization_headers = parse_authorization_header(self.authorization_header)
diff --git a/tests/oauth2/draft25/test_utils.py b/tests/oauth2/draft25/test_utils.py
index 3844392..c526891 100644
--- a/tests/oauth2/draft25/test_utils.py
+++ b/tests/oauth2/draft25/test_utils.py
@@ -1,8 +1,7 @@
from __future__ import absolute_import
from ...unittest import TestCase
-
-from oauthlib.oauth2.draft25.utils import *
+from oauthlib.oauth2.draft25.utils import escape, host_from_uri
class UtilsTests(TestCase):
@@ -12,31 +11,6 @@ class UtilsTests(TestCase):
self.assertRaises(ValueError, escape, "I am a string type. Not a unicode type.")
self.assertEqual(escape(u"I am a unicode type."), u"I%20am%20a%20unicode%20type.")
- def test_generate_timestamp(self):
- """ TODO: Better test here """
- timestamp = generate_timestamp()
- self.assertTrue(isinstance(timestamp, unicode))
- self.assertTrue(int(timestamp))
- self.assertTrue(int(timestamp) > 1331672335) # is this increasing?
-
- def test_generate_nonce(self):
- """ TODO: better test here """
- nonce = generate_nonce()
- for i in range(50):
- self.assertTrue(nonce != generate_nonce())
-
- def test_generate_token(self):
- """ TODO: better test here"""
- token = generate_token()
- self.assertEqual(len(token), 20)
-
- token = generate_token(length=44)
- self.assertEqual(len(token), 44)
-
- token = generate_token(length=6, chars="python")
- self.assertEqual(len(token), 6)
- self.assertTrue("a" not in token)
-
def test_host_from_uri(self):
"""Test if hosts and ports are properly extracted from URIs.
@@ -47,8 +21,3 @@ class UtilsTests(TestCase):
self.assertEqual(host_from_uri(u'https://a.b.com:8080'), (u'a.b.com', u'8080'))
self.assertEqual(host_from_uri(u'http://www.example.com'), (u'www.example.com', u'80'))
self.assertEqual(host_from_uri(u'https://www.example.com'), (u'www.example.com', u'443'))
-
- def test_urlencode(self):
- """Ensure query components encoded properly"""
- self.assertEqual(urlencode([(u'hello', u' world')]), u'hello=%20world')
- self.assertEqual(urlencode({u'hello': u' world'}), u'hello=%20world')
diff --git a/tests/test_common.py b/tests/test_common.py
index 457b96d..85ceedd 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -80,3 +80,28 @@ class CommonTests(TestCase):
def test_dict_body(self):
r = Request(self.uri, body=self.params_dict)
self.assertEqual(r.decoded_body, self.params_twotuple)
+
+ def test_generate_timestamp(self):
+ """ TODO: Better test here """
+ timestamp = generate_timestamp()
+ self.assertIsInstance(timestamp, unicode)
+ self.assertTrue(int(timestamp))
+ self.assertGreater(int(timestamp), 1331672335) # is this increasing?
+
+ def test_generate_nonce(self):
+ """ TODO: better test here """
+
+ nonce = generate_nonce()
+ for i in range(50):
+ self.assertNotEqual(nonce, generate_nonce())
+
+ def test_generate_token(self):
+ token = generate_token()
+ self.assertEqual(len(token), 30)
+
+ token = generate_token(length=44)
+ self.assertEqual(len(token), 44)
+
+ token = generate_token(length=6, chars="python")
+ self.assertEqual(len(token), 6)
+ self.assertNotIn("a", token)