Permalink
Browse files

updating docstrings, simplifying OAuth 2.0 auth methods

  • Loading branch information...
1 parent 169ebbe commit a38ca1a752c0cc7a563030a687ef5a94f107e06a @maxcountryman maxcountryman committed Mar 8, 2012
Showing with 158 additions and 22 deletions.
  1. +2 −1 tests/test_service.py
  2. +1 −1 webauth/__init__.py
  3. +14 −1 webauth/hook.py
  4. +16 −3 webauth/oauth.py
  5. +125 −16 webauth/service.py
View
3 tests/test_service.py
@@ -50,7 +50,8 @@ def test_init_with_access_token(self):
def test_get_authorize_url(self):
authorize_url = self.service.get_authorize_url()
- expected_url = 'http://example.com/authorize?client_id=123'
+ expected_url = \
+ 'http://example.com/authorize?response_type=code&client_id=123'
self.assertEqual(expected_url, authorize_url)
@patch.object(requests.Session, 'request')
View
2 webauth/__init__.py
@@ -6,4 +6,4 @@
'''
-__version__ = '0.1.3'
+__version__ = '0.1.4'
View
15 webauth/hook.py
@@ -51,6 +51,15 @@ class OAuth1Hook(object):
Additionally some services will make use of header authentication. This is
provided by passing :class:`__init__` the `auth_header` parameter as
`True`.
+
+ :param consumer_key: Client consumer key.
+ :param consumer_secret: Client consumer secret.
+ :param access_token: Access token key.
+ :param access_token_secret: Access token secret.
+ :param header_auth: Authenication via header, defauls to False.
+ :param signature: A signature method used to sign request parameters.
+ Defaults to None. If None the `HmacSha1Signature` method is used as
+ default.
'''
OAUTH_VERSION = '1.0'
@@ -131,7 +140,11 @@ def generate_oauth_params(self):
return oauth_params
def generate_authorization_header(self, oauth_params, realm=None):
- '''This method constructs an authorization header.'''
+ '''This method constructs an authorization header.
+
+ :param oauth_params: The OAuth parameters to be added to the header.
+ :param realm: The authentication realm. Defaults to None.
+ '''
auth_header = 'OAuth realm="{0}"'.format(realm)
params = ''
for k, v in oauth_params.items():
View
19 webauth/oauth.py
@@ -40,11 +40,17 @@ def _encode_utf8(self, s):
return unicode(s, 'utf-8').encode('utf-8')
def _escape(self, s):
- '''Escapes a string, ensuring it is encoded as a UTF-8 octet.'''
+ '''Escapes a string, ensuring it is encoded as a UTF-8 octet.
+
+ :param s: A string to be encoded.
+ '''
return quote(self._encode_utf8(s), safe='~')
def _remove_qs(self, url):
- '''Removes a query string from a URL before signing.'''
+ '''Removes a query string from a URL before signing.
+
+ :param url: The URL to strip.
+ '''
# split 'em up
scheme, netloc, path, query, fragment = urlsplit(url)
@@ -69,6 +75,8 @@ def _normalize_request_parameters(self, request):
Otherwise we build a series intermediary lists of tuples depending on
the type of `request.params` and `request.data`.
+
+ :param request: The request object that will be normalized.
'''
if type(request.params) != str and type(request.data) != str:
# if neither params nor data are a string, i.e. both are dicts
@@ -131,7 +139,12 @@ class HmacSha1Signature(SignatureMethod):
NAME = 'HMAC-SHA1'
def sign(self, request, consumer, token=None):
- '''Sign request parameters.'''
+ '''Sign request parameters.
+
+ :param request: The request to sign.
+ :param consumer: The consumer token object.
+ :param token: The access token object.
+ '''
# the necessary parameters we'll sign
url = self._remove_qs(request.url)
View
141 webauth/service.py
@@ -15,6 +15,7 @@
def _parse_response(response):
+ '''Attempts to coerce response.content into a dictionary.'''
if isinstance(response.content, str):
try:
content = json.loads(response.content)
@@ -27,7 +28,41 @@ def _parse_response(response):
class OAuth2Service(object):
- '''An OAuth 2.0 Service container.'''
+ '''An OAuth 2.0 Service container.
+
+ This class is similar in nature to the OAuth1Service container but does
+ not make use of a request's hook. Instead the OAuth 2.0 spec is currently
+ simple enough that we can wrap it around requests directly.
+
+ You might intialize :class:`OAuth2Service` something like this::
+
+ service = OAuth2Service(
+ name='example',
+ consumer_key='123',
+ consumer_secret='456',
+ access_token_url='http://example.com/token',
+ authorize_url='http://example.com/authorize')
+
+ Given the simplicity of OAuth 2.0 now this object `service` can be used to
+ retrieve an access token in two steps::
+
+ # the return URL is used to validate the request
+ url = service.get_authorize_url(redirect_uri='http://example.com/',
+ response_type='code')
+
+ # once the above URL is consumed by a client we can ask for an access
+ # token. note that the code is retrieved from the redirect URL above,
+ # as set by the provider
+ token = service.get_access_token(code='foobar',
+ grant_type='authorization_code',
+ redirect_uri='http://example.com/')
+ :param name: The service name.
+ :param consumer_key: Client consumer key.
+ :param consumer_secret: Client consumer secret.
+ :param access_token_url: Access token endpoint.
+ :param authorize_url: Authorize endpoint.
+ :param access_token: An access token, defaults to None.
+ '''
def __init__(self, name, consumer_key, consumer_secret, access_token_url,
authorize_url, access_token=None):
self.name = name
@@ -42,14 +77,35 @@ def __init__(self, name, consumer_key, consumer_secret, access_token_url,
if access_token is not None:
self.access_token = access_token
- def get_authorize_url(self, **params):
- '''Returns a proper authorize URL.'''
+ def get_authorize_url(self, response_type=None, **params):
+ '''Returns a proper authorize URL.
+
+ :param reponse_type: The response type, usually 'code'. Defaults to
+ None, if set to None, appends 'response_type=code' to the request
+ querystring.
+ :param **params: Additional arguments to be added to the request
+ querystring.
+ '''
+ # defaults to 'code'
+ if response_type is None:
+ params.update({'response_type': 'code'})
+
params.update({'client_id': self.consumer_key})
params = '?' + urlencode(params)
return self.authorize_url + params
- def get_access_token(self, **data):
- '''Retrieves the access token.'''
+ def get_access_token(self, grant_type=None, **data):
+ '''Retrieves the access token.
+
+ :param grant_type: The response type, usually 'authorization_code'.
+ Defaults to None, if set to None, appends
+ 'grant_type=authorization_code' to the request body.
+ :param **data: Arguments to be passed in the body of the request.
+ '''
+ # defaults to authorization_code
+ if grant_type is None:
+ data.update({'grant_type': 'authorization_code'})
+
data.update(dict(client_id=self.consumer_key,
client_secret=self.consumer_secret))
@@ -63,7 +119,19 @@ def get_access_token(self, **data):
def request(self, http_method, url, access_token=None, **params):
'''Sends a request to an OAuth 2.0 endpoint, properly wrapped around
- requests.'''
+ requests.
+
+ The first time an access token is provided it will be saved on the
+ object for convenience.
+
+ :param http_method: A string representation of the HTTP method to be
+ used.
+ :param url: The resource to be requested.
+ :param access_token: The access token as returned by
+ :class:`get_access_token`
+ :param **params: Additional arguments to be added to the request
+ querystring.
+ '''
if access_token is None and self.access_token is None:
raise ValueError('Access token must be set!')
elif access_token is not None:
@@ -83,14 +151,14 @@ class OAuth1Service(object):
'''An OAuth 1.0/a Service container.
This class provides a container for an OAuth Service provider. It utilizes
- the OAuthHook module which in turn is hooked into Python Requests.
- Primarily this object can be used to provide a clean interface to provider
- endpoints and helps streamline the process of making OAuth requests.
+ the OAuthHook object which in turn is hooked into Python Requests. This
+ object can be used to streamline the process of authenticating with and
+ using an OAuth 1.0/a service provider.
- You might intialize :class:`OAuthService` something like this::
+ You might intialize :class:`OAuth1Service` something like this::
service = OAuth1Service(
- 'example',
+ name='example',
consumer_key='123',
consumer_secret='456',
request_token_url='http://example.com/request_token',
@@ -120,6 +188,14 @@ class OAuth1Service(object):
Finally the :class:`get_authenticated_session` method returns a wrapped
session and can be used once the access token has been made available.
This provides simple access to the providers endpoints.
+
+ :param name: The service name.
+ :param consumer_key: Client consumer key.
+ :param consumer_secret: Client consumer secret.
+ :param request_token_url: Request token endpoint.
+ :param access_token_url: Access token endpoint.
+ :param authorize_url: Authorize endpoint.
+ :param header_auth: Authenication via header, defauls to False.
'''
def __init__(self, name, consumer_key, consumer_secret, request_token_url,
access_token_url, authorize_url, header_auth=False):
@@ -128,6 +204,7 @@ def __init__(self, name, consumer_key, consumer_secret, request_token_url,
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
+ # authorization endpoints
self.request_token_url = request_token_url
self.access_token_url = access_token_url
self.authorize_url = authorize_url
@@ -137,14 +214,23 @@ def __init__(self, name, consumer_key, consumer_secret, request_token_url,
def _construct_session(self, **kwargs):
'''Construct the request session, supplying the consumer key and
- secret.'''
+ secret.
+
+ :param **kwargs: Extra arguments to be passed to the OAuth1Hook
+ constructor.
+ '''
hook = OAuth1Hook(consumer_key=self.consumer_key,
consumer_secret=self.consumer_secret,
**kwargs)
return requests.session(hooks={'pre_request': hook})
def get_request_token(self, http_method, **data):
- '''Gets a request token from the request token endpoint.'''
+ '''Gets a request token from the request token endpoint.
+
+ :param http_method: A string representation of the HTTP method to be
+ used.
+ :param **data: Arguments to be passed in the body of the request.
+ '''
auth_session = \
self._construct_session(header_auth=self.header_auth)
@@ -159,14 +245,30 @@ def get_request_token(self, http_method, **data):
return data['oauth_token'], data['oauth_token_secret']
def get_authorize_url(self, request_token, **params):
- '''Returns a proper authorize URL.'''
+ '''Returns a proper authorize URL.
+
+ :param request_token: The request token as returned by
+ :class:`get_request_token`.
+ :param **params: Additional arguments to be added to the request
+ querystring.
+ '''
params.update({'oauth_token': quote(request_token)})
params = '?' + urlencode(params)
return self.authorize_url + params
def get_access_token(self, request_token, request_token_secret,
http_method, **params):
- '''Retrieves the access token.'''
+ '''Retrieves the access token.
+
+ :param request_token: The request token as returned by
+ :class:`get_request_token`
+ :param request_token_secret: The request token secret as returned by
+ :class:`get_request_token`
+ :param http_method: A string representation of the HTTP method to be
+ used.
+ :param **params: Additional arguments to be added to the request
+ querystring.
+ '''
auth_session = self._construct_session(
access_token=request_token,
access_token_secret=request_token_secret,
@@ -183,7 +285,14 @@ def get_access_token(self, request_token, request_token_secret,
def get_authenticated_session(self, access_token, access_token_secret,
header_auth=False):
- '''Returns an authenticated Requests session utilizing the hook.'''
+ '''Returns an authenticated Requests session utilizing the hook.
+
+ :param access_token: The access token as returned by
+ :class:`get_access_token`
+ :param access_token_secret: The access token secret as returned by
+ :class:`get_access_token`
+ :param header_auth: Authenication via header, defauls to False.
+ '''
return self._construct_session(access_token=access_token,
access_token_secret=access_token_secret,
header_auth=header_auth)

0 comments on commit a38ca1a

Please sign in to comment.