From 8caae4b7b70e9c44121643683ac1595c202125fc Mon Sep 17 00:00:00 2001 From: Daniel Haas Date: Wed, 6 Jun 2012 15:30:52 +0000 Subject: [PATCH 1/5] Always sign requests in the headers, not in the body or querystring. --- oauth2/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 835270e3..90fefed6 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -402,7 +402,7 @@ def to_header(self, realm=''): def to_postdata(self): """Serialize as post data for a POST request.""" d = {} - for k, v in self.iteritems(): + for k, v in self.get_nonoauth_parameters().iteritems(): d[k.encode('utf-8')] = to_utf8_optional_iterator(v) # tell urlencode to deal with sequence values and map them correctly @@ -419,7 +419,7 @@ def to_url(self): # must be python <2.5 query = base_url[4] query = parse_qs(query) - for k, v in self.items(): + for k, v in self.get_nonoauth_parameters().iteritems(): query.setdefault(k, []).append(v) try: @@ -670,12 +670,14 @@ def request(self, uri, method="GET", body='', headers=None, realm = schema + ':' + hierpart + host + # Get request data, as the body or as the querystring if is_form_encoded: body = req.to_postdata() elif method == "GET": uri = req.to_url() - else: - headers.update(req.to_header(realm=realm)) + + # Always sign the headers + headers.update(req.to_header(realm=realm)) return httplib2.Http.request(self, uri, method=method, body=body, headers=headers, redirections=redirections, From 654858cf352e07e63ababbd44d23696ef0865012 Mon Sep 17 00:00:00 2001 From: Daniel Haas Date: Fri, 8 Jun 2012 21:05:38 +0000 Subject: [PATCH 2/5] Normalized parameters for sbs still need to include form-encoded body data. --- oauth2/__init__.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 90fefed6..779b0876 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -32,7 +32,7 @@ import httplib2 try: - from urlparse import parse_qs + from urlparse import parse_qs, parse_qsl parse_qs # placate pyflakes except ImportError: # fall back for Python 2.5 @@ -450,7 +450,18 @@ def get_parameter(self, parameter): def get_normalized_parameters(self): """Return a string that contains the parameters that must be signed.""" items = [] - for key, value in self.iteritems(): + + params = self.items() + # Include any query string parameters from the provided URL + query = urlparse.urlparse(self.url)[4] + url_items = self._split_url_string(query).items() + params.extend(url_items) + # Include any form-encoded parameters from the body + if self.is_form_encoded: + params.extend(parse_qsl(self.body)) + + for p in params: + key, value = p if key == 'oauth_signature': continue # 1.0a/9.1.1 states that kvp must be sorted by key, then by value, @@ -466,13 +477,6 @@ def get_normalized_parameters(self): else: items.extend((to_utf8_if_string(key), to_utf8_if_string(item)) for item in value) - # 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) - items.sort() encoded_str = urllib.urlencode(items) # Encode signature parameters per Oauth Core 1.0 protocol @@ -650,14 +654,9 @@ def request(self, uri, method="GET", body='', headers=None, is_form_encoded = \ headers.get('Content-Type') == 'application/x-www-form-urlencoded' - if is_form_encoded and body: - parameters = parse_qs(body) - else: - parameters = None - req = Request.from_consumer_and_token(self.consumer, token=self.token, http_method=method, http_url=uri, - parameters=parameters, body=body, is_form_encoded=is_form_encoded) + parameters=None, body=body, is_form_encoded=is_form_encoded) req.sign_request(self.method, self.consumer, self.token) @@ -670,10 +669,8 @@ def request(self, uri, method="GET", body='', headers=None, realm = schema + ':' + hierpart + host - # Get request data, as the body or as the querystring - if is_form_encoded: - body = req.to_postdata() - elif method == "GET": + # Get querystring request data + if method == "GET": uri = req.to_url() # Always sign the headers From 9014d97798280ee4355a4734f45c9443f95c98a4 Mon Sep 17 00:00:00 2001 From: Travers Franckle Date: Thu, 14 Jun 2012 11:35:41 -0400 Subject: [PATCH 3/5] Bugfix: keeping blank values in form-encoded body parameters, as per the oauth spec --- oauth2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 779b0876..0be674a9 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -458,7 +458,7 @@ def get_normalized_parameters(self): params.extend(url_items) # Include any form-encoded parameters from the body if self.is_form_encoded: - params.extend(parse_qsl(self.body)) + params.extend(parse_qsl(self.body, keep_blank_values=True)) for p in params: key, value = p From b75b15108f1e7fdb99951e7bae795aa83d96eb24 Mon Sep 17 00:00:00 2001 From: Travers Franckle Date: Thu, 14 Jun 2012 11:39:55 -0400 Subject: [PATCH 4/5] Add support for mod_wsgi style authorization header --- oauth2/__init__.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 0be674a9..e3f8c83b 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -523,10 +523,14 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None, parameters = {} # Headers - if headers and 'Authorization' in headers: - auth_header = headers['Authorization'] + if headers: + auth_header = None + if 'Authorization' in headers: + auth_header = headers['Authorization'] + elif 'HTTP_AUTHORIZATION' in headers: + auth_header = headers['HTTP_AUTHORIZATION'] # Check that the authorization header is OAuth. - if auth_header[:6] == 'OAuth ': + if auth_header and auth_header[:6] == 'OAuth ': auth_header = auth_header[6:] try: # Get the parameters from the header. @@ -534,9 +538,9 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None, parameters.update(header_params) except: raise Error('Unable to parse OAuth parameters from ' - 'Authorization header.') - - # GET or POST query string. + 'Authorization header.') + + # GET or POST query string. if query_string: query_params = cls._split_url_string(query_string) parameters.update(query_params) From 0fb769f8c4187c7c5e2380e09191891fc2d7ad32 Mon Sep 17 00:00:00 2001 From: Travers Franckle Date: Thu, 14 Jun 2012 11:40:58 -0400 Subject: [PATCH 5/5] don't double de-encode params --- oauth2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index e3f8c83b..228053da 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -616,7 +616,7 @@ 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]) + parameters[k] = v[0] return parameters