summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIb Lundgren <ib.lundgren@gmail.com>2013-06-20 10:58:07 +0100
committerIb Lundgren <ib.lundgren@gmail.com>2013-06-20 10:58:07 +0100
commitce861400b97e884b6b9e326d4708e30d045fe2f2 (patch)
treec095bd313e373927bd33c9e16678c321e296c772
parentded77d72addaa46d718d84643616c8bba5fab43d (diff)
downloadoauthlib-oauth1_provider_revamp.tar.gz
Endpoint source documentation.oauth1_provider_revamp
-rw-r--r--docs/oauth1/endpoints.rst15
-rw-r--r--oauthlib/oauth1/rfc5849/endpoints/access_token.py91
-rw-r--r--oauthlib/oauth1/rfc5849/endpoints/authorization.py75
-rw-r--r--oauthlib/oauth1/rfc5849/endpoints/request_token.py88
-rw-r--r--oauthlib/oauth1/rfc5849/endpoints/resource.py55
-rw-r--r--tests/oauth1/rfc5849/endpoints/test_access_token.py8
-rw-r--r--tests/oauth1/rfc5849/endpoints/test_authorization.py2
-rw-r--r--tests/oauth1/rfc5849/endpoints/test_request_token.py8
8 files changed, 309 insertions, 33 deletions
diff --git a/docs/oauth1/endpoints.rst b/docs/oauth1/endpoints.rst
index b01d7f8..0b76212 100644
--- a/docs/oauth1/endpoints.rst
+++ b/docs/oauth1/endpoints.rst
@@ -1,20 +1,35 @@
Provider endpoints
==================
+.. contents:: OAuth 1 Provider Endpoints
+ :depth: 3
+
Each endpoint is responsible for one step in the OAuth 1 workflow. They can be
used either independently or in a combination. They depend on the use of a
:doc:`validator`.
See :doc:`preconfigured_servers` for available composite endpoints/servers.
+AuthorizationEndpoint
+---------------------
+
.. autoclass:: oauthlib.oauth1.AuthorizationEndpoint
:members:
+AccessTokenEndpoint
+-------------------
+
.. autoclass:: oauthlib.oauth1.AccessTokenEndpoint
:members:
+RequestTokenEndpoint
+--------------------
+
.. autoclass:: oauthlib.oauth1.RequestTokenEndpoint
:members:
+ResourceEndpoint
+----------------
+
.. autoclass:: oauthlib.oauth1.ResourceEndpoint
:members:
diff --git a/oauthlib/oauth1/rfc5849/endpoints/access_token.py b/oauthlib/oauth1/rfc5849/endpoints/access_token.py
index 34a08dd..24727bb 100644
--- a/oauthlib/oauth1/rfc5849/endpoints/access_token.py
+++ b/oauthlib/oauth1/rfc5849/endpoints/access_token.py
@@ -2,11 +2,13 @@
from __future__ import absolute_import, unicode_literals
"""
-oauthlib.oauth1.rfc5849
-~~~~~~~~~~~~~~
+oauthlib.oauth1.rfc5849.endpoints.access_token
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This module is an implementation of various logic needed
-for signing and checking OAuth 1.0 RFC 5849 requests.
+This module is an implementation of the access token provider logic of
+OAuth 1.0 RFC 5849. It validates the correctness of access token requests,
+creates and persists tokens as well as create the proper response to be
+returned to the client.
"""
from oauthlib.common import log, urlencode
@@ -15,8 +17,24 @@ from .. import errors
class AccessTokenEndpoint(BaseEndpoint):
+ """An endpoint responsible for providing OAuth 1 access tokens.
- def create_access_token(self, request):
+ Typical use is to instantiate with a request validator and invoke the
+ ``create_access_token_response`` from a view function. The tuple returned
+ has all information necessary (body, status, headers) to quickly form
+ and return a proper response. See :doc:`validator` for details on which
+ validator methods to implement for this endpoint.
+ """
+
+ def create_access_token(self, request, credentials):
+ """Create and save a new access token.
+
+ Similar to OAuth 2, indication of granted scopes will be included as a
+ space separated list in ``oauth_authorized_realms``.
+
+ :param request: An oauthlib.common.Request object.
+ :returns: The token as an urlencoded string.
+ """
request.realm = self.request_validator.get_realms(
request.oauth_token, request)
token = {
@@ -25,25 +43,82 @@ class AccessTokenEndpoint(BaseEndpoint):
# Backport the authorized scopes indication used in OAuth2
'oauth_authorized_realms': ' '.join(request.realm)
}
+ token.update(credentials)
self.request_validator.save_access_token(token, request)
return urlencode(token.items())
def create_access_token_response(self, uri, http_method='GET', body=None,
- headers=None):
+ headers=None, credentials=None):
+
+ """Create an access token response, with a new request token if valid.
+
+ :param uri: The full URI of the token request.
+ :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
+ :param body: The request body as a string.
+ :param headers: The request headers as a dict.
+ :param credentials: A list of extra credentials to include in the token.
+ :returns: A tuple of 4 elements.
+ 1. None (uri but n/a for this endpoint, here for consistency.
+ 2. A dict of headers to set on the response.
+ 3. The response body as a string.
+ 4. The response status code as an integer.
+
+ An example of a valid request::
+
+ >>> from your_validator import your_validator
+ >>> from oauthlib.oauth1 import AccessTokenEndpoint
+ >>> endpoint = AccessTokenEndpoint(your_validator)
+ >>> u, h, b, s = endpoint.create_access_token_response(
+ ... 'https://your.provider/access_token?foo=bar',
+ ... headers={
+ ... 'Authorization': 'OAuth oauth_token=234lsdkf....'
+ ... },
+ ... credentials={
+ ... 'my_specific': 'argument',
+ ... })
+ >>> u
+ None
+ >>> h
+ {'Content-Type': 'application/x-www-form-urlencoded'}
+ >>> b
+ 'oauth_token=lsdkfol23w54jlksdef&oauth_token_secret=qwe089234lkjsdf&oauth_authorized_realms=movies+pics&my_specific=argument'
+ >>> s
+ 200
+
+ An response to invalid request would have a different body and status::
+
+ >>> b
+ 'error=invalid_request&description=missing+resource+owner+key'
+ >>> s
+ 400
+
+ The same goes for an an unauthorized request:
+
+ >>> b
+ ''
+ >>> s
+ 401
+ """
resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
try:
request = self._create_request(uri, http_method, body, headers)
valid, processed_request = self.validate_access_token_request(
request)
if valid:
- token = self.create_access_token(request)
+ token = self.create_access_token(request, credentials or {})
return None, resp_headers, token, 200
else:
- return None, {}, None, 403
+ return None, {}, None, 401
except errors.OAuth1Error as e:
return None, resp_headers, e.urlencoded, e.status_code
def validate_access_token_request(self, request):
+ """Validate an access token request.
+
+ :param request: An oauthlib.common.Request object.
+ :raises: OAuth1Error if the request is invalid.
+ :returns: True or False
+ """
self._check_transport_security(request)
self._check_mandatory_parameters(request)
diff --git a/oauthlib/oauth1/rfc5849/endpoints/authorization.py b/oauthlib/oauth1/rfc5849/endpoints/authorization.py
index 672863b..6957232 100644
--- a/oauthlib/oauth1/rfc5849/endpoints/authorization.py
+++ b/oauthlib/oauth1/rfc5849/endpoints/authorization.py
@@ -2,8 +2,8 @@
from __future__ import absolute_import, unicode_literals
"""
-oauthlib.oauth1.rfc5849
-~~~~~~~~~~~~~~
+oauthlib.oauth1.rfc5849.endpoints.authorization
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
@@ -16,8 +16,31 @@ from .. import errors
class AuthorizationEndpoint(BaseEndpoint):
+ """An endpoint responsible for letting authenticated users authorize access
+ to their protected resources to a client.
+
+ Typical use would be to have two views, one for displaying the authorization
+ form and one to process said form on submission.
+
+ The first view will want to utilize ``get_realms_and_credentials`` to fetch
+ requested realms and useful client credentials, such as name and
+ description, to be used when creating the authorization form.
+
+ During form processing you can use ``create_authorization_response`` to
+ validate the request, create a verifier as well as prepare the final
+ redirection URI used to send the user back to the client.
+
+ See :doc:`validator` for details on which validator methods to implement
+ for this endpoint.
+ """
def create_verifier(self, request, credentials):
+ """Create and save a new request token.
+
+ :param request: An oauthlib.common.Request object.
+ :param credentials: A dict of extra token credentials.
+ :returns: The verifier as a dict.
+ """
verifier = {
'oauth_token': request.oauth_token,
'oauth_verifier': self.token_generator(),
@@ -29,6 +52,41 @@ class AuthorizationEndpoint(BaseEndpoint):
def create_authorization_response(self, uri, http_method='GET', body=None,
headers=None, realms=None, credentials=None):
+ """Create an authorization response, with a new request token if valid.
+
+ :param uri: The full URI of the token request.
+ :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
+ :param body: The request body as a string.
+ :param headers: The request headers as a dict.
+ :param credentials: A list of credentials to include in the verifier.
+ :returns: A tuple of 4 elements.
+ 1. The URI to be used to redirect the user back to client.
+ 2. A dict of headers to set on the response.
+ 3. The response body as a string.
+ 4. The response status code as an integer.
+
+ An example of a valid request::
+
+ >>> from your_validator import your_validator
+ >>> from oauthlib.oauth1 import RequestTokenEndpoint
+ >>> endpoint = RequestTokenEndpoint(your_validator)
+ >>> u, h, b, s = endpoint.create_request_token_response(
+ ... 'https://your.provider/request_token?foo=bar',
+ ... headers={
+ ... 'Authorization': 'OAuth realm=movies user, oauth_....'
+ ... },
+ ... credentials={
+ ... 'extra': 'argument',
+ ... })
+ >>> u
+ 'https://the.client/callback?oauth_verifier=...&mextra=argument'
+ >>> h
+ {}
+ >>> b
+ ''
+ >>> s
+ 302
+ """
request = Request(uri, http_method=http_method, body=body,
headers=headers)
@@ -50,10 +108,21 @@ class AuthorizationEndpoint(BaseEndpoint):
request.oauth_token, request)
verifier = self.create_verifier(request, credentials or {})
uri = add_params_to_uri(redirect_uri, verifier.items())
- return uri, {}, None, 301
+ return uri, {}, None, 302
def get_realms_and_credentials(self, uri, http_method='GET', body=None,
headers=None):
+ """Fetch realms and credentials for the presented request token.
+
+ :param uri: The full URI of the token request.
+ :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
+ :param body: The request body as a string.
+ :param headers: The request headers as a dict.
+ :returns: A tuple of 2 elements.
+ 1. A list of request realms.
+ 2. A dict of credentials which may be useful in creating the
+ authorization form.
+ """
request = Request(uri, http_method=http_method, body=body,
headers=headers)
diff --git a/oauthlib/oauth1/rfc5849/endpoints/request_token.py b/oauthlib/oauth1/rfc5849/endpoints/request_token.py
index d75ed0e..0fe8e3d 100644
--- a/oauthlib/oauth1/rfc5849/endpoints/request_token.py
+++ b/oauthlib/oauth1/rfc5849/endpoints/request_token.py
@@ -2,11 +2,13 @@
from __future__ import absolute_import, unicode_literals
"""
-oauthlib.oauth1.rfc5849
-~~~~~~~~~~~~~~
+oauthlib.oauth1.rfc5849.endpoints.request_token
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This module is an implementation of various logic needed
-for signing and checking OAuth 1.0 RFC 5849 requests.
+This module is an implementation of the request token provider logic of
+OAuth 1.0 RFC 5849. It validates the correctness of request token requests,
+creates and persists tokens as well as create the proper response to be
+returned to the client.
"""
from oauthlib.common import log, urlencode
@@ -15,32 +17,102 @@ from .. import errors
class RequestTokenEndpoint(BaseEndpoint):
-
- def create_request_token(self, request):
+ """An endpoint responsible for providing OAuth 1 request tokens.
+
+ Typical use is to instantiate with a request validator and invoke the
+ ``create_request_token_response`` from a view function. The tuple returned
+ has all information necessary (body, status, headers) to quickly form
+ and return a proper response. See :doc:`validator` for details on which
+ validator methods to implement for this endpoint.
+ """
+
+ def create_request_token(self, request, credentials):
+ """Create and save a new request token.
+
+ :param request: An oauthlib.common.Request object.
+ :param credentials: A dict of extra token credentials.
+ :returns: The token as an urlencoded string.
+ """
token = {
'oauth_token': self.token_generator(),
'oauth_token_secret': self.token_generator(),
'oauth_callback_confirmed': 'true'
}
+ token.update(credentials)
self.request_validator.save_request_token(token, request)
return urlencode(token.items())
def create_request_token_response(self, uri, http_method='GET', body=None,
headers=None, credentials=None):
+ """Create a request token response, with a new request token if valid.
+
+ :param uri: The full URI of the token request.
+ :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
+ :param body: The request body as a string.
+ :param headers: The request headers as a dict.
+ :param credentials: A list of extra credentials to include in the token.
+ :returns: A tuple of 4 elements.
+ 1. None (uri but n/a for this endpoint, here for consistency.
+ 2. A dict of headers to set on the response.
+ 3. The response body as a string.
+ 4. The response status code as an integer.
+
+ An example of a valid request::
+
+ >>> from your_validator import your_validator
+ >>> from oauthlib.oauth1 import RequestTokenEndpoint
+ >>> endpoint = RequestTokenEndpoint(your_validator)
+ >>> u, h, b, s = endpoint.create_request_token_response(
+ ... 'https://your.provider/request_token?foo=bar',
+ ... headers={
+ ... 'Authorization': 'OAuth realm=movies user, oauth_....'
+ ... },
+ ... credentials={
+ ... 'my_specific': 'argument',
+ ... })
+ >>> u
+ None
+ >>> h
+ {'Content-Type': 'application/x-www-form-urlencoded'}
+ >>> b
+ 'oauth_token=lsdkfol23w54jlksdef&oauth_token_secret=qwe089234lkjsdf&oauth_callback_confirmed=true&my_specific=argument'
+ >>> s
+ 200
+
+ An response to invalid request would have a different body and status::
+
+ >>> b
+ 'error=invalid_request&description=missing+callback+uri'
+ >>> s
+ 400
+
+ The same goes for an an unauthorized request:
+
+ >>> b
+ ''
+ >>> s
+ 401
+ """
resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
try:
request = self._create_request(uri, http_method, body, headers)
valid, processed_request = self.validate_request_token_request(
request)
if valid:
- token = self.create_request_token(request)
+ token = self.create_request_token(request, credentials or {})
return None, resp_headers, token, 200
else:
- return None, {}, None, 403
+ return None, {}, None, 401
except errors.OAuth1Error as e:
return None, resp_headers, e.urlencoded, e.status_code
def validate_request_token_request(self, request):
+ """Validate a request token request.
+
+ :param request: An oauthlib.common.Request object.
+ :raises: OAuth1Error if the request is invalid.
+ :returns: True or False
+ """
self._check_transport_security(request)
self._check_mandatory_parameters(request)
diff --git a/oauthlib/oauth1/rfc5849/endpoints/resource.py b/oauthlib/oauth1/rfc5849/endpoints/resource.py
index fc3574b..9ca49cb 100644
--- a/oauthlib/oauth1/rfc5849/endpoints/resource.py
+++ b/oauthlib/oauth1/rfc5849/endpoints/resource.py
@@ -2,11 +2,11 @@
from __future__ import absolute_import, unicode_literals
"""
-oauthlib.oauth1.rfc5849
-~~~~~~~~~~~~~~
+oauthlib.oauth1.rfc5849.endpoints.resource
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This module is an implementation of various logic needed
-for signing and checking OAuth 1.0 RFC 5849 requests.
+This module is an implementation of the resource protection provider logic of
+OAuth 1.0 RFC 5849.
"""
from oauthlib.common import log
@@ -15,10 +15,55 @@ from .. import errors
class ResourceEndpoint(BaseEndpoint):
+ """An endpoint responsible for protecting resources.
+
+ Typical use is to instantiate with a request validator and invoke the
+ ``validate_protected_resource_request`` in a decorator around a view
+ function. If the request is valid, invoke and return the response of the
+ view. If invalid create and return an error response directly from the
+ decorator.
+
+ See :doc:`validator` for details on which validator methods to implement
+ for this endpoint.
+
+ An example decorator::
+
+ from functools import wraps
+ from your_validator import your_validator
+ from oauthlib.oauth1 import ResourceEndpoint
+ endpoint = ResourceEndpoint(your_validator)
+
+ def require_oauth(realms=None):
+ def decorator(f):
+ @wraps(f)
+ def wrapper(request, *args, **kwargs):
+ v, r = provider.validate_protected_resource_request(
+ request.url,
+ http_method=request.method,
+ body=request.data,
+ headers=request.headers,
+ valid_realms=realms or [])
+ if v:
+ return f(*args, **kwargs)
+ else:
+ return abort(403)
+ """
def validate_protected_resource_request(self, uri, http_method='GET',
body=None, headers=None, valid_realms=None):
-
+ """Create a request token response, with a new request token if valid.
+
+ :param uri: The full URI of the token request.
+ :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
+ :param body: The request body as a string.
+ :param headers: The request headers as a dict.
+ :param valid_realms: A list of realms the resource is protected under.
+ This will be supplied to the ``validate_realm``
+ method of the request validator.
+ :returns: A tuple of 2 elements.
+ 1. True if valid, False otherwise.
+ 2. An oauthlib.common.Request object.
+ """
try:
request = self._create_request(uri, http_method, body, headers)
except errors.OAuth1Error:
diff --git a/tests/oauth1/rfc5849/endpoints/test_access_token.py b/tests/oauth1/rfc5849/endpoints/test_access_token.py
index dc8e039..ccb5f59 100644
--- a/tests/oauth1/rfc5849/endpoints/test_access_token.py
+++ b/tests/oauth1/rfc5849/endpoints/test_access_token.py
@@ -55,19 +55,19 @@ class AccessTokenEndpointTest(TestCase):
self.validator.validate_client_key.return_value = False
u, h, b, s = self.endpoint.create_access_token_response(
self.uri, headers=self.headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_validate_request_token(self):
self.validator.validate_request_token.return_value = False
u, h, b, s = self.endpoint.create_access_token_response(
self.uri, headers=self.headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_validate_verifier(self):
self.validator.validate_verifier.return_value = False
u, h, b, s = self.endpoint.create_access_token_response(
self.uri, headers=self.headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_validate_signature(self):
client = Client('foo',
@@ -77,7 +77,7 @@ class AccessTokenEndpointTest(TestCase):
_, headers, _ = client.sign(self.uri + '/extra')
u, h, b, s = self.endpoint.create_access_token_response(
self.uri, headers=headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_valid_request(self):
u, h, b, s = self.endpoint.create_access_token_response(
diff --git a/tests/oauth1/rfc5849/endpoints/test_authorization.py b/tests/oauth1/rfc5849/endpoints/test_authorization.py
index 1e00434..c3876e1 100644
--- a/tests/oauth1/rfc5849/endpoints/test_authorization.py
+++ b/tests/oauth1/rfc5849/endpoints/test_authorization.py
@@ -40,6 +40,6 @@ class ResourceEndpointTest(TestCase):
def test_create_authorization_response(self):
u, h, b, s = self.endpoint.create_authorization_response(self.uri)
- self.assertEqual(s, 301)
+ self.assertEqual(s, 302)
self.assertTrue(u.startswith('https://c.b/cb'))
self.assertIn('oauth_verifier', u)
diff --git a/tests/oauth1/rfc5849/endpoints/test_request_token.py b/tests/oauth1/rfc5849/endpoints/test_request_token.py
index 1743eda..a6855ab 100644
--- a/tests/oauth1/rfc5849/endpoints/test_request_token.py
+++ b/tests/oauth1/rfc5849/endpoints/test_request_token.py
@@ -50,26 +50,26 @@ class RequestTokenEndpointTest(TestCase):
self.validator.validate_client_key.return_value = False
u, h, b, s = self.endpoint.create_request_token_response(
self.uri, headers=self.headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_validate_realm(self):
self.validator.validate_requested_realm.return_value = False
u, h, b, s = self.endpoint.create_request_token_response(
self.uri, headers=self.headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_validate_redirect_uri(self):
self.validator.validate_redirect_uri.return_value = False
u, h, b, s = self.endpoint.create_request_token_response(
self.uri, headers=self.headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_validate_signature(self):
client = Client('foo', callback_uri='https://c.b/cb')
_, headers, _ = client.sign(self.uri + '/extra')
u, h, b, s = self.endpoint.create_request_token_response(
self.uri, headers=headers)
- self.assertEqual(s, 403)
+ self.assertEqual(s, 401)
def test_valid_request(self):
u, h, b, s = self.endpoint.create_request_token_response(