From d2eac6aa038a8919f52ab3700a0005266a8ba06b Mon Sep 17 00:00:00 2001 From: Guillaume Duhamel Date: Wed, 13 Apr 2011 13:27:21 +0800 Subject: [PATCH 1/4] Encode parse_qs input in utf-8 so we make sure the output will be utf-8 too and we don't end up mixing utf-8 characters (from %xx translation) into a non utf-8 sequence. --- oauth2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 835270e3..87210f7c 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -418,7 +418,7 @@ def to_url(self): except AttributeError: # must be python <2.5 query = base_url[4] - query = parse_qs(query) + query = parse_qs(query.encode('utf-8')) for k, v in self.items(): query.setdefault(k, []).append(v) From 8b214bf933ae827338cf525bce9839c5b19f9464 Mon Sep 17 00:00:00 2001 From: Vladimir Prudnikov Date: Mon, 22 Oct 2012 00:55:07 +0200 Subject: [PATCH 2/4] FIX #121: Incorrect signature when multiple values for a single key provided. --- oauth2/__init__.py | 38 ++++++++++++++++++++++++++++++++------ tests/test_oauth.py | 30 ++++++++++++++++++------------ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 87210f7c..fce03f8d 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -420,8 +420,18 @@ def to_url(self): query = base_url[4] query = parse_qs(query.encode('utf-8')) for k, v in self.items(): - query.setdefault(k, []).append(v) - + + if isinstance(v, basestring): + query.setdefault(to_utf8_if_string(k), []).append(to_utf8(v)) + else: + try: + v = list(v) + except TypeError, e: + assert 'is not iterable' in str(e) + query.setdefault(to_utf8_if_string(k), []).append(to_utf8_if_string(v)) + else: + query.setdefault(to_utf8_if_string(k), []).extend(to_utf8_if_string(item) for item in v) + try: scheme = base_url.scheme netloc = base_url.netloc @@ -469,10 +479,25 @@ def get_normalized_parameters(self): # Include any query string parameters from the provided URL query = urlparse.urlparse(self.url)[4] - url_items = self._split_url_string(query).items() - url_items = [(to_utf8(k), to_utf8(v)) for k, v in url_items if k != 'oauth_signature' ] - items.extend(url_items) + _url_items = self._split_url_string(query).items() + url_items = [] + for url_item_key,url_item_value in _url_items: + if url_item_key == "oauth_signature": + continue + + if isinstance(url_item_value, basestring): + url_items.append((to_utf8(url_item_key), to_utf8(url_item_value))) + else: + try: + url_item_value = list(url_item_value) + except TypeError, e: + assert 'is not iterable' in str(e) + url_items.append((to_utf8(url_item_key), to_utf8_if_string(url_item_value))) + else: + url_items.extend((to_utf8(url_item_key), to_utf8_if_string(item)) for item in url_item_value) + + items.extend(url_items) items.sort() encoded_str = urllib.urlencode(items) # Encode signature parameters per Oauth Core 1.0 protocol @@ -608,7 +633,8 @@ def _split_url_string(param_str): """Turn URL string into parameters.""" parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True) for k, v in parameters.iteritems(): - parameters[k] = urllib.unquote(v[0]) + v = map(urllib.unquote, v) + parameters[to_utf8(k)] = to_utf8_optional_iterator(v) return parameters diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 099e5794..b634633b 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -337,7 +337,7 @@ def test_no_url_set(self): pass def test_url_query(self): - url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" + url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10&a=1&a=2" normalized_url = urlparse.urlunparse(urlparse.urlparse(url)[:3] + (None, None, None)) method = "GET" @@ -459,10 +459,14 @@ def test_to_url(self): 'oauth_signature_method': "HMAC-SHA1", 'oauth_token': "ad180jjd733klru7", 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + 'multi': ['FOO','BAR'], } + req = oauth.Request("GET", url, parameters=params) - req = oauth.Request("GET", url, params) - exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(params))) + flat = [('multi','FOO'),('multi','BAR')] + del params['multi'] + flat.extend(params.items()) + exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(flat))) res = urlparse.urlparse(req.to_url()) self.assertEquals(exp.scheme, res.scheme) self.assertEquals(exp.netloc, res.netloc) @@ -471,7 +475,8 @@ def test_to_url(self): a = parse_qs(exp.query) b = parse_qs(res.query) self.assertEquals(a, b) - + + def test_to_url_with_query(self): url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" @@ -802,7 +807,8 @@ def test_sign_request(self): params = { 'oauth_version': "1.0", 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200" + 'oauth_timestamp': "137131200", + 'multi':['foo', 'bar'], } tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") @@ -813,9 +819,9 @@ def test_sign_request(self): req = oauth.Request(method="GET", url=url, parameters=params) methods = { - 'DX01TdHws7OninCLK9VztNTH1M4=': oauth.SignatureMethod_HMAC_SHA1(), + 'wP01jr2KUdSJ8Kbylra2Q4qwINk=': oauth.SignatureMethod_HMAC_SHA1(), 'con-test-secret&tok-test-secret': oauth.SignatureMethod_PLAINTEXT() - } + } for exp, method in methods.items(): req.sign_request(method, con, tok) @@ -826,23 +832,23 @@ def test_sign_request(self): url = "http://sp.example.com/\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + self.assertEquals(req['oauth_signature'], 'vJK7BqHR2QDF7v4o+WwFvk/NVcg=') url = u'http://sp.example.com/\u2019' # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + self.assertEquals(req['oauth_signature'], 'vJK7BqHR2QDF7v4o+WwFvk/NVcg=') # Also if there are non-ascii chars in the query args. url = "http://sp.example.com/?q=\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + self.assertEquals(req['oauth_signature'], 'cYkoCVyghnoMcSs1Z1+Mdi2LpMI=') url = u'http://sp.example.com/?q=\u2019' # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + self.assertEquals(req['oauth_signature'], 'cYkoCVyghnoMcSs1Z1+Mdi2LpMI=') def test_from_request(self): url = "http://sp.example.com/" @@ -881,7 +887,7 @@ def test_from_request(self): exp = parse_qs(qs, keep_blank_values=False) for k, v in exp.iteritems(): - exp[k] = urllib.unquote(v[0]) + exp[k] = map(urllib.unquote, v) self.assertEquals(exp, req.copy()) From 59a8fa2239520defc7731a050680b080e27659dc Mon Sep 17 00:00:00 2001 From: Vladimir Prudnikov Date: Mon, 22 Oct 2012 14:57:57 +0200 Subject: [PATCH 3/4] Update version to 1.5.212 --- oauth2/_version.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2/_version.py b/oauth2/_version.py index 0d74e056..1968f88d 100644 --- a/oauth2/_version.py +++ b/oauth2/_version.py @@ -4,7 +4,7 @@ -auto_build_num = "170" +auto_build_num = "212" diff --git a/setup.py b/setup.py index acc41e17..a61ffe0b 100755 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ description="library for OAuth version 1.0", author="Joe Stump", author_email="joe@simplegeo.com", - url="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/simplegeo/python-oauth2", + url="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/prudnikov/python-oauth2", packages = find_packages(), install_requires = ['httplib2'], license = "MIT License", From 92fde70626f624937a15de0c1acfe61b2d599770 Mon Sep 17 00:00:00 2001 From: Vladimir Prudnikov Date: Mon, 22 Oct 2012 22:19:49 +0200 Subject: [PATCH 4/4] Use string instead of lists for oauth_* variables. --- oauth2/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index fce03f8d..596d56cd 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -348,6 +348,8 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None, for k, v in parameters.iteritems(): k = to_unicode(k) v = to_unicode_optional_iterator(v) + if k.startswith("oauth_") and type(v) in [list, tuple]: + v = v[0] self[k] = v self.body = body self.is_form_encoded = is_form_encoded