diff options
author | Idan Gazit <idan@gazit.me> | 2012-03-12 17:23:18 -0700 |
---|---|---|
committer | Idan Gazit <idan@gazit.me> | 2012-03-12 17:23:18 -0700 |
commit | a4a954c9fc0970c90ed4dbacdb2a6dd5200e2543 (patch) | |
tree | f4f968e1249ad16d31f0ef0af746d1703b06af30 /oauthlib | |
parent | 9b264d1efde3800bcc0258f805ab7affd081ffcb (diff) | |
parent | 31710fe8b16ff3b19ccc770a635ed7994f00562d (diff) | |
download | oauthlib-a4a954c9fc0970c90ed4dbacdb2a6dd5200e2543.tar.gz |
Merge branch 'master' of github.com:idangazit/oauthlib
Diffstat (limited to 'oauthlib')
-rw-r--r-- | oauthlib/oauth.py | 229 | ||||
-rw-r--r-- | oauthlib/signature.py | 13 | ||||
-rw-r--r-- | oauthlib/utils.py | 12 |
3 files changed, 113 insertions, 141 deletions
diff --git a/oauthlib/oauth.py b/oauthlib/oauth.py index bf71546..1ff1955 100644 --- a/oauthlib/oauth.py +++ b/oauthlib/oauth.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import """ oauthlib.oauth @@ -8,141 +9,105 @@ This module is a generic implementation of various logic needed for signing OAuth requests. """ -import time -import hashlib -from random import getrandbits -import urllib -import hmac -import binascii -from urlparse import urlparse +import urlparse + +from . import parameters, signature, utils SIGNATURE_HMAC = "HMAC-SHA1" SIGNATURE_RSA = "RSA-SHA1" SIGNATURE_PLAINTEXT = "PLAINTEXT" +SIGNATURE_TYPE_AUTH_HEADER = 'AUTH_HEADER' +SIGNATURE_TYPE_QUERY = 'QUERY' + +class OAuthClient(object): + """An OAuth client used to sign OAuth requests""" + def __init__(self, client_key, client_secret, + resource_owner_key, resource_owner_secret, + signature_method=SIGNATURE_HMAC, + signature_type=SIGNATURE_TYPE_AUTH_HEADER, + callback_uri=None, rsa_key=None, verifier=None): + self.client_key = client_key + self.client_secret = client_secret + self.resource_owner_key = resource_owner_key + self.resource_owner_secret = resource_owner_secret + self.signature_method = signature_method + self.signature_type = signature_type + self.callback_uri = callback_uri + self.rsa_key = rsa_key + self.verifier = verifier + + def get_oauth_signature(self, uri, http_method=u'GET', body=None, + authorization_header=None): + """Get an OAuth signature to be used in signing a request""" + if self.signature_method == SIGNATURE_PLAINTEXT: + # fast-path + return signature.sign_plaintext(self.client_secret, + self.resource_owner_secret) + + query = urlparse.urlparse(uri).query + params = signature.collect_parameters(uri_query=query, + authorization_header=authorization_header, body=body) + normalized_params = signature.normalize_parameters(params) + normalized_uri = signature.normalize_base_string_uri(uri) + base_string = signature.construct_base_string(http_method, + normalized_uri, normalized_params) + if self.signature_method == SIGNATURE_HMAC: + return signature.sign_hmac_sha1(base_string, self.client_secret, + self.resource_owner_secret) + elif self.signature_method == SIGNATURE_RSA: + return signature.sign_rsa_sha1(base_string, self.rsa_key) + else: + return signature.sign_plaintext(self.client_secret, + self.resource_owner_secret) + + def get_oauth_params(self): + """Get the basic OAuth parameters to be used in generating a signature. + """ + params = [ + (u'oauth_nonce', utils.generate_nonce()), + (u'oauth_timestamp', utils.generate_timestamp()), + (u'oauth_version', '1.0'), + (u'oauth_signature_method', self.signature_method), + (u'oauth_consumer_key', self.client_key), + (u'oauth_token', self.resource_owner_key), + ] + if self.callback_uri: + params.append((u'oauth_callback', self.callback_uri)) + if self.verifier: + params.append((u'oauth_verifier', self.verifier)) + + return params + + def _contribute_parameters(self, uri, params): + if self.signature_type == SIGNATURE_TYPE_AUTH_HEADER: + authorization_header = parameters.prepare_authorization_header( + params) + complete_uri = uri + else: + authorization_header = None + complete_uri = paramaters.prepare_request_uri_query(params, uri) + + return complete_uri, authorization_header + + def sign_request(self, uri, http_method=u'GET', body=None): + """Get the signed uri and authorization header. + Authorization header will be None if signature type is "query". + """ + # get the OAuth params and contribute them to either the uri or + # authorization header + params = self.get_oauth_params() + complete_uri, authorization_header = self._contribute_parameters(uri, + params) + + # use the new uri and authorization header to generate a signature and + # contribute that signature to the OAuth parameters + oauth_signature = self.get_oauth_signature(complete_uri, + http_method=http_method, body=body, + authorization_header=authorization_header) + params.append((u'oauth_signature', oauth_signature)) + + # take the new OAuth params with signature and contribute the + # now-complete parameters to the uri or authorization header + return self._contribute_parameters(uri, params) - - -def generate_params(client_key, access_token, signature_method): - """Generates the requisite parameters for a valid OAuth request.""" - params = { - 'oauth_consumer_key': client_key, - 'oauth_nonce': generate_nonce(), - 'oauth_signature_method': signature_method, - 'oauth_token': access_token, - 'oauth_timestamp': generate_timestamp(), - 'oauth_version': '1.0', - } - return params - - -def normalize_http_method(method): - """Uppercases the HTTP method. - - Per `section 3.4.1.1` of the spec. - - .. _`section 3.4.1.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.1 - - """ - return method.upper() - - -def normalize_base_string_uri(uri): - """Prepares the base string URI. - - Parses the URL and rebuilds it to be scheme://host/path. The normalized - return value is already escaped. - - Per `section 3.4.1.2` of the spec. - - .. _`section 3.4.1.2`: http://tools.ietf.org/html/rfc5849#section-3.4.1.2 - - """ - parts = urlparse(uri) - scheme, netloc, path = parts[:3] - # Exclude default port numbers. - if scheme == 'http' and netloc[-3:] == ':80': - netloc = netloc[:-3] - elif scheme == 'https' and netloc[-4:] == ':443': - netloc = netloc[:-4] - return escape('{0}://{1}{2}'.format(scheme, netloc, path)) - - -def normalize_parameters(params): - """Prepares the request parameters. - - Per `section 3.4.1.3` of the spec. - - .. _`section 3.4.1.3`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3 - - """ - try: - # Exclude the signature if it exists. - del params['oauth_signature'] - except: - pass - - # Escape key values before sorting. - key_values = [] - for k, v in params.items(): - key_values.append((escape(utf8_str(k)), escape(utf8_str(v)))) - - # Sort lexicographically, first after key, then after value. - key_values.sort() - - # Combine key value pairs into a string and return. - return escape('&'.join(['{0}={1}'.format(escape(k), escape(v)) for k, v in key_values])) - - -def prepare_hmac_key(client_secret, access_secret=None): - """Prepares the signing key for HMAC-SHA1. - - Per `section 3.4.2`_ of the spec. - - .. _`section 3.4.2`: http://tools.ietf.org/html/rfc5849#section-3.4.2 - - """ - return '{0}&{1}'.format( - escape(client_secret), - escape(access_secret or '')) - - -def prepare_base_string(method, uri, params): - """Prepare a signature base string. - - Per `section 3.4.1`_ of the spec. - - .. _`section 3.4.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1 - - """ - return "{method}&{uri}&{params}".format( - method=normalize_http_method(method), - uri=normalize_base_string_uri(uri), - params=normalize_parameters(params)) - - -def sign_hmac(method, url, params, client_secret, access_secret): - """Sign a request using HMAC-SHA1. - - Per `section 3.4.2`_ of the spec. - - .. _`section 3.4.2`: http://tools.ietf.org/html/rfc5849#section-3.4.2 - - """ - base_string = prepare_base_string(method, url, params) - key = prepare_hmac_key(client_secret, access_secret) - signature = hmac.new(key, base_string, hashlib.sha1) - return escape(binascii.b2a_base64(signature.digest())[:-1]) - - -def prepare_authorization_header(realm, params): - """Prepare the authorization header. - - Per `section 3.5.1`_ of the spec. - - .. _`section 3.5.1`: http://tools.ietf.org/html/rfc5849#section-3.5.1 - - """ - return 'OAuth realm="{realm}", {params}'.format( - realm=realm, params=', '.join( - ['{0}="{1}"'.format(k, params[k]) for k in params.keys()])) diff --git a/oauthlib/signature.py b/oauthlib/signature.py index 688a133..c5a2f5b 100644 --- a/oauthlib/signature.py +++ b/oauthlib/signature.py @@ -24,7 +24,6 @@ Steps for signing a request: import binascii import hashlib import hmac -import urllib2 import urlparse from . import utils @@ -78,12 +77,7 @@ def collect_parameters(uri_query=None, authorization_header=None, body=None): params.extend(urlparse.parse_qs(uri_query).items()) if authorization_header is not None: - items = urllib2.parse_http_list(authorization_header) - try: - auth_header_params = urllib2.parse_keqv_list(items) - except ValueError: - raise ValueError('Malformed authorization header') - params.extend(auth_header_params.items()) + params.extend(utils.parse_authorization_header(authorization_header)) if body is not None: params.extend(urlparse.parse_qs(body).items()) @@ -94,7 +88,7 @@ def collect_parameters(uri_query=None, authorization_header=None, body=None): ) params = filter(lambda i: i[0] not in exclude_params, params) - return params.items() + return params def normalize_parameters(params): """Normalize querystring parameters for use in constructing a base string. @@ -122,7 +116,8 @@ def sign_hmac_sha1(base_string, client_secret, resource_owner_secret): key = u'&'.join((utils.escape(client_secret), utils.escape(resource_owner_secret))) - signature = hmac.new(key, base_string, hashlib.sha1) + signature = hmac.new(key.encode('utf-8'), base_string.encode('utf-8'), + hashlib.sha1) return binascii.b2a_base64(signature.digest())[:-1] def sign_rsa_sha1(base_string, rsa_private_key): diff --git a/oauthlib/utils.py b/oauthlib/utils.py index 0b81df4..20b4d70 100644 --- a/oauthlib/utils.py +++ b/oauthlib/utils.py @@ -10,6 +10,7 @@ spec. import time import urllib +import urllib2 from random import getrandbits @@ -90,3 +91,14 @@ def urlencode(query): query = query.items() return "&".join(['='.join([escape(k), escape(v)]) for k, v in query]) +def parse_authorization_header(authorization_header): + """Parse an OAuth authorization header into a list of 2-tuples""" + auth_scheme = 'OAuth ' + if authorization_header.startswith(auth_scheme): + authorization_header = authorization_header.replace(auth_scheme, '', 1) + items = urllib2.parse_http_list(authorization_header) + try: + return urllib2.parse_keqv_list(items).items() + except ValueError: + raise ValueError('Malformed authorization header') + |