summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Gouldin <david@gould.in>2012-05-12 15:20:14 -0700
committerDavid Gouldin <david@gould.in>2012-05-12 15:20:14 -0700
commitbbee30c35e5eb438567e693f6df00644ad3bc4fb (patch)
tree644edf917cf21123adc4cde9a864ee0b8bf47583
parenta56cf9c1721e81abb02b8578493cb85bd3307e35 (diff)
downloadoauthlib-use_request_objects.tar.gz
Continuing to refactor oauth1 to use Request objects internally.use_request_objects
-rw-r--r--oauthlib/common.py7
-rw-r--r--oauthlib/oauth1/rfc5849/__init__.py46
-rw-r--r--oauthlib/oauth1/rfc5849/parameters.py47
-rw-r--r--oauthlib/oauth1/rfc5849/signature.py13
-rw-r--r--tests/oauth1/rfc5849/test_parameters.py14
-rw-r--r--tests/oauth1/rfc5849/test_signatures.py32
6 files changed, 93 insertions, 66 deletions
diff --git a/oauthlib/common.py b/oauthlib/common.py
index 5ec354e..3d10bd8 100644
--- a/oauthlib/common.py
+++ b/oauthlib/common.py
@@ -152,8 +152,11 @@ class Request(object):
@property
def uri_query_params(self):
- return urlparse.parse_qsl(self.uri_query, keep_blank_values=True,
- strict_parsing=True)
+ if self.uri_query:
+ return urlparse.parse_qsl(self.uri_query, keep_blank_values=True,
+ strict_parsing=True)
+ else:
+ return []
@property
def urlencoded_body(self):
diff --git a/oauthlib/oauth1/rfc5849/__init__.py b/oauthlib/oauth1/rfc5849/__init__.py
index 7744a48..4fb4a49 100644
--- a/oauthlib/oauth1/rfc5849/__init__.py
+++ b/oauthlib/oauth1/rfc5849/__init__.py
@@ -96,13 +96,11 @@ class Client(object):
def _render(self, request, formencode=False):
"""Render a signed request according to signature type
- Returns a 3-tuple containing the request URI, headers, and body.
+ Returns a rendered Request instance.
If the formencode argument is True and the body contains parameters, it
is escaped and returned as a valid formencoded string.
"""
- # TODO what if there are body params on a header-type auth?
- # TODO what if there are query params on a body-type auth?
return parameters.prepare_request(request, self.signature_type)
def sign(self, uri, http_method=u'GET', body=None, headers=None):
@@ -190,29 +188,6 @@ class Server(object):
def get_resource_owner_secret(self, resource_owner_key):
raise NotImplementedError("Subclasses must implement this function.")
- def get_signature_type_and_params(self, uri_query, headers, body):
- signature_types_with_oauth_params = filter(lambda s: s[1], (
- (constants.SIGNATURE_TYPE_AUTH_HEADER, utils.filter_oauth_params(
- signature.collect_parameters(headers=headers,
- exclude_oauth_signature=False))),
- (constants.SIGNATURE_TYPE_BODY, utils.filter_oauth_params(
- signature.collect_parameters(body=body,
- exclude_oauth_signature=False))),
- (constants.SIGNATURE_TYPE_QUERY, utils.filter_oauth_params(
- signature.collect_parameters(uri_query=uri_query,
- exclude_oauth_signature=False))),
- ))
-
- if len(signature_types_with_oauth_params) > 1:
- raise ValueError('oauth_ params must come from only 1 signature type but were found in %s' % ', '.join(
- [s[0] for s in signature_types_with_oauth_params]))
- try:
- signature_type, params = signature_types_with_oauth_params[0]
- except IndexError:
- raise ValueError('oauth_ params are missing. Could not determine signature type.')
-
- return signature_type, dict(params)
-
def check_client_key(self, client_key):
raise NotImplementedError("Subclasses must implement this function.")
@@ -234,13 +209,11 @@ class Server(object):
.. _`section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2
"""
- headers = headers or {}
- signature_type = None
- # FIXME: urlparse does not return unicode!
- uri_query = urlparse.urlparse(uri).query
-
- signature_type, params = self.get_signature_type_and_params(uri_query,
- headers, body)
+ request = Request(uri, http_method=http_method, body=body,
+ headers=headers)
+ signature_type = parameters.get_signature_type(request)
+ params = signature.collect_parameters(request,
+ exclude_oauth_signature=False)
# the parameters may not include duplicate oauth entries
filtered_params = utils.filter_oauth_params(params)
@@ -307,10 +280,11 @@ class Server(object):
signature_type=signature_type,
verifier=verifier)
- request = Request(uri, http_method, body, headers)
- request.oauth_params = params
+ new_request = request.clone()
+ new_request.oauth_params = params.items()
- client_signature = oauth_client.get_oauth_signature(request)
+ client_signature = oauth_client.get_oauth_signature(new_request)
# FIXME: use near constant time string compare to avoid timing attacks
return client_signature == request_signature
+
diff --git a/oauthlib/oauth1/rfc5849/parameters.py b/oauthlib/oauth1/rfc5849/parameters.py
index 315cccf..b0e6459 100644
--- a/oauthlib/oauth1/rfc5849/parameters.py
+++ b/oauthlib/oauth1/rfc5849/parameters.py
@@ -14,6 +14,53 @@ from urlparse import urlparse, urlunparse
from . import constants, utils
from oauthlib.common import extract_params, urlencode
+def get_signature_type(request):
+ """**Determine the signature type of a request**
+ Per `section 3.5`_ of the spec.
+
+ .. _`section 3.5`: http://tools.ietf.org/html/rfc5849#section-3.5
+ """
+ signature_types = []
+
+ # When making an OAuth-authenticated request, protocol parameters as
+ # well as any other parameter using the "oauth_" prefix SHALL be
+ # included in the request using one and only one of the following
+ # locations, listed in order of decreasing preference:
+
+ # 1. The HTTP "Authorization" header field as described in
+ # `Section 3.5.1`_.
+ #
+ # .. _`Section 3.5.1`: http://tools.ietf.org/html/rfc5849#section-3.5.1
+ if request.headers:
+ headers_lower = dict(
+ (k.lower(), v) for k, v in request.headers.items())
+ authorization_header = headers_lower.get(u'authorization')
+ if authorization_header is not None:
+ header_oauth_params = utils.filter_oauth_params(
+ utils.parse_authorization_header(authorization_header))
+ if header_oauth_params:
+ signature_types.append(constants.SIGNATURE_TYPE_AUTH_HEADER)
+
+ # 2. The HTTP request entity-body as described in `Section 3.5.2`_.
+ #
+ # .. _`Section 3.5.2`: http://tools.ietf.org/html/rfc5849#section-3.5.2
+ if utils.filter_oauth_params(extract_params(request.body) or []):
+ signature_types.append(constants.SIGNATURE_TYPE_BODY)
+
+ # 3. The HTTP request URI query as described in `Section 3.5.3`_.
+ #
+ # .. _`Section 3.5.3`: http://tools.ietf.org/html/rfc5849#section-3.5.3
+ if utils.filter_oauth_params(request.uri_query_params):
+ signature_types.append(constants.SIGNATURE_TYPE_QUERY)
+
+ if len(signature_types) > 1:
+ raise ValueError('oauth_ params must come from only 1 signature type but were found in %s' % ', '.join(
+ signature_types))
+ try:
+ return signature_types[0]
+ except IndexError:
+ raise ValueError('oauth_ params are missing. Could not determine signature type.')
+
def prepare_headers(request, realm=None):
"""**Prepare the Authorization header.**
diff --git a/oauthlib/oauth1/rfc5849/signature.py b/oauthlib/oauth1/rfc5849/signature.py
index 99101d4..edd553b 100644
--- a/oauthlib/oauth1/rfc5849/signature.py
+++ b/oauthlib/oauth1/rfc5849/signature.py
@@ -167,8 +167,7 @@ def normalize_base_string_uri(uri):
#
# .. _`section 3.4.1.3`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3
-def collect_parameters(uri_query='', body=[], headers=None,
- exclude_oauth_signature=True):
+def collect_parameters(request, exclude_oauth_signature=True):
"""**Parameter Sources**
Parameters starting with `oauth_` will be unescaped.
@@ -226,7 +225,7 @@ def collect_parameters(uri_query='', body=[], headers=None,
.. _`section 3.4.1.3.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
"""
- headers = headers or {}
+ headers = request.headers or {}
params = []
# The parameters from the following sources are collected into a single
@@ -240,9 +239,9 @@ def collect_parameters(uri_query='', body=[], headers=None,
# `W3C.REC-html40-19980424`_, Section 17.13.4.
#
# .. _`RFC3986, Section 3.4`: http://tools.ietf.org/html/rfc3986#section-3.4
- # .. _`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424
- if uri_query:
- params.extend(urlparse.parse_qsl(uri_query, keep_blank_values=True))
+ # .. _`W3C.REC-html40-19i980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424
+ if request.uri_query:
+ params.extend(urlparse.parse_qsl(request.uri_query, keep_blank_values=True))
# * The OAuth HTTP "Authorization" header field (`Section 3.5.1`_) if
# present. The header's content is parsed into a list of name/value
@@ -271,7 +270,7 @@ def collect_parameters(uri_query='', body=[], headers=None,
# .._`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424
# TODO: enforce header param inclusion conditions
- bodyparams = extract_params(body) or []
+ bodyparams = extract_params(request.body) or []
params.extend(bodyparams)
# ensure all oauth params are unescaped
diff --git a/tests/oauth1/rfc5849/test_parameters.py b/tests/oauth1/rfc5849/test_parameters.py
index 9765cab..4388123 100644
--- a/tests/oauth1/rfc5849/test_parameters.py
+++ b/tests/oauth1/rfc5849/test_parameters.py
@@ -7,6 +7,10 @@ from ...unittest import TestCase
class ParameterTests(TestCase):
+ data_params = [
+ (u'data_param_foo', u'foo'),
+ (u'data_param_1', u'1'),
+ ]
auth_only_params = [
(u'oauth_consumer_key', u"9djdj82h48djs9d2"),
(u'oauth_token', u"kkk9d7dh3k39sjv7"),
@@ -15,9 +19,7 @@ class ParameterTests(TestCase):
(u'oauth_nonce', u"7d8f3e4a"),
(u'oauth_signature', u"bYT5CMsGcbgUdFHObYMEfcx6bsw=")
]
- auth_and_data = list(auth_only_params)
- auth_and_data.append((u'data_param_foo', u'foo'))
- auth_and_data.append((u'data_param_1', u'1'))
+ auth_and_data = auth_only_params + data_params
realm = u'testrealm'
norealm_authorization_header = u' '.join((
u'OAuth',
@@ -72,11 +74,11 @@ class ParameterTests(TestCase):
{u'Authorization': self.withrealm_authorization_header})
def test_prepare_form_encoded_body(self):
- request = Request(u'http://www.google.com/')
+ request = Request(u'http://www.google.com/', body=self.data_params)
request.oauth_params = self.auth_only_params
- form_encoded_body = 'data_param_foo=foo&data_param_1=1&oauth_consumer_key=9djdj82h48djs9d2&oauth_token=kkk9d7dh3k39sjv7&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131201&oauth_nonce=7d8f3e4a&oauth_signature=bYT5CMsGcbgUdFHObYMEfcx6bsw%3D'
+ form_encoded_body = u'data_param_foo=foo&data_param_1=1&oauth_consumer_key=9djdj82h48djs9d2&oauth_token=kkk9d7dh3k39sjv7&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131201&oauth_nonce=7d8f3e4a&oauth_signature=bYT5CMsGcbgUdFHObYMEfcx6bsw%3D'
self.assertEqual(
- urlencode(prepare_form_encoded_body(self.auth_and_data, existing_body)),
+ urlencode(prepare_form_encoded_body(request).body),
form_encoded_body)
def test_prepare_request_uri_query(self):
diff --git a/tests/oauth1/rfc5849/test_signatures.py b/tests/oauth1/rfc5849/test_signatures.py
index fc81ea7..8fa237b 100644
--- a/tests/oauth1/rfc5849/test_signatures.py
+++ b/tests/oauth1/rfc5849/test_signatures.py
@@ -3,6 +3,7 @@ from __future__ import absolute_import
import urllib
from oauthlib.oauth1.rfc5849.signature import *
+from oauthlib.common import Request
from ...unittest import TestCase
@@ -85,16 +86,14 @@ class SignatureTests(TestCase):
""" We check against parameters multiple times in case things change after more
parameters are added.
"""
- # check against empty parameters
- # check against empty parameters
- # check against empty parameters
- self.assertEquals(collect_parameters(), [])
+
+ uri = u'http://example.com/request?%s' % self.uri_query
# Check against uri_query
# Check against uri_query
# Check against uri_query
-
- parameters = collect_parameters(uri_query=self.uri_query)
+ request = Request(uri)
+ parameters = collect_parameters(request)
self.assertEquals(len(parameters), 6)
self.assertEquals(parameters[0], ('b5', '=%3D'))
@@ -108,9 +107,10 @@ class SignatureTests(TestCase):
# check against authorization header as well
# check against authorization header as well
- parameters = collect_parameters(uri_query=self.uri_query, headers={
+ request = Request(uri, headers={
'Authorization': self.authorization_header,
})
+ parameters = collect_parameters(request)
# Redo the checks against all the parameters. Duplicated code but better safety
self.assertEquals(len(parameters), 11)
@@ -129,10 +129,10 @@ class SignatureTests(TestCase):
# Add in the body.
# TODO - add more valid content for the body. Daniel Greenfeld 2012/03/12
# Redo again the checks against all the parameters. Duplicated code but better safety
- parameters = collect_parameters(uri_query=self.uri_query,
- body=self.body, headers={
- 'Authorization': self.authorization_header,
- })
+ request = Request(uri, body=self.body, headers={
+ 'Authorization': self.authorization_header,
+ })
+ parameters = collect_parameters(request)
self.assertEquals(len(parameters), 12)
self.assertEquals(parameters[0], ('b5', '=%3D'))
self.assertEquals(parameters[1], ('a3', 'a'))
@@ -150,11 +150,13 @@ class SignatureTests(TestCase):
def test_normalize_parameters(self):
""" We copy some of the variables from the test method above."""
+ uri = u'http://example.com/request?%s' % self.uri_query
+
# Create the parameters
- parameters = collect_parameters(uri_query=unicode(self.uri_query),
- body=unicode(self.body), headers={
- u'Authorization': unicode(self.authorization_header),
- })
+ request = Request(uri, body=unicode(self.body), headers={
+ u'Authorization': unicode(self.authorization_header),
+ })
+ parameters = collect_parameters(request)
normalized = normalize_parameters(parameters)
# check the parameters type