diff options
author | Ib Lundgren <ib.lundgren@gmail.com> | 2013-06-20 10:58:07 +0100 |
---|---|---|
committer | Ib Lundgren <ib.lundgren@gmail.com> | 2013-06-20 10:58:07 +0100 |
commit | ce861400b97e884b6b9e326d4708e30d045fe2f2 (patch) | |
tree | c095bd313e373927bd33c9e16678c321e296c772 | |
parent | ded77d72addaa46d718d84643616c8bba5fab43d (diff) | |
download | oauthlib-oauth1_provider_revamp.tar.gz |
Endpoint source documentation.oauth1_provider_revamp
-rw-r--r-- | docs/oauth1/endpoints.rst | 15 | ||||
-rw-r--r-- | oauthlib/oauth1/rfc5849/endpoints/access_token.py | 91 | ||||
-rw-r--r-- | oauthlib/oauth1/rfc5849/endpoints/authorization.py | 75 | ||||
-rw-r--r-- | oauthlib/oauth1/rfc5849/endpoints/request_token.py | 88 | ||||
-rw-r--r-- | oauthlib/oauth1/rfc5849/endpoints/resource.py | 55 | ||||
-rw-r--r-- | tests/oauth1/rfc5849/endpoints/test_access_token.py | 8 | ||||
-rw-r--r-- | tests/oauth1/rfc5849/endpoints/test_authorization.py | 2 | ||||
-rw-r--r-- | tests/oauth1/rfc5849/endpoints/test_request_token.py | 8 |
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( |