diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 835270e3..c5d22077 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -500,6 +500,13 @@ def sign_request(self, signature_method, consumer, token): self['oauth_signature_method'] = signature_method.name self['oauth_signature'] = signature_method.sign(self, consumer, token) + + def verify_body(self): + """Compare the body hash to the one from actual body.""" + try: + return self['oauth_body_hash'] == base64.b64encode(sha(self.body).digest()) + except KeyError: + raise Error('oauth_body_hash expected, none found in request') @classmethod def make_timestamp(cls): @@ -513,7 +520,7 @@ def make_nonce(cls): @classmethod def from_request(cls, http_method, http_url, headers=None, parameters=None, - query_string=None): + query_string=None, body=''): """Combines multiple parameter sources.""" if parameters is None: parameters = {} @@ -531,6 +538,10 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None, except: raise Error('Unable to parse OAuth parameters from ' 'Authorization header.') + is_form_encoded = False + if headers and 'Content-Type' in headers: + is_form_encoded = \ + headers.get('Content-Type') == 'application/x-www-form-urlencoded' # GET or POST query string. if query_string: @@ -543,7 +554,8 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None, parameters.update(url_params) if parameters: - return cls(http_method, http_url, parameters) + return cls(http_method, http_url, parameters, body=body, + is_form_encoded=is_form_encoded) return None @@ -695,8 +707,9 @@ class Server(object): version = OAUTH_VERSION signature_methods = None - def __init__(self, signature_methods=None): + def __init__(self, signature_methods=None, body_hashing=False): self.signature_methods = signature_methods or {} + self.body_hashing = body_hashing def add_signature_method(self, signature_method): self.signature_methods[signature_method.name] = signature_method @@ -751,6 +764,11 @@ def _get_verifier(self, request): def _check_signature(self, request, consumer, token): timestamp, nonce = request._get_timestamp_nonce() self._check_timestamp(timestamp) + + if self.body_hashing and not request.is_form_encoded and \ + not request.verify_body(): + raise Error('Invalid oauth_body_hash.') + signature_method = self._get_signature_method(request) try: diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 099e5794..883e2916 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -995,6 +995,20 @@ def test_verify_request(self): self.assertEquals(parameters['foo'], 59) self.assertEquals(parameters['multi'], ['FOO','BAR']) + def test_verify_body_hashed_request(self): + server = oauth.Server(body_hashing=True) + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + + parameters = server.verify_request(self.request, self.consumer, + self.token) + + self.assertTrue('bar' in parameters) + self.assertTrue('foo' in parameters) + self.assertTrue('multi' in parameters) + self.assertEquals(parameters['bar'], 'blerg') + self.assertEquals(parameters['foo'], 59) + self.assertEquals(parameters['multi'], ['FOO','BAR']) + def test_build_authenticate_header(self): server = oauth.Server() headers = server.build_authenticate_header('example.com')