From 28af4266c160be4f38cd78b54fb8d857fcb1edf5 Mon Sep 17 00:00:00 2001 From: rickhanlonii Date: Fri, 26 Apr 2013 20:23:00 -0300 Subject: [PATCH 1/2] Specification improvement and bug fix to sign_request Per the OAuth Body Hash specifications (3.2)[1], if there is no body entity, then the hash should be done over the empty string. Also, when httplib2 handles a redirect, the follow is performed with None as the request body. This causes a TypeError to be thrown when attempting to hash the body here. This commit fixes the bug while improving the specification alignment. [1]http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html --- oauth2/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 835270e3..efc55adf 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -485,8 +485,14 @@ def sign_request(self, signature_method, consumer, token): """Set the signature parameter to the result of sign.""" if not self.is_form_encoded: - # according to + # according to # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html + # section 3.2 "If the request does not have an entity body, the hash should + # be taken over the empty string." + if self.body is None: + self.body = "" + + # according to ibid # section 4.1.1 "OAuth Consumers MUST NOT include an # oauth_body_hash parameter on requests with form-encoded # request bodies." From b5d35d58c0a0fe9eb9f3e0e8af63a72dcf0a2393 Mon Sep 17 00:00:00 2001 From: Rick Hanlon II Date: Wed, 29 Jul 2015 00:08:43 -0400 Subject: [PATCH 2/2] Add test for hashing NoneType body --- tests/test_oauth.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 099e5794..dd654ef4 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -1289,6 +1289,38 @@ def mockrequest(cl, ur, **kw): client.request(uri, 'GET') + @mock.patch('httplib2.Http.request') + def test_url_with_query_string_body_none(self, mockHttpRequest): + uri = 'http://example.com/foo/bar/?show=thundercats&character=snarf' + client = oauth.Client(self.consumer, None) + random_result = random.randint(1,100) + + def mockrequest(cl, ur, **kw): + self.failUnless(cl is client) + self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) + self.failUnlessEqual(kw['body'], None) + self.failUnlessEqual(kw['connection_type'], None) + self.failUnlessEqual(kw['method'], 'GET') + self.failUnlessEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) + self.failUnless(isinstance(kw['headers'], dict)) + + req = oauth.Request.from_consumer_and_token(self.consumer, None, + http_method='GET', http_url=uri, parameters={}) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, None) + expected = parse_qsl(urlparse.urlparse(req.to_url()).query) + actual = parse_qsl(urlparse.urlparse(ur).query) + self.failUnlessEqual(len(expected), len(actual)) + actual = dict(actual) + for key, value in expected: + if key not in ('oauth_signature', 'oauth_nonce', 'oauth_timestamp'): + self.failUnlessEqual(actual[key], value) + + return random_result + + mockHttpRequest.side_effect = mockrequest + + client.request(uri, 'GET', body=None) + @mock.patch('httplib2.Http.request') @mock.patch('oauth2.Request.from_consumer_and_token') def test_multiple_values_for_a_key(self, mockReqConstructor, mockHttpRequest):