diff -Nru python-oauthlib-0.5.1/PKG-INFO python-oauthlib-0.6.1/PKG-INFO --- python-oauthlib-0.5.1/PKG-INFO 2013-07-26 18:13:18.000000000 +0000 +++ python-oauthlib-0.6.1/PKG-INFO 2014-01-20 11:10:42.000000000 +0000 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: oauthlib -Version: 0.5.1 +Version: 0.6.1 Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic Home-page: https://github.com/idan/oauthlib -Author: Idan Gazit -Author-email: idan@gazit.me +Author: Ib Lundgren +Author-email: ib.lundgren@gmail.com License: BSD Description: OAuthLib ======== @@ -19,19 +19,23 @@ OAuth often seems complicated and difficult-to-implement. There are several - prominent libraries for signing OAuth requests, but they all suffer from one or + prominent libraries for handling OAuth requests, but they all suffer from one or both of the following: 1. They predate the `OAuth 1.0 spec`_, AKA RFC 5849. - 2. They assume the usage of a specific HTTP request library. + 2. They predate the `OAuth 2.0 spec`_, AKA RFC 6749. + 3. They assume the usage of a specific HTTP request library. .. _`OAuth 1.0 spec`: http://tools.ietf.org/html/rfc5849 + .. _`OAuth 2.0 spec`: http://tools.ietf.org/html/rfc6749 OAuthLib is a generic utility which implements the logic of OAuth without - assuming a specific HTTP request object. Use it to graft OAuth support onto your - favorite HTTP library. If you're a maintainer of such a library, write a thin + assuming a specific HTTP request object or web framework. Use it to graft OAuth + client support onto your favorite HTTP library, or provider support onto your + favourite web framework. If you're a maintainer of such a library, write a thin veneer on top of OAuthLib and get OAuth support for very little effort. + Documentation -------------- @@ -90,13 +94,47 @@ OAuthLib is yours to use and abuse according to the terms of the BSD license. Check the LICENSE file for full details. - - Changelog --------- - *OAuthLib is in active development, with most of OAuth1 complete and OAuth2 - already in the works.* + *OAuthLib is in active development, with the core of both OAuth 1 and 2 + completed, for providers as well as clients.* See `supported features`_ for + details. + + .. _`supported features`: http://oauthlib.readthedocs.org/en/latest/feature_matrix.html + + 0.6.1: Draft revocation endpoint features and numerous fixes including + + * (OAuth 2 Provider) is_within_original_scope to check whether a refresh token + is trying to aquire a new set of scopes that are a subset of the original scope. + + * (OAuth 2 Provider) expires_in token lifetime can be set per request. + + * (OAuth 2 Provider) client_authentication_required method added to differentiate + between public and confidential clients. + + * (OAuth 2 Provider) rotate_refresh_token now indicates whether a new refresh + token should be generated during token refresh or if old should be kept. + + * (OAuth 2 Provider) returned JSON headers no longer include charset. + + * (OAuth 2 Provider) validate_authorizatoin_request now also includes the + internal request object in the returned dictionary. Note that this is + not meant to be relied upon heavily and its interface might change. + + * and many style and typo fixes. + + 0.6.0: OAuth 1 & 2 provider API refactor with breaking changes + + * All endpoint methods change contract to return 3 values instead of 4. The new + signature is `headers`, `body`, `status code` where the initial `redirect_uri` + has been relocated to its rightful place inside headers as `Location`. + + * OAuth 1 Access Token Endpoint has a new required validator method + `invalidate_request_token`. + + * OAuth 1 Authorization Endpoint now returns a 200 response instead of 302 on + `oob` callbacks. 0.5.1: OAuth 1 provider fix for incorrect token param in nonce validation. diff -Nru python-oauthlib-0.5.1/README.rst python-oauthlib-0.6.1/README.rst --- python-oauthlib-0.5.1/README.rst 2013-07-26 18:12:19.000000000 +0000 +++ python-oauthlib-0.6.1/README.rst 2014-01-20 11:08:30.000000000 +0000 @@ -11,19 +11,23 @@ OAuth often seems complicated and difficult-to-implement. There are several -prominent libraries for signing OAuth requests, but they all suffer from one or +prominent libraries for handling OAuth requests, but they all suffer from one or both of the following: 1. They predate the `OAuth 1.0 spec`_, AKA RFC 5849. -2. They assume the usage of a specific HTTP request library. +2. They predate the `OAuth 2.0 spec`_, AKA RFC 6749. +3. They assume the usage of a specific HTTP request library. .. _`OAuth 1.0 spec`: http://tools.ietf.org/html/rfc5849 +.. _`OAuth 2.0 spec`: http://tools.ietf.org/html/rfc6749 OAuthLib is a generic utility which implements the logic of OAuth without -assuming a specific HTTP request object. Use it to graft OAuth support onto your -favorite HTTP library. If you're a maintainer of such a library, write a thin +assuming a specific HTTP request object or web framework. Use it to graft OAuth +client support onto your favorite HTTP library, or provider support onto your +favourite web framework. If you're a maintainer of such a library, write a thin veneer on top of OAuthLib and get OAuth support for very little effort. + Documentation -------------- @@ -82,13 +86,47 @@ OAuthLib is yours to use and abuse according to the terms of the BSD license. Check the LICENSE file for full details. - - Changelog --------- -*OAuthLib is in active development, with most of OAuth1 complete and OAuth2 -already in the works.* +*OAuthLib is in active development, with the core of both OAuth 1 and 2 +completed, for providers as well as clients.* See `supported features`_ for +details. + +.. _`supported features`: http://oauthlib.readthedocs.org/en/latest/feature_matrix.html + +0.6.1: Draft revocation endpoint features and numerous fixes including + +* (OAuth 2 Provider) is_within_original_scope to check whether a refresh token + is trying to aquire a new set of scopes that are a subset of the original scope. + +* (OAuth 2 Provider) expires_in token lifetime can be set per request. + +* (OAuth 2 Provider) client_authentication_required method added to differentiate + between public and confidential clients. + +* (OAuth 2 Provider) rotate_refresh_token now indicates whether a new refresh + token should be generated during token refresh or if old should be kept. + +* (OAuth 2 Provider) returned JSON headers no longer include charset. + +* (OAuth 2 Provider) validate_authorizatoin_request now also includes the + internal request object in the returned dictionary. Note that this is + not meant to be relied upon heavily and its interface might change. + +* and many style and typo fixes. + +0.6.0: OAuth 1 & 2 provider API refactor with breaking changes + +* All endpoint methods change contract to return 3 values instead of 4. The new + signature is `headers`, `body`, `status code` where the initial `redirect_uri` + has been relocated to its rightful place inside headers as `Location`. + +* OAuth 1 Access Token Endpoint has a new required validator method + `invalidate_request_token`. + +* OAuth 1 Authorization Endpoint now returns a 200 response instead of 302 on + `oob` callbacks. 0.5.1: OAuth 1 provider fix for incorrect token param in nonce validation. diff -Nru python-oauthlib-0.5.1/debian/changelog python-oauthlib-0.6.1/debian/changelog --- python-oauthlib-0.5.1/debian/changelog 2013-09-03 20:53:27.000000000 +0000 +++ python-oauthlib-0.6.1/debian/changelog 2014-01-28 21:53:02.000000000 +0000 @@ -1,3 +1,11 @@ +python-oauthlib (0.6.1-1) unstable; urgency=medium + + * New upstream release + * debian/control + - Bumped Standards-Version to 3.9.5 (no changes needed) + + -- Daniele Tricoli Mon, 27 Jan 2014 00:45:09 +0100 + python-oauthlib (0.5.1-1) unstable; urgency=low * New upstream release diff -Nru python-oauthlib-0.5.1/debian/control python-oauthlib-0.6.1/debian/control --- python-oauthlib-0.5.1/debian/control 2013-09-03 19:32:14.000000000 +0000 +++ python-oauthlib-0.6.1/debian/control 2014-01-28 21:52:51.000000000 +0000 @@ -17,7 +17,7 @@ python3-mock, python3-nose, python3-setuptools -Standards-Version: 3.9.4 +Standards-Version: 3.9.5 X-Python-Version: >= 2.6 X-Python3-Version: >= 3.2 Homepage: https://github.com/idan/oauthlib diff -Nru python-oauthlib-0.5.1/oauthlib/__init__.py python-oauthlib-0.6.1/oauthlib/__init__.py --- python-oauthlib-0.5.1/oauthlib/__init__.py 2013-01-06 18:48:46.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/__init__.py 2014-01-20 11:08:46.000000000 +0000 @@ -0,0 +1,13 @@ +""" + oauthlib + ~~~~~~~~ + + A generic, spec-compliant, thorough implementation of the OAuth + request-signing logic. + + :copyright: (c) 2011 by Idan Gazit. + :license: BSD, see LICENSE for details. +""" + +__author__ = 'Idan Gazit ' +__version__ = '0.6.1' diff -Nru python-oauthlib-0.5.1/oauthlib/common.py python-oauthlib-0.6.1/oauthlib/common.py --- python-oauthlib-0.5.1/oauthlib/common.py 2013-07-26 18:05:17.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/common.py 2013-10-04 10:24:45.000000000 +0000 @@ -244,10 +244,9 @@ def add_params_to_qs(query, params): """Extend a query with a list of two-tuples.""" - if isinstance(query, dict): - queryparams = query.items() - else: - queryparams = urlparse.parse_qsl(query, keep_blank_values=True) + if isinstance(params, dict): + params = params.items() + queryparams = urlparse.parse_qsl(query, keep_blank_values=True) queryparams.extend(params) return urlencode(queryparams) @@ -383,7 +382,7 @@ @property def duplicate_params(self): seen_keys = collections.defaultdict(int) - all_keys = (p[0] for p in self.decoded_body or [] + self.uri_query_params) + all_keys = (p[0] for p in (self.decoded_body or []) + self.uri_query_params) for k in all_keys: seen_keys[k] += 1 return [k for k, c in seen_keys.items() if c > 1] diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/__init__.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/__init__.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/__init__.py 2013-06-20 13:50:07.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/__init__.py 2013-09-12 12:10:09.000000000 +0000 @@ -134,8 +134,7 @@ elif self.signature_method == SIGNATURE_RSA: sig = signature.sign_rsa_sha1(base_string, self.rsa_key) else: - sig = signature.sign_plaintext(self.client_secret, - self.resource_owner_secret) + raise ValueError('Invalid signature method.') log.debug("Signature: {0}".format(sig)) return sig diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/access_token.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/access_token.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/access_token.py 2013-07-26 18:06:22.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/access_token.py 2013-09-17 14:14:38.000000000 +0000 @@ -22,7 +22,7 @@ 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 + and return a proper response. See :doc:`/oauth1/validator` for details on which validator methods to implement for this endpoint. """ @@ -57,18 +57,17 @@ :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. + :returns: A tuple of 3 elements. + 1. A dict of headers to set on the response. + 2. The response body as a string. + 3. 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( + >>> h, b, s = endpoint.create_access_token_response( ... 'https://your.provider/access_token?foo=bar', ... headers={ ... 'Authorization': 'OAuth oauth_token=234lsdkf....' @@ -76,8 +75,6 @@ ... credentials={ ... 'my_specific': 'argument', ... }) - >>> u - None >>> h {'Content-Type': 'application/x-www-form-urlencoded'} >>> b @@ -106,11 +103,15 @@ request) if valid: token = self.create_access_token(request, credentials or {}) - return None, resp_headers, token, 200 + self.request_validator.invalidate_request_token( + request.client_key, + request.resource_owner_key, + request) + return resp_headers, token, 200 else: - return None, {}, None, 401 + return {}, None, 401 except errors.OAuth1Error as e: - return None, resp_headers, e.urlencoded, e.status_code + return resp_headers, e.urlencoded, e.status_code def validate_access_token_request(self, request): """Validate an access token request. diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/authorization.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/authorization.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/authorization.py 2013-07-26 18:06:22.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/authorization.py 2013-09-17 14:15:37.000000000 +0000 @@ -13,7 +13,10 @@ from .base import BaseEndpoint from .. import errors - +try: + from urllib import urlencode +except ImportError: + from urllib.parse import urlencode class AuthorizationEndpoint(BaseEndpoint): """An endpoint responsible for letting authenticated users authorize access @@ -30,7 +33,7 @@ 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 + See :doc:`/oauth1/validator` for details on which validator methods to implement for this endpoint. """ @@ -59,33 +62,48 @@ :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. + :returns: A tuple of 3 elements. + 1. A dict of headers to set on the response. + 2. The response body as a string. + 3. The response status code as an integer. + + If the callback URI tied to the current token is "oob", a response with + a 200 status code will be returned. In this case, it may be desirable to + modify the response to better display the verifier to the client. - An example of a valid request:: + An example of an authorization 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_....' - ... }, + >>> from oauthlib.oauth1 import AuthorizationEndpoint + >>> endpoint = AuthorizationEndpoint(your_validator) + >>> h, b, s = endpoint.create_authorization_response( + ... 'https://your.provider/authorize?oauth_token=...', ... credentials={ ... 'extra': 'argument', ... }) - >>> u - 'https://the.client/callback?oauth_verifier=...&mextra=argument' >>> h - {} + {'Location': 'https://the.client/callback?oauth_verifier=...&extra=argument'} >>> b - '' + None >>> s 302 + + An example of a request with an "oob" callback:: + + >>> from your_validator import your_validator + >>> from oauthlib.oauth1 import AuthorizationEndpoint + >>> endpoint = AuthorizationEndpoint(your_validator) + >>> h, b, s = endpoint.create_authorization_response( + ... 'https://your.provider/authorize?foo=bar', + ... credentials={ + ... 'extra': 'argument', + ... }) + >>> h + {'Content-Type': 'application/x-www-form-urlencoded'} + >>> b + 'oauth_verifier=...&extra=argument' + >>> s + 200 """ request = self._create_request(uri, http_method=http_method, body=body, headers=headers) @@ -104,11 +122,16 @@ description=('User granted access to realms outside of ' 'what the client may request.')) + verifier = self.create_verifier(request, credentials or {}) redirect_uri = self.request_validator.get_redirect_uri( request.resource_owner_key, request) - verifier = self.create_verifier(request, credentials or {}) - uri = add_params_to_uri(redirect_uri, verifier.items()) - return uri, {}, None, 302 + if redirect_uri == 'oob': + response_headers = {'Content-Type': 'application/x-www-form-urlencoded'} + response_body = urlencode(verifier) + return response_headers, response_body, 200 + else: + populated_redirect = add_params_to_uri(redirect_uri, verifier.items()) + return {'Location': populated_redirect}, None, 302 def get_realms_and_credentials(self, uri, http_method='GET', body=None, headers=None): diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/request_token.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/request_token.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/request_token.py 2013-07-09 15:51:18.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/request_token.py 2013-09-17 14:15:10.000000000 +0000 @@ -22,7 +22,7 @@ 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 + and return a proper response. See :doc:`/oauth1/validator` for details on which validator methods to implement for this endpoint. """ @@ -51,18 +51,17 @@ :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. + :returns: A tuple of 3 elements. + 1. A dict of headers to set on the response. + 2. The response body as a string. + 3. 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( + >>> h, b, s = endpoint.create_request_token_response( ... 'https://your.provider/request_token?foo=bar', ... headers={ ... 'Authorization': 'OAuth realm=movies user, oauth_....' @@ -70,8 +69,6 @@ ... credentials={ ... 'my_specific': 'argument', ... }) - >>> u - None >>> h {'Content-Type': 'application/x-www-form-urlencoded'} >>> b @@ -100,11 +97,11 @@ request) if valid: token = self.create_request_token(request, credentials or {}) - return None, resp_headers, token, 200 + return resp_headers, token, 200 else: - return None, {}, None, 401 + return {}, None, 401 except errors.OAuth1Error as e: - return None, resp_headers, e.urlencoded, e.status_code + return resp_headers, e.urlencoded, e.status_code def validate_request_token_request(self, request): """Validate a request token request. diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/resource.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/resource.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/endpoints/resource.py 2013-07-26 18:06:22.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/endpoints/resource.py 2013-09-17 14:15:21.000000000 +0000 @@ -23,7 +23,7 @@ view. If invalid create and return an error response directly from the decorator. - See :doc:`validator` for details on which validator methods to implement + See :doc:`/oauth1/validator` for details on which validator methods to implement for this endpoint. An example decorator:: diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/request_validator.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/request_validator.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/request_validator.py 2013-07-26 18:06:22.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/request_validator.py 2013-09-13 09:36:22.000000000 +0000 @@ -59,7 +59,7 @@ - validate_realms - validate_verifier - Method used to retrieve sensitive information from storage. + Methods used to retrieve sensitive information from storage. The following methods must be implemented: - get_client_secret @@ -67,6 +67,13 @@ - get_access_token_secret - get_rsa_key + Methods used to save credentials. + The following methods must be implemented: + + - save_request_token + - save_verifier + - save_access_token + To prevent timing attacks it is necessary to not exit early even if the client key or resource owner key is invalid. Instead dummy values should be used during the remaining verification process. It is very important @@ -360,12 +367,16 @@ raise NotImplementedError("Subclasses must implement this function.") def get_redirect_uri(self, token, request): - """Get the redirect uri associated with a request token. + """Get the redirect URI associated with a request token. :param token: The request token string. :param request: An oauthlib.common.Request object. :returns: The redirect URI associated with the request token. + It may be desirable to return a custom URI if the redirect is set to "oob". + In this case, the user will be redirected to the returned URI and at that + endpoint the verifier can be displayed. + This method is used by * AuthorizationEndpoint @@ -394,6 +405,20 @@ """ raise NotImplementedError("Subclasses must implement this function.") + def invalidate_request_token(self, client_key, request_token, request): + """Invalidates a used request token. + + :param client_key: The client/consumer key. + :param request_token: The request token string. + :param request: An oauthlib.common.Request object. + :returns: The rsa public key as a string. + + This method is used by + + * AccessTokenEndpoint + """ + raise NotImplementedError("Subclasses must implement this function.") + def validate_client_key(self, client_key, request): """Validates that supplied client key is a registered and valid client. @@ -713,7 +738,7 @@ raise NotImplementedError("Subclasses must implement this function.") def save_request_token(self, token, request): - """Save an OAuth1 access token. + """Save an OAuth1 request token. :param token: A dict with token credentials. :param request: An oauthlib.common.Request object. @@ -736,7 +761,7 @@ """Associate an authorization verifier with a request token. :param token: A request token string. - :param verifier A dictionary containing the oauth_verifier and + :param verifier A dictionary containing the oauth_verifier and oauth_token :param request: An oauthlib.common.Request object. diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/signature.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/signature.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/signature.py 2013-05-20 09:22:10.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/signature.py 2013-09-12 10:40:10.000000000 +0000 @@ -133,8 +133,8 @@ # # .. _`RFC3986`: http://tools.ietf.org/html/rfc3986 - if not scheme: - raise ValueError('uri must include a scheme') + if not scheme or not netloc: + raise ValueError('uri must include a scheme and netloc') # Per `RFC 2616 section 5.1.2`_: # diff -Nru python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/utils.py python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/utils.py --- python-oauthlib-0.5.1/oauthlib/oauth1/rfc5849/utils.py 2013-07-09 16:50:36.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth1/rfc5849/utils.py 2013-09-12 10:51:57.000000000 +0000 @@ -66,17 +66,6 @@ return unquote(u) -def urlencode(query): - """Encode a sequence of two-element tuples or dictionary into a URL query string. - - Operates using an OAuth-safe escape() method, in contrast to urllib.urlencode. - """ - # Convert dictionaries to list of tuples - if isinstance(query, dict): - query = query.items() - return "&".join(['='.join([escape(k), escape(v)]) for k, v in query]) - - def parse_keqv_list(l): """A unicode-safe version of urllib2.parse_keqv_list""" # With Python 2.6, parse_http_list handles unicode fine diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/__init__.py python-oauthlib-0.6.1/oauthlib/oauth2/__init__.py --- python-oauthlib-0.5.1/oauthlib/oauth2/__init__.py 2013-05-30 10:44:57.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/__init__.py 2013-09-17 11:32:40.000000000 +0000 @@ -17,6 +17,7 @@ from .rfc6749.endpoints import AuthorizationEndpoint from .rfc6749.endpoints import TokenEndpoint from .rfc6749.endpoints import ResourceEndpoint +from .rfc6749.endpoints import RevocationEndpoint from .rfc6749.endpoints import Server from .rfc6749.endpoints import WebApplicationServer from .rfc6749.endpoints import MobileApplicationServer diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/__init__.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/__init__.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/__init__.py 2013-05-30 10:49:32.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/__init__.py 2013-08-13 21:12:37.000000000 +0000 @@ -43,7 +43,7 @@ if not endpoint.available: e = TemporarilyUnavailableError() log.info('Endpoint unavailable, ignoring request %s.' % uri) - return None, {}, e.json, 503 + return {}, e.json, 503 if endpoint.catch_errors: try: @@ -55,7 +55,7 @@ except Exception as e: error = ServerError() log.warning('Exception caught while processing request, %s.' % e) - return None, {}, error.json, 500 + return {}, error.json, 500 else: return f(endpoint, uri, *args, **kwargs) return wrapper diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/clients/base.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/clients/base.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/clients/base.py 2013-05-31 14:13:49.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/clients/base.py 2013-10-04 09:06:13.000000000 +0000 @@ -8,7 +8,7 @@ This module is an implementation of various logic needed for consuming OAuth 2.0 RFC6749. """ -import datetime +import time from oauthlib.oauth2.rfc6749 import tokens from oauthlib.oauth2.rfc6749.parameters import prepare_token_request @@ -114,7 +114,7 @@ if not self.access_token: raise ValueError("Missing access token.") - if self._expires_at and self._expires_at < datetime.datetime.now(): + if self._expires_at and self._expires_at < time.time(): raise TokenExpiredError() return case_insensitive_token_types[self.token_type.lower()](uri, http_method, body, @@ -184,8 +184,10 @@ if 'expires_in' in response: self.expires_in = response.get('expires_in') - self._expires_at = datetime.datetime.now() + datetime.timedelta( - seconds=int(self.expires_in)) + self._expires_at = time.time() + int(self.expires_in) + + if 'expires_at' in response: + self._expires_at = int(response.get('expires_at')) if 'code' in response: self.code = response.get('code') diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/__init__.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/__init__.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/__init__.py 2013-05-30 10:36:32.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/__init__.py 2013-09-17 11:33:00.000000000 +0000 @@ -12,6 +12,7 @@ from .authorization import AuthorizationEndpoint from .token import TokenEndpoint from .resource import ResourceEndpoint +from .revocation import RevocationEndpoint from .pre_configured import Server from .pre_configured import WebApplicationServer from .pre_configured import MobileApplicationServer diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/base.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/base.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/base.py 2013-05-30 10:35:04.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/base.py 2013-08-13 21:12:37.000000000 +0000 @@ -44,7 +44,7 @@ if not endpoint.available: e = TemporarilyUnavailableError() log.info('Endpoint unavailable, ignoring request %s.' % uri) - return None, {}, e.json, 503 + return {}, e.json, 503 if endpoint.catch_errors: try: @@ -56,7 +56,7 @@ except Exception as e: error = ServerError() log.warning('Exception caught while processing request, %s.' % e) - return None, {}, error.json, 500 + return {}, error.json, 500 else: return f(endpoint, uri, *args, **kwargs) return wrapper diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py 2013-07-09 15:55:54.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py 2013-09-19 08:53:04.000000000 +0000 @@ -18,13 +18,26 @@ from .authorization import AuthorizationEndpoint from .token import TokenEndpoint from .resource import ResourceEndpoint +from .revocation import RevocationEndpoint -class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint): +class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, + RevocationEndpoint): """An all-in-one endpoint featuring all four major grant types.""" def __init__(self, request_validator, token_expires_in=None, token_generator=None, *args, **kwargs): + """Construct a new all-grants-in-one server. + + :param request_validator: An implementation of + oauthlib.oauth2.RequestValidator. + :param token_expires_in: An int or a function to generate a token + expiration offset (in seconds) given a + oauthlib.common.Request object. + :param token_generator: A function to generate a token from a request. + :param kwargs: Extra parameters to pass to authorization-, + token-, resource-, and revocation-endpoint constructors. + """ auth_grant = AuthorizationCodeGrant(request_validator) implicit_grant = ImplicitGrant(request_validator) password_grant = ResourceOwnerPasswordCredentialsGrant(request_validator) @@ -48,19 +61,25 @@ default_token_type=bearer) ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) + RevocationEndpoint.__init__(self, request_validator) -class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint): +class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, + RevocationEndpoint): """An all-in-one endpoint featuring Authorization code grant and Bearer tokens.""" def __init__(self, request_validator, token_generator=None, token_expires_in=None, **kwargs): """Construct a new web application server. - :param request_validator: An implementation of oauthlib.oauth2.RequestValidator. + :param request_validator: An implementation of + oauthlib.oauth2.RequestValidator. + :param token_expires_in: An int or a function to generate a token + expiration offset (in seconds) given a + oauthlib.common.Request object. :param token_generator: A function to generate a token from a request. - :param kwargs: Extra parameters to pass to authorization endpoint, - token endpoint and resource endpoint constructors. + :param kwargs: Extra parameters to pass to authorization-, + token-, resource-, and revocation-endpoint constructors. """ auth_grant = AuthorizationCodeGrant(request_validator) refresh_grant = RefreshTokenGrant(request_validator) @@ -77,13 +96,26 @@ default_token_type=bearer) ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) + RevocationEndpoint.__init__(self, request_validator) -class MobileApplicationServer(AuthorizationEndpoint, ResourceEndpoint): +class MobileApplicationServer(AuthorizationEndpoint, ResourceEndpoint, + RevocationEndpoint): """An all-in-one endpoint featuring Implicit code grant and Bearer tokens.""" def __init__(self, request_validator, token_generator=None, token_expires_in=None, **kwargs): + """Construct a new implicit grant server. + + :param request_validator: An implementation of + oauthlib.oauth2.RequestValidator. + :param token_expires_in: An int or a function to generate a token + expiration offset (in seconds) given a + oauthlib.common.Request object. + :param token_generator: A function to generate a token from a request. + :param kwargs: Extra parameters to pass to authorization-, + token-, resource-, and revocation-endpoint constructors. + """ implicit_grant = ImplicitGrant(request_validator) bearer = BearerToken(request_validator, token_generator, expires_in=token_expires_in) @@ -92,13 +124,27 @@ default_token_type=bearer) ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) + RevocationEndpoint.__init__(self, request_validator, + supported_token_types=['access_token']) -class LegacyApplicationServer(TokenEndpoint, ResourceEndpoint): +class LegacyApplicationServer(TokenEndpoint, ResourceEndpoint, + RevocationEndpoint): """An all-in-one endpoint featuring Resource Owner Password Credentials grant and Bearer tokens.""" def __init__(self, request_validator, token_generator=None, token_expires_in=None, **kwargs): + """Construct a resource owner password credentials grant server. + + :param request_validator: An implementation of + oauthlib.oauth2.RequestValidator. + :param token_expires_in: An int or a function to generate a token + expiration offset (in seconds) given a + oauthlib.common.Request object. + :param token_generator: A function to generate a token from a request. + :param kwargs: Extra parameters to pass to authorization-, + token-, resource-, and revocation-endpoint constructors. + """ password_grant = ResourceOwnerPasswordCredentialsGrant(request_validator) refresh_grant = RefreshTokenGrant(request_validator) bearer = BearerToken(request_validator, token_generator, @@ -111,13 +157,26 @@ default_token_type=bearer) ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) + RevocationEndpoint.__init__(self, request_validator) -class BackendApplicationServer(TokenEndpoint, ResourceEndpoint): +class BackendApplicationServer(TokenEndpoint, ResourceEndpoint, + RevocationEndpoint): """An all-in-one endpoint featuring Client Credentials grant and Bearer tokens.""" def __init__(self, request_validator, token_generator=None, token_expires_in=None, **kwargs): + """Construct a client credentials grant server. + + :param request_validator: An implementation of + oauthlib.oauth2.RequestValidator. + :param token_expires_in: An int or a function to generate a token + expiration offset (in seconds) given a + oauthlib.common.Request object. + :param token_generator: A function to generate a token from a request. + :param kwargs: Extra parameters to pass to authorization-, + token-, resource-, and revocation-endpoint constructors. + """ credentials_grant = ClientCredentialsGrant(request_validator) bearer = BearerToken(request_validator, token_generator, expires_in=token_expires_in) @@ -126,3 +185,5 @@ default_token_type=bearer) ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) + RevocationEndpoint.__init__(self, request_validator, + supported_token_types=['access_token']) diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/revocation.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/revocation.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/endpoints/revocation.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/endpoints/revocation.py 2013-09-17 12:42:29.000000000 +0000 @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals + +""" +oauthlib.oauth2.rfc6749.endpoint.revocation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An implementation of the OAuth 2 `Token Revocation`_ spec (draft 11). + +.. _`Token Revocation`: http://tools.ietf.org/html/draft-ietf-oauth-revocation-11 +""" + +from oauthlib.common import Request, log + +from .base import BaseEndpoint, catch_errors_and_unavailability +from ..errors import InvalidClientError, UnsupportedTokenTypeError +from ..errors import InvalidRequestError, OAuth2Error + + +class RevocationEndpoint(BaseEndpoint): + """Token revocation endpoint. + + Endpoint used by authenticated clients to revoke access and refresh tokens. + Commonly this will be part of the Authorization Endpoint. + """ + + valid_token_types = ('access_token', 'refresh_token') + + @property + def supported_token_types(self): + return self._supported_token_types + + def __init__(self, request_validator, supported_token_types=None): + BaseEndpoint.__init__(self) + self.request_validator = request_validator + self._supported_token_types = ( + supported_token_types or self.valid_token_types) + + @catch_errors_and_unavailability + def create_revocation_response(self, uri, http_method='POST', body=None, + headers=None): + """Revoke supplied access or refresh token. + + + The authorization server responds with HTTP status code 200 if the + token has been revoked sucessfully or if the client submitted an + invalid token. + + Note: invalid tokens do not cause an error response since the client + cannot handle such an error in a reasonable way. Moreover, the purpose + of the revocation request, invalidating the particular token, is + already achieved. + + The content of the response body is ignored by the client as all + necessary information is conveyed in the response code. + + An invalid token type hint value is ignored by the authorization server + and does not influence the revocation response. + """ + request = Request(uri, http_method=http_method, body=body, headers=headers) + try: + self.validate_revocation_request(request) + log.debug('Token revocation valid for %r.', request) + except OAuth2Error as e: + log.debug('Client error during validation of %r. %r.', request, e) + return {}, e.json, e.status_code + + self.request_validator.revoke_token(request.token, + request.token_type_hint, request) + response_body = request.callback + '()' if request.callback else None + return {}, response_body, 200 + + def validate_revocation_request(self, request): + """Ensure the request is valid. + + The client constructs the request by including the following parameters + using the "application/x-www-form-urlencoded" format in the HTTP + request entity-body: + + token (REQUIRED). The token that the client wants to get revoked. + + token_type_hint (OPTIONAL). A hint about the type of the token + submitted for revocation. Clients MAY pass this parameter in order to + help the authorization server to optimize the token lookup. If the + server is unable to locate the token using the given hint, it MUST + extend its search accross all of its supported token types. An + authorization server MAY ignore this parameter, particularly if it is + able to detect the token type automatically. This specification + defines two such values: + + * access_token: An Access Token as defined in [RFC6749], + `section 1.4`_ + + * refresh_token: A Refresh Token as defined in [RFC6749], + `section 1.5`_ + + Specific implementations, profiles, and extensions of this + specification MAY define other values for this parameter using + the registry defined in `Section 4.1.2`_. + + The client also includes its authentication credentials as described in + `Section 2.3`_. of [`RFC6749`_]. + + .. _`section 1.4`: http://tools.ietf.org/html/rfc6749#section-1.4 + .. _`section 1.5`: http://tools.ietf.org/html/rfc6749#section-1.5 + .. _`section 2.3`: http://tools.ietf.org/html/rfc6749#section-2.3 + .. _`Section 4.1.2`: http://tools.ietf.org/html/draft-ietf-oauth-revocation-11#section-4.1.2 + .. _`RFC6749`: http://tools.ietf.org/html/rfc6749 + """ + if not request.token: + raise InvalidRequestError(request=request, + description='Missing token parameter.') + + if not self.request_validator.authenticate_client(request): + raise InvalidClientError(request=request) + + if (request.token_type_hint in self.valid_token_types and + request.token_type_hint not in self.supported_token_types): + raise UnsupportedTokenTypeError(request=request) diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/errors.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/errors.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/errors.py 2013-06-20 14:01:27.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/errors.py 2013-09-17 11:50:14.000000000 +0000 @@ -210,6 +210,14 @@ error = 'unsupported_grant_type' +class UnsupportedTokenTypeError(OAuth2Error): + """The authorization server does not support the revocation of the + presented token type. I.e. the client tried to revoke an access token + on a server not supporting this feature. + """ + error = 'unsupported_token_type' + + def raise_from_error(error, params=None): import inspect import sys diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py 2013-05-30 10:48:18.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py 2013-11-04 12:00:01.000000000 +0000 @@ -131,7 +131,7 @@ :param request: oauthlib.commong.Request :param token_handler: A token handler instace, for example of type oauthlib.oauth2.BearerToken. - :returns: uri, headers, body, status + :returns: headers, body, status :raises: FatalClientError on invalid redirect URI or client id. ValueError if scopes are not set on the request object. @@ -203,12 +203,12 @@ except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) request.redirect_uri = request.redirect_uri or self.error_uri - return common.add_params_to_uri(request.redirect_uri, e.twotuples), None, None, e.status_code + return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples)}, None, 302 grant = self.create_authorization_code(request) log.debug('Saving grant %r for %r.', grant, request) self.request_validator.save_authorization_code(request.client_id, grant, request) - return common.add_params_to_uri(request.redirect_uri, grant.items()), None, None, 302 + return {'Location': common.add_params_to_uri(request.redirect_uri, grant.items())}, None, 302 def create_token_response(self, request, token_handler): """Validate the authorization code. @@ -220,7 +220,7 @@ code is bound to the client identifier and redirection URI. """ headers = { - 'Content-Type': 'application/json;charset=UTF-8', + 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', } @@ -229,12 +229,12 @@ log.debug('Token request validation ok for %r.', request) except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) - return None, headers, e.json, e.status_code + return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=True) self.request_validator.invalidate_authorization_code( request.client_id, request.code, request) - return None, headers, json.dumps(token), 200 + return headers, json.dumps(token), 200 def validate_authorization_request(self, request): """Check the authorization request for normal and fatal errors. @@ -326,6 +326,7 @@ 'redirect_uri': request.redirect_uri, 'response_type': request.response_type, 'state': request.state, + 'request': request, } def validate_token_request(self, request): @@ -335,32 +336,34 @@ if request.code is None: raise errors.InvalidRequestError( - description='Missing code parameter.', request=request) + description='Missing code parameter.', request=request) for param in ('client_id', 'grant_type', 'redirect_uri'): if param in request.duplicate_params: raise errors.InvalidRequestError(state=request.state, - description='Duplicate %s parameter.' % param, - request=request) + description='Duplicate %s parameter.' % param, + request=request) - # If the client type is confidential or the client was issued client - # credentials (or assigned other authentication requirements), the - # client MUST authenticate with the authorization server as described - # in Section 3.2.1. - # http://tools.ietf.org/html/rfc6749#section-3.2.1 - if not self.request_validator.authenticate_client(request): - # REQUIRED, if the client is not authenticating with the - # authorization server as described in Section 3.2.1. + if self.request_validator.client_authentication_required(request): + # If the client type is confidential or the client was issued client + # credentials (or assigned other authentication requirements), the + # client MUST authenticate with the authorization server as described + # in Section 3.2.1. # http://tools.ietf.org/html/rfc6749#section-3.2.1 - if not self.request_validator.authenticate_client_id( - request.client_id, request): + if not self.request_validator.authenticate_client(request): log.debug('Client authentication failed, %r.', request) raise errors.InvalidClientError(request=request) - else: - if not hasattr(request.client, 'client_id'): - raise NotImplementedError('Authenticate client must set the ' - 'request.client.client_id attribute ' - 'in authenticate_client.') + elif not self.request_validator.authenticate_client_id(request.client_id, request): + # REQUIRED, if the client is not authenticating with the + # authorization server as described in Section 3.2.1. + # http://tools.ietf.org/html/rfc6749#section-3.2.1 + log.debug('Client authentication failed, %r.', request) + raise errors.InvalidClientError(request=request) + + if not hasattr(request.client, 'client_id'): + raise NotImplementedError('Authenticate client must set the ' + 'request.client.client_id attribute ' + 'in authenticate_client.') # Ensure client is authorized use of this grant type self.validate_grant_type(request) @@ -368,7 +371,7 @@ # REQUIRED. The authorization code received from the # authorization server. if not self.request_validator.validate_code(request.client_id, - request.code, request.client, request): + request.code, request.client, request): log.debug('Client, %r (%r), is not allowed access to scopes %r.', request.client_id, request.client, request.scopes) raise errors.InvalidGrantError(request=request) @@ -380,8 +383,8 @@ # REQUIRED, if the "redirect_uri" parameter was included in the # authorization request as described in Section 4.1.1, and their # values MUST be identical. - if not self.request_validator.confirm_redirect_uri(request.client_id, - request.code, request.redirect_uri, request.client): + if not self.request_validator.confirm_redirect_uri(request.client_id, request.code, + request.redirect_uri, request.client): log.debug('Redirect_uri (%r) invalid for client %r (%r).', request.redirect_uri, request.client_id, request.client) raise errors.AccessDeniedError(request=request) diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py 2013-06-20 13:51:05.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py 2014-01-20 10:40:48.000000000 +0000 @@ -61,17 +61,22 @@ .. _`Section 5.1`: http://tools.ietf.org/html/rfc6749#section-5.1 .. _`Section 5.2`: http://tools.ietf.org/html/rfc6749#section-5.2 """ + headers = { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', + } try: log.debug('Validating access token request, %r.', request) self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request. %s.', e) - return None, {}, e.json, e.status_code + return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=False) log.debug('Issuing token to client id %r (%r), %r.', request.client_id, request.client, token) - return None, {}, json.dumps(token), 200 + return headers, json.dumps(token), 200 def validate_token_request(self, request): if not getattr(request, 'grant_type'): diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/implicit.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/implicit.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/implicit.py 2013-05-30 10:48:40.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/implicit.py 2013-11-04 12:00:01.000000000 +0000 @@ -220,12 +220,12 @@ # http://tools.ietf.org/html/rfc6749#appendix-B except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) - return common.add_params_to_uri(request.redirect_uri, e.twotuples, - fragment=True), {}, None, e.status_code + return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples, + fragment=True)}, None, 302 token = token_handler.create_token(request, refresh_token=False) - return common.add_params_to_uri(request.redirect_uri, token.items(), - fragment=True), {}, None, 302 + return {'Location': common.add_params_to_uri(request.redirect_uri, token.items(), + fragment=True)}, None, 302 def validate_authorization_request(self, request): return self.validate_token_request(request) @@ -331,4 +331,5 @@ 'redirect_uri': request.redirect_uri, 'response_type': request.response_type, 'state': request.state, + 'request': request, } diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py 2013-06-21 10:24:40.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py 2014-01-20 10:11:11.000000000 +0000 @@ -46,7 +46,7 @@ .. _`Section 5.2`: http://tools.ietf.org/html/rfc6749#section-5.2 """ headers = { - 'Content-Type': 'application/json;charset=UTF-8', + 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', } @@ -54,13 +54,13 @@ log.debug('Validating refresh token request, %r.', request) self.validate_token_request(request) except errors.OAuth2Error as e: - return None, headers, e.json, e.status_code + return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=self.issue_new_refresh_tokens) log.debug('Issuing new token to client id %r (%r), %r.', request.client_id, request.client, token) - return None, headers, json.dumps(token), 200 + return headers, json.dumps(token), 200 def validate_token_request(self, request): # REQUIRED. Value MUST be set to "refresh_token". @@ -69,8 +69,8 @@ if request.refresh_token is None: raise errors.InvalidRequestError( - description='Missing refresh token parameter.', - request=request) + description='Missing refresh token parameter.', + request=request) # Because refresh tokens are typically long-lasting credentials used to # request additional access tokens, the refresh token is bound to the @@ -79,10 +79,14 @@ # authentication requirements), the client MUST authenticate with the # authorization server as described in Section 3.2.1. # http://tools.ietf.org/html/rfc6749#section-3.2.1 - log.debug('Authenticating client, %r.', request) - if not self.request_validator.authenticate_client(request): - log.debug('Invalid client (%r), denying access.', request) - raise errors.InvalidClientError(request=request, status_code=401) + if self.request_validator.client_authentication_required(request): + log.debug('Authenticating client, %r.', request) + if not self.request_validator.authenticate_client(request): + log.debug('Invalid client (%r), denying access.', request) + raise errors.InvalidClientError(request=request, status_code=401) + elif not self.request_validator.authenticate_client_id(request.client_id, request): + log.debug('Client authentication failed, %r.', request) + raise errors.InvalidClientError(request=request) # Ensure client is authorized use of this grant type self.validate_grant_type(request) @@ -102,7 +106,9 @@ if request.scope: request.scopes = utils.scope_to_list(request.scope) - if not all((s in original_scopes for s in request.scopes)): + if (not all((s in original_scopes for s in request.scopes)) + and not self.request_validator.is_within_original_scope( + request.scopes, request.refresh_token, request)): log.debug('Refresh token %s lack requested scopes, %r.', request.refresh_token, request.scopes) raise errors.InvalidScopeError( diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py 2013-05-30 10:49:13.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py 2013-11-04 12:00:01.000000000 +0000 @@ -69,8 +69,7 @@ def __init__(self, request_validator=None): self.request_validator = request_validator or RequestValidator() - def create_token_response(self, request, token_handler, - require_authentication=True): + def create_token_response(self, request, token_handler): """Return token or error in json format. If the access token request is valid and authorized, the @@ -83,34 +82,29 @@ .. _`Section 5.2`: http://tools.ietf.org/html/rfc6749#section-5.2 """ headers = { - 'Content-Type': 'application/json;charset=UTF-8', - 'Cache-Control': 'no-store', - 'Pragma': 'no-cache', + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', } try: - if require_authentication: + if self.request_validator.client_authentication_required(request): log.debug('Authenticating client, %r.', request) if not self.request_validator.authenticate_client(request): log.debug('Client authentication failed, %r.', request) raise errors.InvalidClientError(request=request) - else: - if not hasattr(request.client, 'client_id'): - raise NotImplementedError( - 'Authenticate client must set the ' - 'request.client.client_id attribute ' - 'in authenticate_client.') - else: - log.debug('Client authentication disabled, %r.', request) + elif not self.request_validator.authenticate_client_id(request.client_id, request): + log.debug('Client authentication failed, %r.', request) + raise errors.InvalidClientError(request=request) log.debug('Validating access token request, %r.', request) self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request, %s.', e) - return None, headers, e.json, e.status_code + return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=True) log.debug('Issuing token %r to client id %r (%r) and username %s.', token, request.client_id, request.client, request.username) - return None, headers, json.dumps(token), 200 + return headers, json.dumps(token), 200 def validate_token_request(self, request): """ diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/parameters.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/parameters.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/parameters.py 2013-05-31 14:13:49.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/parameters.py 2013-11-04 12:00:01.000000000 +0000 @@ -11,6 +11,7 @@ """ import json +import time try: import urlparse except ImportError: @@ -222,6 +223,9 @@ if 'scope' in params: params['scope'] = scope_to_list(params['scope']) + if 'expires_in' in params: + params['expires_at'] = time.time() + int(params['expires_in']) + if state and params.get('state', None) != state: raise ValueError("Mismatching or missing state in params.") @@ -272,7 +276,7 @@ .. code-block:: http HTTP/1.1 200 OK - Content-Type: application/json;charset=UTF-8 + Content-Type: application/json Cache-Control: no-store Pragma: no-cache @@ -294,6 +298,9 @@ if 'scope' in params: params['scope'] = scope_to_list(params['scope']) + if 'expires_in' in params: + params['expires_at'] = time.time() + int(params['expires_in']) + validate_token_parameters(params, scope) return params diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/request_validator.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/request_validator.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/request_validator.py 2013-06-21 10:25:55.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/request_validator.py 2014-01-20 10:11:11.000000000 +0000 @@ -12,6 +12,34 @@ class RequestValidator(object): + def client_authentication_required(self, request, *args, **kwargs): + """Determine if client authentication is required for current request. + + According to the rfc6749, client authentication is required in the following cases: + - Resource Owner Password Credentials Grant, when Client type is Confidential or when + Client was issued client credentials or whenever Client provided client + authentication, see `Section 4.3.2`_. + - Authorization Code Grant, when Client type is Confidential or when Client was issued + client credentials or whenever Client provided client authentication, + see `Section 4.1.3`_. + - Refresh Token Grant, when Client type is Confidential or when Client was issued + client credentials or whenever Client provided client authentication, see + `Section 6`_ + + :param request: oauthlib.common.Request + :rtype: True or False + + Method is used by: + - Authorization Code Grant + - Resource Owner Password Credentials Grant + - Refresh Token Grant + + .. _`Section 4.3.2`: http://tools.ietf.org/html/rfc6749#section-4.3.2 + .. _`Section 4.1.3`: http://tools.ietf.org/html/rfc6749#section-4.1.3 + .. _`Section 6`: http://tools.ietf.org/html/rfc6749#section-6 + """ + return True + def authenticate_client(self, request, *args, **kwargs): """Authenticate client through means outside the OAuth 2 spec. @@ -60,7 +88,8 @@ """ raise NotImplementedError('Subclasses must implement this method.') - def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **kwargs): + def confirm_redirect_uri(self, client_id, code, redirect_uri, client, + *args, **kwargs): """Ensure client is authorized to redirect to the redirect_uri requested. If the client specifies a redirect_uri when obtaining code then @@ -122,6 +151,27 @@ """ raise NotImplementedError('Subclasses must implement this method.') + def is_within_original_scope(self, request_scopes, refresh_token, request, *args, **kwargs): + """Check if requested scopes are within a scope of the refresh token. + + When access tokens are refreshed the scope of the new token + needs to be within the scope of the original token. This is + ensured by checking that all requested scopes strings are on + the list returned by the get_original_scopes. If this check + fails, is_within_original_scope is called. The method can be + used in situations where returning all valid scopes from the + get_original_scopes is not practical. + + :param request_scopes: A list of scopes that were requested by client + :param refresh_token: Unicode refresh_token + :param request: The HTTP Request (oauthlib.common.Request) + :rtype: True or False + + Method is used by: + - Refresh token grant + """ + return False + def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs): """Invalidate an authorization code after use. @@ -134,6 +184,33 @@ """ raise NotImplementedError('Subclasses must implement this method.') + def revoke_token(self, token, token_type_hint, request, *args, **kwargs): + """Revoke an access or refresh token. + + :param token: The token string. + :param token_type_hint: access_token or refresh_token. + :param request: The HTTP Request (oauthlib.common.Request) + + Method is used by: + - Revocation Endpoint + """ + raise NotImplementedError('Subclasses must implement this method.') + + def rotate_refresh_token(self, request): + """Determine whether to rotate the refresh token. Default, yes. + + When access tokens are refreshed the old refresh token can be kept + or replaced with a new one (rotated). Return True to rotate and + and False for keeping original. + + :param request: oauthlib.common.Request + :rtype: True or False + + Method is used by: + - Refresh Token Grant + """ + return True + def save_authorization_code(self, client_id, code, request, *args, **kwargs): """Persist the authorization_code. @@ -222,7 +299,7 @@ convenient, access control. A powerful way to use scopes would mimic UNIX ACLs and see a scope - as a group with certain privilegues. For a restful API these might + as a group with certain privileges. For a restful API these might map to HTTP verbs instead of read, write and execute. Note, the request.user attribute can be set to the resource owner diff -Nru python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/tokens.py python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/tokens.py --- python-oauthlib-0.5.1/oauthlib/oauth2/rfc6749/tokens.py 2013-05-30 08:45:51.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib/oauth2/rfc6749/tokens.py 2013-09-19 09:14:17.000000000 +0000 @@ -22,14 +22,20 @@ from . import utils -def prepare_mac_header(token, uri, key, http_method, nonce=None, headers=None, - body=None, ext='', hash_algorithm='hmac-sha-1', issue_time=None, - draft=0): +def prepare_mac_header(token, uri, key, http_method, + nonce=None, + headers=None, + body=None, + ext='', + hash_algorithm='hmac-sha-1', + issue_time=None, + draft=0): """Add an `MAC Access Authentication`_ signature to headers. - Unlike OAuth 1, this HMAC signature does not require inclusion of the request - payload/body, neither does it use a combination of client_secret and - token_secret but rather a mac_key provided together with the access token. + Unlike OAuth 1, this HMAC signature does not require inclusion of the + request payload/body, neither does it use a combination of client_secret + and token_secret but rather a mac_key provided together with the access + token. Currently two algorithms are supported, "hmac-sha-1" and "hmac-sha-256", `extension algorithms`_ are not supported. @@ -48,7 +54,7 @@ :param http_method: HTTP Request method. :param key: MAC given provided by token endpoint. :param hash_algorithm: HMAC algorithm provided by token endpoint. - :param issue_time: Time when the MAC credentials were issues as a datetime object. + :param issue_time: Time when the MAC credentials were issued (datetime). :param draft: MAC authentication specification version. :return: headers dictionary with the authorization field added. """ @@ -78,7 +84,8 @@ # Hash the body/payload if body is not None and draft == 0: - bodyhash = b2a_base64(h(body.encode('utf-8')).digest())[:-1].decode('utf-8') + body = body.encode('utf-8') + bodyhash = b2a_base64(h(body).digest())[:-1].decode('utf-8') else: bodyhash = '' @@ -173,18 +180,25 @@ class BearerToken(TokenBase): def __init__(self, request_validator=None, token_generator=None, - expires_in=None): + expires_in=None): self.request_validator = request_validator self.token_generator = token_generator or random_token_generator self.expires_in = expires_in or 3600 def create_token(self, request, refresh_token=False): """Create a BearerToken, by default without refresh token.""" + + if callable(self.expires_in): + expires_in = self.expires_in(request) + else: + expires_in = self.expires_in + token = { 'access_token': self.token_generator(request), - 'expires_in': self.expires_in, + 'expires_in': expires_in, 'token_type': 'Bearer', } + if request.scopes is not None: token['scope'] = ' '.join(request.scopes) @@ -192,7 +206,11 @@ token['state'] = request.state if refresh_token: - token['refresh_token'] = self.token_generator( + if (request.refresh_token and + not self.request_validator.rotate_refresh_token(request)): + token['refresh_token'] = request.refresh_token + else: + token['refresh_token'] = self.token_generator( request, refresh_token=True) token.update(request.extra_credentials or {}) @@ -206,7 +224,8 @@ token = request.headers.get('Authorization')[7:] else: token = request.access_token - return self.request_validator.validate_bearer_token(token, request.scopes, request) + return self.request_validator.validate_bearer_token( + token, request.scopes, request) def estimate_type(self, request): if request.headers.get('Authorization', '').startswith('Bearer'): diff -Nru python-oauthlib-0.5.1/oauthlib.egg-info/PKG-INFO python-oauthlib-0.6.1/oauthlib.egg-info/PKG-INFO --- python-oauthlib-0.5.1/oauthlib.egg-info/PKG-INFO 2013-07-26 18:13:18.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib.egg-info/PKG-INFO 2014-01-20 11:10:42.000000000 +0000 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: oauthlib -Version: 0.5.1 +Version: 0.6.1 Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic Home-page: https://github.com/idan/oauthlib -Author: Idan Gazit -Author-email: idan@gazit.me +Author: Ib Lundgren +Author-email: ib.lundgren@gmail.com License: BSD Description: OAuthLib ======== @@ -19,19 +19,23 @@ OAuth often seems complicated and difficult-to-implement. There are several - prominent libraries for signing OAuth requests, but they all suffer from one or + prominent libraries for handling OAuth requests, but they all suffer from one or both of the following: 1. They predate the `OAuth 1.0 spec`_, AKA RFC 5849. - 2. They assume the usage of a specific HTTP request library. + 2. They predate the `OAuth 2.0 spec`_, AKA RFC 6749. + 3. They assume the usage of a specific HTTP request library. .. _`OAuth 1.0 spec`: http://tools.ietf.org/html/rfc5849 + .. _`OAuth 2.0 spec`: http://tools.ietf.org/html/rfc6749 OAuthLib is a generic utility which implements the logic of OAuth without - assuming a specific HTTP request object. Use it to graft OAuth support onto your - favorite HTTP library. If you're a maintainer of such a library, write a thin + assuming a specific HTTP request object or web framework. Use it to graft OAuth + client support onto your favorite HTTP library, or provider support onto your + favourite web framework. If you're a maintainer of such a library, write a thin veneer on top of OAuthLib and get OAuth support for very little effort. + Documentation -------------- @@ -90,13 +94,47 @@ OAuthLib is yours to use and abuse according to the terms of the BSD license. Check the LICENSE file for full details. - - Changelog --------- - *OAuthLib is in active development, with most of OAuth1 complete and OAuth2 - already in the works.* + *OAuthLib is in active development, with the core of both OAuth 1 and 2 + completed, for providers as well as clients.* See `supported features`_ for + details. + + .. _`supported features`: http://oauthlib.readthedocs.org/en/latest/feature_matrix.html + + 0.6.1: Draft revocation endpoint features and numerous fixes including + + * (OAuth 2 Provider) is_within_original_scope to check whether a refresh token + is trying to aquire a new set of scopes that are a subset of the original scope. + + * (OAuth 2 Provider) expires_in token lifetime can be set per request. + + * (OAuth 2 Provider) client_authentication_required method added to differentiate + between public and confidential clients. + + * (OAuth 2 Provider) rotate_refresh_token now indicates whether a new refresh + token should be generated during token refresh or if old should be kept. + + * (OAuth 2 Provider) returned JSON headers no longer include charset. + + * (OAuth 2 Provider) validate_authorizatoin_request now also includes the + internal request object in the returned dictionary. Note that this is + not meant to be relied upon heavily and its interface might change. + + * and many style and typo fixes. + + 0.6.0: OAuth 1 & 2 provider API refactor with breaking changes + + * All endpoint methods change contract to return 3 values instead of 4. The new + signature is `headers`, `body`, `status code` where the initial `redirect_uri` + has been relocated to its rightful place inside headers as `Location`. + + * OAuth 1 Access Token Endpoint has a new required validator method + `invalidate_request_token`. + + * OAuth 1 Authorization Endpoint now returns a 200 response instead of 302 on + `oob` callbacks. 0.5.1: OAuth 1 provider fix for incorrect token param in nonce validation. diff -Nru python-oauthlib-0.5.1/oauthlib.egg-info/SOURCES.txt python-oauthlib-0.6.1/oauthlib.egg-info/SOURCES.txt --- python-oauthlib-0.5.1/oauthlib.egg-info/SOURCES.txt 2013-07-26 18:13:18.000000000 +0000 +++ python-oauthlib-0.6.1/oauthlib.egg-info/SOURCES.txt 2014-01-20 11:10:42.000000000 +0000 @@ -42,6 +42,7 @@ oauthlib/oauth2/rfc6749/endpoints/base.py oauthlib/oauth2/rfc6749/endpoints/pre_configured.py oauthlib/oauth2/rfc6749/endpoints/resource.py +oauthlib/oauth2/rfc6749/endpoints/revocation.py oauthlib/oauth2/rfc6749/endpoints/token.py oauthlib/oauth2/rfc6749/grant_types/__init__.py oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -67,11 +68,31 @@ tests/oauth1/rfc5849/endpoints/test_resource.py tests/oauth2/__init__.py tests/oauth2/rfc6749/__init__.py -tests/oauth2/rfc6749/test_client.py -tests/oauth2/rfc6749/test_grant_types.py tests/oauth2/rfc6749/test_parameters.py +tests/oauth2/rfc6749/test_request_validator.py tests/oauth2/rfc6749/test_server.py -tests/oauth2/rfc6749/test_servers.py tests/oauth2/rfc6749/test_tokens.py tests/oauth2/rfc6749/test_utils.py +tests/oauth2/rfc6749/clients/__init__.py +tests/oauth2/rfc6749/clients/test_backend_application.py +tests/oauth2/rfc6749/clients/test_base.py +tests/oauth2/rfc6749/clients/test_legacy_application.py +tests/oauth2/rfc6749/clients/test_mobile_application.py +tests/oauth2/rfc6749/clients/test_web_application.py +tests/oauth2/rfc6749/endpoints/__init__.py +tests/oauth2/rfc6749/endpoints/test_base_endpoint.py +tests/oauth2/rfc6749/endpoints/test_client_authentication.py +tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py +tests/oauth2/rfc6749/endpoints/test_error_responses.py +tests/oauth2/rfc6749/endpoints/test_extra_credentials.py +tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py +tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py +tests/oauth2/rfc6749/endpoints/test_scope_handling.py +tests/oauth2/rfc6749/endpoints/test_utils.py +tests/oauth2/rfc6749/grant_types/__init__.py +tests/oauth2/rfc6749/grant_types/test_authorization_code.py +tests/oauth2/rfc6749/grant_types/test_client_credentials.py +tests/oauth2/rfc6749/grant_types/test_implicit.py +tests/oauth2/rfc6749/grant_types/test_refresh_token.py +tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py tests/unittest/__init__.py \ No newline at end of file diff -Nru python-oauthlib-0.5.1/setup.py python-oauthlib-0.6.1/setup.py --- python-oauthlib-0.5.1/setup.py 2013-07-26 18:12:40.000000000 +0000 +++ python-oauthlib-0.6.1/setup.py 2014-01-20 10:11:11.000000000 +0000 @@ -10,6 +10,7 @@ from os.path import dirname, join from setuptools import setup, find_packages +import oauthlib def fread(fn): @@ -26,11 +27,13 @@ setup( name='oauthlib', - version='0.5.1', + version=oauthlib.__version__, description='A generic, spec-compliant, thorough implementation of the OAuth request-signing logic', long_description=fread('README.rst'), author='Idan Gazit', author_email='idan@gazit.me', + maintainer='Ib Lundgren', + maintainer_email='ib.lundgren@gmail.com', url='https://github.com/idan/oauthlib', platforms='any', license='BSD', diff -Nru python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_access_token.py python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_access_token.py --- python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_access_token.py 2013-07-26 18:08:37.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_access_token.py 2013-09-13 09:42:57.000000000 +0000 @@ -24,6 +24,7 @@ self.validator.validate_request_token.return_value = True self.validator.validate_verifier.return_value = True self.validator.validate_timestamp_and_nonce.return_value = True + self.validator.invalidate_request_token.return_value = True self.validator.dummy_client = 'dummy' self.validator.dummy_secret = 'dummy' self.validator.dummy_request_token = 'dummy' @@ -39,33 +40,33 @@ def test_check_request_token(self): self.validator.check_request_token.return_value = False - u, h, b, s = self.endpoint.create_access_token_response( + h, b, s = self.endpoint.create_access_token_response( self.uri, headers=self.headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) def test_check_verifier(self): self.validator.check_verifier.return_value = False - u, h, b, s = self.endpoint.create_access_token_response( + h, b, s = self.endpoint.create_access_token_response( self.uri, headers=self.headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) def test_validate_client_key(self): self.validator.validate_client_key.return_value = False - u, h, b, s = self.endpoint.create_access_token_response( + h, b, s = self.endpoint.create_access_token_response( self.uri, headers=self.headers) 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( + h, b, s = self.endpoint.create_access_token_response( self.uri, headers=self.headers) 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( + h, b, s = self.endpoint.create_access_token_response( self.uri, headers=self.headers) self.assertEqual(s, 401) @@ -75,16 +76,18 @@ resource_owner_secret='secret', verifier='verfier') _, headers, _ = client.sign(self.uri + '/extra') - u, h, b, s = self.endpoint.create_access_token_response( + h, b, s = self.endpoint.create_access_token_response( self.uri, headers=headers) self.assertEqual(s, 401) def test_valid_request(self): - u, h, b, s = self.endpoint.create_access_token_response( + h, b, s = self.endpoint.create_access_token_response( self.uri, headers=self.headers) self.assertEqual(s, 200) self.assertIn('oauth_token', b) self.validator.validate_timestamp_and_nonce.assert_called_once_with( self.client.client_key, ANY, ANY, ANY, request_token=self.client.resource_owner_key) + self.validator.invalidate_request_token.assert_called_once_with( + self.client.client_key, self.client.resource_owner_key, ANY) diff -Nru python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_authorization.py python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_authorization.py --- python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_authorization.py 2013-06-20 13:50:07.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_authorization.py 2013-08-13 21:12:37.000000000 +0000 @@ -15,7 +15,6 @@ self.validator.verify_request_token.return_value = True self.validator.verify_realms.return_value = True self.validator.get_realms.return_value = ['test'] - self.validator.get_redirect_uri.return_value = 'https://c.b/cb' self.validator.save_verifier = MagicMock() self.endpoint = AuthorizationEndpoint(self.validator) self.uri = 'https://i.b/authorize?oauth_token=foo' @@ -39,7 +38,17 @@ realms=['bar']) def test_create_authorization_response(self): - u, h, b, s = self.endpoint.create_authorization_response(self.uri) + self.validator.get_redirect_uri.return_value = 'https://c.b/cb' + h, b, s = self.endpoint.create_authorization_response(self.uri) self.assertEqual(s, 302) - self.assertTrue(u.startswith('https://c.b/cb')) - self.assertIn('oauth_verifier', u) + self.assertIn('Location', h) + self.assertTrue(location.startswith('https://c.b/cb')) + self.assertIn('oauth_verifier', location) + + def test_create_authorization_response(self): + self.validator.get_redirect_uri.return_value = 'oob' + h, b, s = self.endpoint.create_authorization_response(self.uri) + self.assertEqual(s, 200) + self.assertNotIn('Location', h) + self.assertIn('oauth_verifier', b) + self.assertIn('oauth_token', b) diff -Nru python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_base.py python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_base.py --- python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_base.py 2013-06-20 13:50:07.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_base.py 2013-09-12 10:40:03.000000000 +0000 @@ -27,13 +27,13 @@ def test_ssl_enforcement(self): uri, headers, _ = self.client.sign('http://i.b/request_token') - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( uri, headers=headers) self.assertEqual(s, 400) self.assertIn('insecure_transport_protocol', b) def test_missing_parameters(self): - u, h, b, s = self.endpoint.create_request_token_response(self.uri) + h, b, s = self.endpoint.create_request_token_response(self.uri) self.assertEqual(s, 400) self.assertIn('invalid_request', b) @@ -41,7 +41,7 @@ headers = {} headers['Authorization'] = self.headers['Authorization'].replace( 'HMAC', 'RSA') - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=headers) self.assertEqual(s, 400) self.assertIn('invalid_signature_method', b) @@ -50,7 +50,7 @@ headers = {} headers['Authorization'] = self.headers['Authorization'].replace( '1.0', '2.0') - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) @@ -61,21 +61,21 @@ headers['Authorization'] = sub('timestamp="\d*k?"', 'timestamp="%s"' % pattern, self.headers['Authorization']) - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) def test_client_key_check(self): self.validator.check_client_key.return_value = False - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=self.headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) def test_noncecheck(self): self.validator.check_nonce.return_value = False - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=self.headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) @@ -330,22 +330,38 @@ def get_request_token_secret(self, client_key, request_token, request): return 'even more secret' + def get_rsa_key(self, client_key, request): + return ("-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNA" + "DCBiQKBgQDVLQCATX8iK+aZuGVdkGb6uiar\nLi/jqFwL1dYj0JLIsdQc" + "KaMWtPC06K0+vI+RRZcjKc6sNB9/7kJcKN9Ekc9BUxyT\n/D09Cz47cmC" + "YsUoiW7G8NSqbE4wPiVpGkJRzFAxaCWwOSSQ+lpC9vwxnvVQfOoZ1\nnp" + "mWbCdA0iTxsMahwQIDAQAB\n-----END PUBLIC KEY-----") + class SignatureVerificationTest(TestCase): - def test_signature_verification(self): + def setUp(self): v = ClientValidator() - e = BaseEndpoint(v) + self.e = BaseEndpoint(v) - uri = 'https://example.com/' + self.uri = 'https://example.com/' + self.sig = ('oauth_signature=%s&' + 'oauth_timestamp=1234567890&' + 'oauth_nonce=abcdefghijklmnopqrstuvwxyz&' + 'oauth_version=1.0&' + 'oauth_signature_method=%s&' + 'oauth_token=abcdefghijklmnopqrstuvxyz&' + 'oauth_consumer_key=foo') + + def test_signature_too_short(self): short_sig = ('oauth_signature=fmrXnTF4lO4o%2BD0%2FlZaJHP%2FXqEY&' 'oauth_timestamp=1234567890&' 'oauth_nonce=abcdefghijklmnopqrstuvwxyz&' 'oauth_version=1.0&oauth_signature_method=HMAC-SHA1&' 'oauth_token=abcdefghijklmnopqrstuvxyz&' 'oauth_consumer_key=foo') - r = e._create_request(uri, 'GET', short_sig, URLENCODED) - self.assertFalse(e._check_signature(r)) + r = self.e._create_request(self.uri, 'GET', short_sig, URLENCODED) + self.assertFalse(self.e._check_signature(r)) plain = ('oauth_signature=correctlengthbutthewrongcontent1111&' 'oauth_timestamp=1234567890&' @@ -353,5 +369,26 @@ 'oauth_version=1.0&oauth_signature_method=PLAINTEXT&' 'oauth_token=abcdefghijklmnopqrstuvxyz&' 'oauth_consumer_key=foo') - r = e._create_request(uri, 'GET', plain, URLENCODED) - self.assertFalse(e._check_signature(r)) + r = self.e._create_request(self.uri, 'GET', plain, URLENCODED) + self.assertFalse(self.e._check_signature(r)) + + def test_hmac_signature(self): + hmac_sig = "fmrXnTF4lO4o%2BD0%2FlZaJHP%2FXqEY%3D" + sig = self.sig % (hmac_sig, "HMAC-SHA1") + r = self.e._create_request(self.uri, 'GET', sig, URLENCODED) + self.assertTrue(self.e._check_signature(r)) + + def test_rsa_signature(self): + rsa_sig = ("fxFvCx33oKlR9wDquJ%2FPsndFzJphyBa3RFPPIKi3flqK%2BJ7yIrMVbH" + "YTM%2FLHPc7NChWz4F4%2FzRA%2BDN1k08xgYGSBoWJUOW6VvOQ6fbYhMA" + "FkOGYbuGDbje487XMzsAcv6ZjqZHCROSCk5vofgLk2SN7RZ3OrgrFzf4in" + "xetClqA%3D") + sig = self.sig % (rsa_sig, "RSA-SHA1") + r = self.e._create_request(self.uri, 'GET', sig, URLENCODED) + self.assertTrue(self.e._check_signature(r)) + + def test_plaintext_signature(self): + plain_sig = "super%252520secret%26even%252520more%252520secret" + sig = self.sig % (plain_sig, "PLAINTEXT") + r = self.e._create_request(self.uri, 'GET', sig, URLENCODED) + self.assertTrue(self.e._check_signature(r)) diff -Nru python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_request_token.py python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_request_token.py --- python-oauthlib-0.5.1/tests/oauth1/rfc5849/endpoints/test_request_token.py 2013-07-26 18:08:09.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth1/rfc5849/endpoints/test_request_token.py 2013-08-13 21:12:37.000000000 +0000 @@ -34,45 +34,45 @@ def test_check_redirect_uri(self): client = Client('foo') uri, headers, _ = client.sign(self.uri) - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( uri, headers=headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) def test_check_realms(self): self.validator.check_realms.return_value = False - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=self.headers) self.assertEqual(s, 400) self.assertIn('invalid_request', b) def test_validate_client_key(self): self.validator.validate_client_key.return_value = False - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=self.headers) self.assertEqual(s, 401) def test_validate_realms(self): self.validator.validate_requested_realms.return_value = False - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=self.headers) 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( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=self.headers) 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( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=headers) self.assertEqual(s, 401) def test_valid_request(self): - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( self.uri, headers=self.headers) self.assertEqual(s, 200) self.assertIn('oauth_token', b) @@ -85,7 +85,7 @@ client_secret='bar') uri = self.uri + '?realm=foo' _, headers, _ = client.sign(uri) - u, h, b, s = self.endpoint.create_request_token_response( + h, b, s = self.endpoint.create_request_token_response( uri, headers=headers) self.assertEqual(s, 200) self.assertIn('oauth_token', b) diff -Nru python-oauthlib-0.5.1/tests/oauth1/rfc5849/test_client.py python-oauthlib-0.6.1/tests/oauth1/rfc5849/test_client.py --- python-oauthlib-0.5.1/tests/oauth1/rfc5849/test_client.py 2013-05-20 09:22:10.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth1/rfc5849/test_client.py 2013-09-12 12:27:11.000000000 +0000 @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, unicode_literals +from oauthlib.oauth1 import SIGNATURE_RSA, SIGNATURE_PLAINTEXT +from oauthlib.oauth1 import SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY from oauthlib.oauth1.rfc5849 import Client, bytes_type from ...unittest import TestCase @@ -59,6 +61,93 @@ self.assertIsInstance(v, bytes_type) +class SignatureMethodTest(TestCase): + + def test_rsa_method(self): + private_key = ( + "-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDk1/bxy" + "S8Q8jiheHeYYp/4rEKJopeQRRKKpZI4s5i+UPwVpupG\nAlwXWfzXw" + "SMaKPAoKJNdu7tqKRniqst5uoHXw98gj0x7zamu0Ck1LtQ4c7pFMVa" + "h\n5IYGhBi2E9ycNS329W27nJPWNCbESTu7snVlG8V8mfvGGg3xNjT" + "MO7IdrwIDAQAB\nAoGBAOQ2KuH8S5+OrsL4K+wfjoCi6MfxCUyqVU9" + "GxocdM1m30WyWRFMEz2nKJ8fR\np3vTD4w8yplTOhcoXdQZl0kRoaD" + "zrcYkm2VvJtQRrX7dKFT8dR8D/Tr7dNQLOXfC\nDY6xveQczE7qt7V" + "k7lp4FqmxBsaaEuokt78pOOjywZoInjZhAkEA9wz3zoZNT0/i\nrf6" + "qv2qTIeieUB035N3dyw6f1BGSWYaXSuerDCD/J1qZbAPKKhyHZbVaw" + "Ft3UMhe\n542UftBaxQJBAO0iJy1I8GQjGnS7B3yvyH3CcLYGy296+" + "XO/2xKp/d/ty1OIeovx\nC60pLNwuFNF3z9d2GVQAdoQ89hUkOtjZL" + "eMCQQD0JO6oPHUeUjYT+T7ImAv7UKVT\nSuy30sKjLzqoGw1kR+wv7" + "C5PeDRvscs4wa4CW9s6mjSrMDkDrmCLuJDtmf55AkEA\nkmaMg2PNr" + "jUR51F0zOEFycaaqXbGcFwe1/xx9zLmHzMDXd4bsnwt9kk+fe0hQzV" + "S\nJzatanQit3+feev1PN3QewJAWv4RZeavEUhKv+kLe95Yd0su7lT" + "LVduVgh4v5yLT\nGa6FHdjGPcfajt+nrpB1n8UQBEH9ZxniokR/IPv" + "dMlxqXA==\n-----END RSA PRIVATE KEY-----" + ) + client = Client('client_key', signature_method=SIGNATURE_RSA, + rsa_key=private_key, timestamp='1234567890', nonce='abc') + u, h, b = client.sign('http://example.com') + correct = ('OAuth oauth_nonce="abc", oauth_timestamp="1234567890", ' + 'oauth_version="1.0", oauth_signature_method="RSA-SHA1", ' + 'oauth_consumer_key="client_key", ' + 'oauth_signature="ktvzkUhtrIawBcq21DRJrAyysTc3E1Zq5GdGu8EzH' + 'OtbeaCmOBDLGHAcqlm92mj7xp5E1Z6i2vbExPimYAJL7FzkLnkRE5YEJR4' + 'rNtIgAf1OZbYsIUmmBO%2BCLuStuu5Lg3tAluwC7XkkgoXCBaRKT1mUXzP' + 'HJILzZ8iFOvS6w5E%3D"') + self.assertEqual(h['Authorization'], correct) + + + def test_plaintext_method(self): + client = Client('client_key', + signature_method=SIGNATURE_PLAINTEXT, + timestamp='1234567890', + nonce='abc', + client_secret='foo', + resource_owner_secret='bar') + u, h, b = client.sign('http://example.com') + correct = ('OAuth oauth_nonce="abc", oauth_timestamp="1234567890", ' + 'oauth_version="1.0", oauth_signature_method="PLAINTEXT", ' + 'oauth_consumer_key="client_key", ' + 'oauth_signature="foo%26bar"') + self.assertEqual(h['Authorization'], correct) + + def test_invalid_method(self): + client = Client('client_key', signature_method='invalid') + self.assertRaises(ValueError, client.sign, 'http://example.com') + + +class SignatureTypeTest(TestCase): + + def test_params_in_body(self): + client = Client('client_key', signature_type=SIGNATURE_TYPE_BODY, + timestamp='1378988215', nonce='14205877133089081931378988215') + _, h, b = client.sign('http://i.b/path', http_method='POST', body='a=b', + headers={'Content-Type': 'application/x-www-form-urlencoded'}) + self.assertEqual(h['Content-Type'], 'application/x-www-form-urlencoded') + correct = ('a=b&oauth_nonce=14205877133089081931378988215&' + 'oauth_timestamp=1378988215&' + 'oauth_version=1.0&' + 'oauth_signature_method=HMAC-SHA1&' + 'oauth_consumer_key=client_key&' + 'oauth_signature=2JAQomgbShqoscqKWBiYQZwWq94%3D') + self.assertEqual(b, correct) + + def test_params_in_query(self): + client = Client('client_key', signature_type=SIGNATURE_TYPE_QUERY, + timestamp='1378988215', nonce='14205877133089081931378988215') + u, _, _ = client.sign('http://i.b/path', http_method='POST') + correct = ('http://i.b/path?oauth_nonce=14205877133089081931378988215&' + 'oauth_timestamp=1378988215&' + 'oauth_version=1.0&' + 'oauth_signature_method=HMAC-SHA1&' + 'oauth_consumer_key=client_key&' + 'oauth_signature=08G5Snvw%2BgDAzBF%2BCmT5KqlrPKo%3D') + self.assertEqual(u, correct) + + def test_invalid_signature_type(self): + client = Client('client_key', signature_type='invalid') + self.assertRaises(ValueError, client.sign, 'http://i.b/path') + + class SigningTest(TestCase): def test_case_insensitive_headers(self): @@ -89,7 +178,7 @@ http_method='POST', body=None, headers={'Content-Type': 'application/x-www-form-urlencoded'}) - def test_sign_empty_body(self): + def test_sign_body(self): client = Client('client_key') _, h, b = client.sign('http://i.b/path', http_method='POST', body='', headers={'Content-Type': 'application/x-www-form-urlencoded'}) diff -Nru python-oauthlib-0.5.1/tests/oauth1/rfc5849/test_signatures.py python-oauthlib-0.6.1/tests/oauth1/rfc5849/test_signatures.py --- python-oauthlib-0.5.1/tests/oauth1/rfc5849/test_signatures.py 2013-05-20 09:22:10.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth1/rfc5849/test_signatures.py 2013-09-12 09:52:26.000000000 +0000 @@ -6,7 +6,13 @@ except ImportError: from urllib.parse import quote -from oauthlib.oauth1.rfc5849.signature import * +from oauthlib.oauth1.rfc5849.signature import collect_parameters +from oauthlib.oauth1.rfc5849.signature import construct_base_string +from oauthlib.oauth1.rfc5849.signature import normalize_base_string_uri +from oauthlib.oauth1.rfc5849.signature import normalize_parameters +from oauthlib.oauth1.rfc5849.signature import sign_hmac_sha1 +from oauthlib.oauth1.rfc5849.signature import sign_rsa_sha1 +from oauthlib.oauth1.rfc5849.signature import sign_plaintext from oauthlib.common import unicode_type from ...unittest import TestCase @@ -23,11 +29,34 @@ oauth_signature="djosJKDKJSD8743243%2Fjdk33klY%3D" """.strip() body = "content=This+is+being+the+body+of+things" http_method = b"post" - base_string_url = quote("http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b").encode('utf-8') - normalized_encoded_request_parameters = quote("""OAuth realm="Example",oauth_consumer_key="9djdj82h48djs9d2",oauth_token="kkk9d7dh3k39sjv7",oauth_signature_method="HMAC-SHA1",oauth_timestamp="137131201",oauth_nonce="7d8f3e4a",oauth_signature="bYT5CMsGcbgUdFHObYMEfcx6bsw%3D" """.strip()).encode('utf-8') + base_string_url = quote("http://example.com/request?b5=%3D%253D" + "&a3=a&c%40=&a2=r%20b").encode('utf-8') + normalized_encoded_request_parameters = quote( + 'OAuth realm="Example",' + 'oauth_consumer_key="9djdj82h48djs9d2",' + 'oauth_token="kkk9d7dh3k39sjv7",' + 'oauth_signature_method="HMAC-SHA1",' + 'oauth_timestamp="137131201",' + 'oauth_nonce="7d8f3e4a",' + 'oauth_signature="bYT5CMsGcbgUdFHObYMEfcx6bsw%3D"' + ).encode('utf-8') client_secret = b"ECrDNoq1VYzzzzzzzzzyAK7TwZNtPnkqatqZZZZ" resource_owner_secret = b"just-a-string asdasd" - control_base_string = "POST&http%253A%2F%2Fexample.com%2Frequest%253Fb5%253D%25253D%2525253D%2526a3%253Da%2526c%252540%253D%2526a2%253Dr%252520b&OAuth%2520realm%253D%2522Example%2522%252Coauth_consumer_key%253D%25229djdj82h48djs9d2%2522%252Coauth_token%253D%2522kkk9d7dh3k39sjv7%2522%252Coauth_signature_method%253D%2522HMAC-SHA1%2522%252Coauth_timestamp%253D%2522137131201%2522%252Coauth_nonce%253D%25227d8f3e4a%2522%252Coauth_signature%253D%2522bYT5CMsGcbgUdFHObYMEfcx6bsw%25253D%2522" + control_base_string = ( + "POST&http%253A%2F%2Fexample.com%2Frequest%253F" + "b5%253D%25253D%2525253D%2526" + "a3%253D" + "a%2526" + "c%252540%253D%2526" + "a2%253D" + "r%252520b&" + "OAuth%2520realm%253D%2522Example%2522%252C" + "oauth_consumer_key%253D%25229djdj82h48djs9d2%2522%252C" + "oauth_token%253D%2522kkk9d7dh3k39sjv7%2522%252C" + "oauth_signature_method%253D%2522HMAC-SHA1%2522%252C" + "oauth_timestamp%253D%2522137131201%2522%252C" + "oauth_nonce%253D%25227d8f3e4a%2522%252C" + "oauth_signature%253D%2522bYT5CMsGcbgUdFHObYMEfcx6bsw%25253D%2522") def test_construct_base_string(self): """ @@ -46,24 +75,32 @@ Sample Base string generated and tested against:: - POST&http%253A%2F%2Fexample.com%2Frequest%253Fb5%253D%25253D%2525253D - %2526a3%253Da%2526c%252540%253D%2526a2%253Dr%252520b&OAuth%2520realm% - 253D%2522Example%2522%252Coauth_consumer_key%253D%25229djdj82h48djs9d - 2%2522%252Coauth_token%253D%2522kkk9d7dh3k39sjv7%2522%252Coauth_signa - ture_method%253D%2522HMAC-SHA1%2522%252Coauth_timestamp%253D%25221371 - 31201%2522%252Coauth_nonce%253D%25227d8f3e4a%2522%252Coauth_signature - %253D%2522bYT5CMsGcbgUdFHObYMEfcx6bsw%25253D%2522 + POST&http%253A%2F%2Fexample.com%2Frequest%253Fb5%253D%25253D%252525 + 3D%2526a3%253Da%2526c%252540%253D%2526a2%253Dr%252520b&OAuth%2520re + alm%253D%2522Example%2522%252Coauth_consumer_key%253D%25229djdj82h4 + 8djs9d2%2522%252Coauth_token%253D%2522kkk9d7dh3k39sjv7%2522%252Coau + th_signature_method%253D%2522HMAC-SHA1%2522%252Coauth_timestamp%253 + D%2522137131201%2522%252Coauth_nonce%253D%25227d8f3e4a%2522%252Coau + th_signature%253D%2522bYT5CMsGcbgUdFHObYMEfcx6bsw%25253D%2522 """ - - # Create test variables - # Create test variables - # Create test variables - - self.assertRaises(ValueError, construct_base_string, self.http_method, self.base_string_url, self.normalized_encoded_request_parameters) - self.assertRaises(ValueError, construct_base_string, self.http_method.decode('utf-8'), self.base_string_url, self.normalized_encoded_request_parameters) - self.assertRaises(ValueError, construct_base_string, self.http_method.decode('utf-8'), self.base_string_url.decode('utf-8'), self.normalized_encoded_request_parameters) - - base_string = construct_base_string(self.http_method.decode('utf-8'), self.base_string_url.decode('utf-8'), self.normalized_encoded_request_parameters.decode('utf-8')) + self.assertRaises(ValueError, construct_base_string, + self.http_method, + self.base_string_url, + self.normalized_encoded_request_parameters) + self.assertRaises(ValueError, construct_base_string, + self.http_method.decode('utf-8'), + self.base_string_url, + self.normalized_encoded_request_parameters) + self.assertRaises(ValueError, construct_base_string, + self.http_method.decode('utf-8'), + self.base_string_url.decode('utf-8'), + self.normalized_encoded_request_parameters) + + base_string = construct_base_string( + self.http_method.decode('utf-8'), + self.base_string_url.decode('utf-8'), + self.normalized_encoded_request_parameters.decode('utf-8') + ) self.assertEqual(self.control_base_string, base_string) @@ -83,13 +120,19 @@ uri = b"www.example.com:8080" self.assertRaises(ValueError, normalize_base_string_uri, uri) + # test for missing scheme + uri = "www.example.com:8080" + self.assertRaises(ValueError, normalize_base_string_uri, uri) + # test a URI with the default port uri = "http://www.example.com:80/" - self.assertEquals(normalize_base_string_uri(uri), "http://www.example.com/") + self.assertEquals(normalize_base_string_uri(uri), + "http://www.example.com/") # test a URI missing a path uri = "http://www.example.com" - self.assertEquals(normalize_base_string_uri(uri), "http://www.example.com/") + self.assertEquals(normalize_base_string_uri(uri), + "http://www.example.com/") # test a relative URI uri = "/a-host-relative-uri" @@ -103,18 +146,12 @@ "http://alternatehost.example.com/a-path") def test_collect_parameters(self): - """ We check against parameters multiple times in case things change after more - parameters are added. + """We check against parameters multiple times in case things change + after more parameters are added. """ - # check against empty parameters - # check against empty parameters - # check against empty parameters self.assertEquals(collect_parameters(), []) # Check against uri_query - # Check against uri_query - # Check against uri_query - parameters = collect_parameters(uri_query=self.uri_query) correct_parameters = [('b5', '=%3D'), ('a3', 'a'), @@ -124,34 +161,32 @@ ('a3', '2 q')] self.assertEqual(sorted(parameters), sorted(correct_parameters)) + headers = {'Authorization': self.authorization_header} # check against authorization header as well - # check against authorization header as well - # check against authorization header as well - - parameters = collect_parameters(uri_query=self.uri_query, headers={ - 'Authorization': self.authorization_header, - }) - parameters_with_realm = collect_parameters(uri_query=self.uri_query, headers={ - 'Authorization': self.authorization_header, - }, with_realm=True) - # Redo the checks against all the parameters. Duplicated code but better safety + parameters = collect_parameters( + uri_query=self.uri_query, headers=headers) + parameters_with_realm = collect_parameters( + uri_query=self.uri_query, headers=headers, with_realm=True) + # Redo the checks against all the parameters. Duplicated code but + # better safety correct_parameters += [ ('oauth_nonce', '7d8f3e4a'), ('oauth_timestamp', '137131201'), ('oauth_consumer_key', '9djdj82h48djs9d2'), ('oauth_signature_method', 'HMAC-SHA1'), ('oauth_token', 'kkk9d7dh3k39sjv7')] - correct_parameters_with_realm = correct_parameters + [('realm', 'Example')] + correct_parameters_with_realm = ( + correct_parameters + [('realm', 'Example')]) self.assertEqual(sorted(parameters), sorted(correct_parameters)) - self.assertEqual(sorted(parameters_with_realm), sorted(correct_parameters_with_realm)) + self.assertEqual(sorted(parameters_with_realm), + sorted(correct_parameters_with_realm)) # Add in the body. - # TODO - add more valid content for the body. Daniel Greenfeld 2012/03/12 - # Redo again the checks against all the parameters. Duplicated code but better safety - parameters = collect_parameters(uri_query=self.uri_query, - body=self.body, headers={ - 'Authorization': self.authorization_header, - }) + # TODO: Add more content for the body. Daniel Greenfeld 2012/03/12 + # Redo again the checks against all the parameters. Duplicated code + # but better safety + parameters = collect_parameters( + uri_query=self.uri_query, body=self.body, headers=headers) correct_parameters += [ ('content', 'This is being the body of things')] self.assertEqual(sorted(parameters), sorted(correct_parameters)) @@ -159,61 +194,96 @@ def test_normalize_parameters(self): """ We copy some of the variables from the test method above.""" - # Create the parameters - parameters = collect_parameters(uri_query=self.uri_query, - body=self.body, headers={ - 'Authorization': self.authorization_header, - }) + headers = {'Authorization': self.authorization_header} + parameters = collect_parameters( + uri_query=self.uri_query, body=self.body, headers=headers) normalized = normalize_parameters(parameters) - # check the parameters type + # Unicode everywhere and always self.assertIsInstance(normalized, unicode_type) # Lets see if things are in order # check to see that querystring keys come in alphanumeric order: - querystring_keys = ['a2', 'a3', 'b5', 'content', 'oauth_consumer_key', 'oauth_nonce', 'oauth_signature_method', 'oauth_timestamp', 'oauth_token'] + querystring_keys = ['a2', 'a3', 'b5', 'content', 'oauth_consumer_key', + 'oauth_nonce', 'oauth_signature_method', + 'oauth_timestamp', 'oauth_token'] index = -1 # start at -1 because the 'a2' key starts at index 0 for key in querystring_keys: self.assertGreater(normalized.index(key), index) index = normalized.index(key) def test_sign_hmac_sha1(self): - """ Verifying correct HMAC-SHA1 signature against one created by openssl.""" - - # self.control_base_string saved in , hmac_key in . - # hmac_key = "ECrDNoq1VYzzzzzzzzzyAK7TwZNtPnkqatqZZZZ&just-a-string%20%20%20%20asdasd" + """Verifying HMAC-SHA1 signature against one created by OpenSSL.""" # Control signature created using openssl: - # $ echo -n $(cat ) | openssl dgst -binary -hmac | base64 + # echo -n $(cat ) | openssl dgst -binary -hmac | base64 control_signature = "Uau4O9Kpd2k6rvh7UZN/RN+RG7Y=" - # check for Unicode - self.assertRaises(ValueError, sign_hmac_sha1, self.control_base_string, self.client_secret, self.resource_owner_secret) + self.assertRaises(ValueError, sign_hmac_sha1, self.control_base_string, + self.client_secret, self.resource_owner_secret) - # Do the actual test - sign = sign_hmac_sha1(self.control_base_string, self.client_secret.decode('utf-8'), self.resource_owner_secret.decode('utf-8')) + sign = sign_hmac_sha1(self.control_base_string, + self.client_secret.decode('utf-8'), + self.resource_owner_secret.decode('utf-8')) self.assertEquals(len(sign), 28) self.assertEquals(sign, control_signature) def test_sign_rsa_sha1(self): - """ Verify correct RSA-SHA1 signature against one created by openssl.""" + """Verify RSA-SHA1 signature against one created by OpenSSL.""" - base_string = b"POST&http%253A%2F%2Fexample.com%2Frequest%253Fb5%253D%25253D%2525253D%2526a3%253Da%2526c%252540%253D%2526a2%253Dr%252520b&OAuth%2520realm%253D%2522Example%2522%252Coauth_consumer_key%253D%25229djdj82h48djs9d2%2522%252Coauth_token%253D%2522kkk9d7dh3k39sjv7%2522%252Coauth_signature_method%253D%2522HMAC-SHA1%2522%252Coauth_timestamp%253D%2522137131201%2522%252Coauth_nonce%253D%25227d8f3e4a%2522%252Coauth_signature%253D%2522bYT5CMsGcbgUdFHObYMEfcx6bsw%25253D%2522" + base_string = (b"POST&http%253A%2F%2Fexample.com%2Frequest%253Fb5%253D" + b"%25253D%2525253D%2526a3%253Da%2526c%252540%253D%2526" + b"a2%253Dr%252520b&OAuth%2520realm%253D%2522Example%25" + b"22%252Coauth_consumer_key%253D%25229djdj82h48djs9d2" + b"%2522%252Coauth_token%253D%2522kkk9d7dh3k39sjv7%2522" + b"%252Coauth_signature_method%253D%2522HMAC-SHA1%2522" + b"%252Coauth_timestamp%253D%2522137131201%2522%252Coau" + b"th_nonce%253D%25227d8f3e4a%2522%252Coauth_signature" + b"%253D%2522bYT5CMsGcbgUdFHObYMEfcx6bsw%25253D%2522") # Generated using: $ openssl genrsa -out .pem 1024 - # PyCrypto / python-rsa requires the key to be concatenated with linebreaks. - private_key = b"-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDk1/bxyS8Q8jiheHeYYp/4rEKJopeQRRKKpZI4s5i+UPwVpupG\nAlwXWfzXwSMaKPAoKJNdu7tqKRniqst5uoHXw98gj0x7zamu0Ck1LtQ4c7pFMVah\n5IYGhBi2E9ycNS329W27nJPWNCbESTu7snVlG8V8mfvGGg3xNjTMO7IdrwIDAQAB\nAoGBAOQ2KuH8S5+OrsL4K+wfjoCi6MfxCUyqVU9GxocdM1m30WyWRFMEz2nKJ8fR\np3vTD4w8yplTOhcoXdQZl0kRoaDzrcYkm2VvJtQRrX7dKFT8dR8D/Tr7dNQLOXfC\nDY6xveQczE7qt7Vk7lp4FqmxBsaaEuokt78pOOjywZoInjZhAkEA9wz3zoZNT0/i\nrf6qv2qTIeieUB035N3dyw6f1BGSWYaXSuerDCD/J1qZbAPKKhyHZbVawFt3UMhe\n542UftBaxQJBAO0iJy1I8GQjGnS7B3yvyH3CcLYGy296+XO/2xKp/d/ty1OIeovx\nC60pLNwuFNF3z9d2GVQAdoQ89hUkOtjZLeMCQQD0JO6oPHUeUjYT+T7ImAv7UKVT\nSuy30sKjLzqoGw1kR+wv7C5PeDRvscs4wa4CW9s6mjSrMDkDrmCLuJDtmf55AkEA\nkmaMg2PNrjUR51F0zOEFycaaqXbGcFwe1/xx9zLmHzMDXd4bsnwt9kk+fe0hQzVS\nJzatanQit3+feev1PN3QewJAWv4RZeavEUhKv+kLe95Yd0su7lTLVduVgh4v5yLT\nGa6FHdjGPcfajt+nrpB1n8UQBEH9ZxniokR/IPvdMlxqXA==\n-----END RSA PRIVATE KEY-----" + # PyCrypto / python-rsa requires the key to be concatenated with + # linebreaks. + private_key = ( + b"-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDk1/bxy" + b"S8Q8jiheHeYYp/4rEKJopeQRRKKpZI4s5i+UPwVpupG\nAlwXWfzXw" + b"SMaKPAoKJNdu7tqKRniqst5uoHXw98gj0x7zamu0Ck1LtQ4c7pFMVa" + b"h\n5IYGhBi2E9ycNS329W27nJPWNCbESTu7snVlG8V8mfvGGg3xNjT" + b"MO7IdrwIDAQAB\nAoGBAOQ2KuH8S5+OrsL4K+wfjoCi6MfxCUyqVU9" + b"GxocdM1m30WyWRFMEz2nKJ8fR\np3vTD4w8yplTOhcoXdQZl0kRoaD" + b"zrcYkm2VvJtQRrX7dKFT8dR8D/Tr7dNQLOXfC\nDY6xveQczE7qt7V" + b"k7lp4FqmxBsaaEuokt78pOOjywZoInjZhAkEA9wz3zoZNT0/i\nrf6" + b"qv2qTIeieUB035N3dyw6f1BGSWYaXSuerDCD/J1qZbAPKKhyHZbVaw" + b"Ft3UMhe\n542UftBaxQJBAO0iJy1I8GQjGnS7B3yvyH3CcLYGy296+" + b"XO/2xKp/d/ty1OIeovx\nC60pLNwuFNF3z9d2GVQAdoQ89hUkOtjZL" + b"eMCQQD0JO6oPHUeUjYT+T7ImAv7UKVT\nSuy30sKjLzqoGw1kR+wv7" + b"C5PeDRvscs4wa4CW9s6mjSrMDkDrmCLuJDtmf55AkEA\nkmaMg2PNr" + b"jUR51F0zOEFycaaqXbGcFwe1/xx9zLmHzMDXd4bsnwt9kk+fe0hQzV" + b"S\nJzatanQit3+feev1PN3QewJAWv4RZeavEUhKv+kLe95Yd0su7lT" + b"LVduVgh4v5yLT\nGa6FHdjGPcfajt+nrpB1n8UQBEH9ZxniokR/IPv" + b"dMlxqXA==\n-----END RSA PRIVATE KEY-----" + ) # Base string saved in "". Signature obtained using: # $ echo -n $(cat ) | openssl dgst -sign .pem | base64 # where echo -n suppresses the last linebreak. - control_signature = "zV5g8ArdMuJuOXlH8XOqfLHS11XdthfIn4HReDm7jz8JmgLabHGmVBqCkCfZoFJPHdka7tLvCplK/jsV4FUOnftrJOQhbXguuBdi87/hmxOFKLmQYqqlEW7BdXmwKLZckiqq3qE5XziBgKSAFRkxJ4gmJAymvJBtrJYN9728rK8=" + control_signature = ( + "zV5g8ArdMuJuOXlH8XOqfLHS11XdthfIn4HReDm7jz8JmgLabHGmVBqCkCfZoFJPH" + "dka7tLvCplK/jsV4FUOnftrJOQhbXguuBdi87/hmxOFKLmQYqqlEW7BdXmwKLZcki" + "qq3qE5XziBgKSAFRkxJ4gmJAymvJBtrJYN9728rK8=" + ) sign = sign_rsa_sha1(base_string, private_key) self.assertEquals(sign, control_signature) + sign = sign_rsa_sha1(base_string.decode('utf-8'), private_key) + self.assertEquals(sign, control_signature) def test_sign_plaintext(self): """ """ - self.assertRaises(ValueError, sign_plaintext, self.client_secret, self.resource_owner_secret) - sign = sign_plaintext(self.client_secret.decode('utf-8'), self.resource_owner_secret.decode('utf-8')) - self.assertEquals(sign, "ECrDNoq1VYzzzzzzzzzyAK7TwZNtPnkqatqZZZZ&just-a-string%20%20%20%20asdasd") + self.assertRaises(ValueError, sign_plaintext, self.client_secret, + self.resource_owner_secret) + sign = sign_plaintext(self.client_secret.decode('utf-8'), + self.resource_owner_secret.decode('utf-8')) + correct = ("ECrDNoq1VYzzzzzzzzzyAK7TwZNtPnkqatqZZZZ&" + "just-a-string%20%20%20%20asdasd") + self.assertEquals(sign, correct) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_backend_application.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_backend_application.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_backend_application.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_backend_application.py 2013-10-04 10:17:01.000000000 +0000 @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals + +from mock import patch + +from oauthlib.oauth2 import BackendApplicationClient + +from ....unittest import TestCase + + +@patch('time.time', new=lambda: 1000) +class BackendApplicationClientTest(TestCase): + + client_id = "someclientid" + scope = ["/profile"] + kwargs = { + "some": "providers", + "require": "extra arguments" + } + + body = "not=empty" + + body_up = "not=empty&grant_type=client_credentials" + body_kwargs = body_up + "&some=providers&require=extra+arguments" + + token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' + ' "token_type":"example",' + ' "expires_in":3600,' + ' "scope":"/profile",' + ' "example_parameter":"example_value"}') + token = { + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": 3600, + "expires_at": 4600, + "scope": ["/profile"], + "example_parameter": "example_value" + } + + def test_request_body(self): + client = BackendApplicationClient(self.client_id) + + # Basic, no extra arguments + body = client.prepare_request_body(body=self.body) + self.assertFormBodyEqual(body, self.body_up) + + rclient = BackendApplicationClient(self.client_id) + body = rclient.prepare_request_body(body=self.body) + self.assertFormBodyEqual(body, self.body_up) + + # With extra parameters + body = client.prepare_request_body(body=self.body, **self.kwargs) + self.assertFormBodyEqual(body, self.body_kwargs) + + def test_parse_token_response(self): + client = BackendApplicationClient(self.client_id) + + # Parse code and state + response = client.parse_request_body_response(self.token_json, scope=self.scope) + self.assertEqual(response, self.token) + self.assertEqual(client.access_token, response.get("access_token")) + self.assertEqual(client.refresh_token, response.get("refresh_token")) + self.assertEqual(client.token_type, response.get("token_type")) + + # Mismatching state + self.assertRaises(Warning, client.parse_request_body_response, self.token_json, scope="invalid") diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_base.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_base.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_base.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_base.py 2013-10-03 16:43:04.000000000 +0000 @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ....unittest import TestCase + +import datetime +from oauthlib import common +from oauthlib.oauth2.rfc6749 import utils +from oauthlib.oauth2 import Client +from oauthlib.oauth2.rfc6749.clients import AUTH_HEADER, URI_QUERY, BODY + + +class ClientTest(TestCase): + + client_id = "someclientid" + uri = "https://example.com/path?query=world" + body = "not=empty" + headers = {} + access_token = "token" + mac_key = "secret" + + bearer_query = uri + "&access_token=" + access_token + bearer_header = { + "Authorization": "Bearer " + access_token + } + bearer_body = body + "&access_token=" + access_token + + mac_00_header = { + "Authorization": 'MAC id="' + access_token + '", nonce="0:abc123",' + + ' bodyhash="Yqyso8r3hR5Nm1ZFv+6AvNHrxjE=",' + + ' mac="0X6aACoBY0G6xgGZVJ1IeE8dF9k="' + } + mac_01_header = { + "Authorization": 'MAC id="' + access_token + '", ts="123456789",' + + ' nonce="abc123", mac="Xuk+9oqaaKyhitkgh1CD0xrI6+s="' + } + + def test_add_bearer_token(self): + """Test a number of bearer token placements""" + + # Invalid token type + client = Client(self.client_id, token_type="invalid") + self.assertRaises(ValueError, client.add_token, self.uri) + + # Case-insensitive token type + client = Client(self.client_id, access_token=self.access_token, token_type="bEAreR") + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers) + self.assertURLEqual(uri, self.uri) + self.assertFormBodyEqual(body, self.body) + self.assertEqual(headers, self.bearer_header) + + # Missing access token + client = Client(self.client_id) + self.assertRaises(ValueError, client.add_token, self.uri) + + # The default token placement, bearer in auth header + client = Client(self.client_id, access_token=self.access_token) + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers) + self.assertURLEqual(uri, self.uri) + self.assertFormBodyEqual(body, self.body) + self.assertEqual(headers, self.bearer_header) + + # Setting default placements of tokens + client = Client(self.client_id, access_token=self.access_token, + default_token_placement=AUTH_HEADER) + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers) + self.assertURLEqual(uri, self.uri) + self.assertFormBodyEqual(body, self.body) + self.assertEqual(headers, self.bearer_header) + + client = Client(self.client_id, access_token=self.access_token, + default_token_placement=URI_QUERY) + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers) + self.assertURLEqual(uri, self.bearer_query) + self.assertFormBodyEqual(body, self.body) + self.assertEqual(headers, self.headers) + + client = Client(self.client_id, access_token=self.access_token, + default_token_placement=BODY) + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers) + self.assertURLEqual(uri, self.uri) + self.assertFormBodyEqual(body, self.bearer_body) + self.assertEqual(headers, self.headers) + + # Asking for specific placement in the add_token method + client = Client(self.client_id, access_token=self.access_token) + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers, token_placement=AUTH_HEADER) + self.assertURLEqual(uri, self.uri) + self.assertFormBodyEqual(body, self.body) + self.assertEqual(headers, self.bearer_header) + + client = Client(self.client_id, access_token=self.access_token) + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers, token_placement=URI_QUERY) + self.assertURLEqual(uri, self.bearer_query) + self.assertFormBodyEqual(body, self.body) + self.assertEqual(headers, self.headers) + + client = Client(self.client_id, access_token=self.access_token) + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers, token_placement=BODY) + self.assertURLEqual(uri, self.uri) + self.assertFormBodyEqual(body, self.bearer_body) + self.assertEqual(headers, self.headers) + + # Invalid token placement + client = Client(self.client_id, access_token=self.access_token) + self.assertRaises(ValueError, client.add_token, self.uri, body=self.body, + headers=self.headers, token_placement="invalid") + + client = Client(self.client_id, access_token=self.access_token, + default_token_placement="invalid") + self.assertRaises(ValueError, client.add_token, self.uri, body=self.body, + headers=self.headers) + + def test_add_mac_token(self): + # Missing access token + client = Client(self.client_id, token_type="MAC") + self.assertRaises(ValueError, client.add_token, self.uri) + + # Invalid hash algorithm + client = Client(self.client_id, token_type="MAC", + access_token=self.access_token, mac_key=self.mac_key, + mac_algorithm="hmac-sha-2") + self.assertRaises(ValueError, client.add_token, self.uri) + + orig_generate_timestamp = common.generate_timestamp + orig_generate_nonce = common.generate_nonce + orig_generate_age = utils.generate_age + self.addCleanup(setattr, common, 'generage_timestamp', orig_generate_timestamp) + self.addCleanup(setattr, common, 'generage_nonce', orig_generate_nonce) + self.addCleanup(setattr, utils, 'generate_age', orig_generate_age) + common.generate_timestamp = lambda: '123456789' + common.generate_nonce = lambda: 'abc123' + utils.generate_age = lambda *args: 0 + + # Add the Authorization header (draft 00) + client = Client(self.client_id, token_type="MAC", + access_token=self.access_token, mac_key=self.mac_key, + mac_algorithm="hmac-sha-1") + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers, issue_time=datetime.datetime.now()) + self.assertEqual(uri, self.uri) + self.assertEqual(body, self.body) + self.assertEqual(headers, self.mac_00_header) + + # Add the Authorization header (draft 00) + client = Client(self.client_id, token_type="MAC", + access_token=self.access_token, mac_key=self.mac_key, + mac_algorithm="hmac-sha-1") + uri, headers, body = client.add_token(self.uri, body=self.body, + headers=self.headers, draft=1) + self.assertEqual(uri, self.uri) + self.assertEqual(body, self.body) + self.assertEqual(headers, self.mac_01_header) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_legacy_application.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_legacy_application.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_legacy_application.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_legacy_application.py 2013-10-04 10:15:45.000000000 +0000 @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals + +from mock import patch + +from oauthlib.oauth2 import LegacyApplicationClient + +from ....unittest import TestCase + + +@patch('time.time', new=lambda: 1000) +class LegacyApplicationClientTest(TestCase): + + client_id = "someclientid" + scope = ["/profile"] + kwargs = { + "some": "providers", + "require": "extra arguments" + } + + username = "foo" + password = "bar" + body = "not=empty" + + body_up = "not=empty&grant_type=password&username=%s&password=%s" % (username, password) + body_kwargs = body_up + "&some=providers&require=extra+arguments" + + token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' + ' "token_type":"example",' + ' "expires_in":3600,' + ' "scope":"/profile",' + ' "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",' + ' "example_parameter":"example_value"}') + token = { + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": 3600, + "expires_at": 4600, + "scope": scope, + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value" + } + + def test_request_body(self): + client = LegacyApplicationClient(self.client_id) + + # Basic, no extra arguments + body = client.prepare_request_body(self.username, self.password, + body=self.body) + self.assertFormBodyEqual(body, self.body_up) + + # With extra parameters + body = client.prepare_request_body(self.username, self.password, + body=self.body, **self.kwargs) + self.assertFormBodyEqual(body, self.body_kwargs) + + def test_parse_token_response(self): + client = LegacyApplicationClient(self.client_id) + + # Parse code and state + response = client.parse_request_body_response(self.token_json, scope=self.scope) + self.assertEqual(response, self.token) + self.assertEqual(client.access_token, response.get("access_token")) + self.assertEqual(client.refresh_token, response.get("refresh_token")) + self.assertEqual(client.token_type, response.get("token_type")) + + # Mismatching state + self.assertRaises(Warning, client.parse_request_body_response, self.token_json, scope="invalid") diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_mobile_application.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_mobile_application.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_mobile_application.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_mobile_application.py 2013-10-04 10:16:35.000000000 +0000 @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals + +from mock import patch + +from oauthlib.oauth2 import MobileApplicationClient + +from ....unittest import TestCase + + +@patch('time.time', new=lambda: 1000) +class MobileApplicationClientTest(TestCase): + + client_id = "someclientid" + uri = "https://example.com/path?query=world" + uri_id = uri + "&response_type=token&client_id=" + client_id + uri_redirect = uri_id + "&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback" + redirect_uri = "http://my.page.com/callback" + scope = ["/profile"] + state = "xyz" + uri_scope = uri_id + "&scope=%2Fprofile" + uri_state = uri_id + "&state=" + state + kwargs = { + "some": "providers", + "require": "extra arguments" + } + uri_kwargs = uri_id + "&some=providers&require=extra+arguments" + + code = "zzzzaaaa" + + response_uri = ('https://client.example.com/cb?#' + 'access_token=2YotnFZFEjr1zCsicMWpAA&' + 'token_type=example&' + 'expires_in=3600&' + 'scope=%2Fprofile&' + 'example_parameter=example_value') + token = { + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": "3600", + "expires_at": 4600, + "scope": scope, + "example_parameter": "example_value" + } + + def test_implicit_token_uri(self): + client = MobileApplicationClient(self.client_id) + + # Basic, no extra arguments + uri = client.prepare_request_uri(self.uri) + self.assertURLEqual(uri, self.uri_id) + + # With redirection uri + uri = client.prepare_request_uri(self.uri, redirect_uri=self.redirect_uri) + self.assertURLEqual(uri, self.uri_redirect) + + # With scope + uri = client.prepare_request_uri(self.uri, scope=self.scope) + self.assertURLEqual(uri, self.uri_scope) + + # With state + uri = client.prepare_request_uri(self.uri, state=self.state) + self.assertURLEqual(uri, self.uri_state) + + # With extra parameters through kwargs + uri = client.prepare_request_uri(self.uri, **self.kwargs) + self.assertURLEqual(uri, self.uri_kwargs) + + def test_parse_token_response(self): + client = MobileApplicationClient(self.client_id) + + # Parse code and state + response = client.parse_request_uri_response(self.response_uri, scope=self.scope) + self.assertEqual(response, self.token) + self.assertEqual(client.access_token, response.get("access_token")) + self.assertEqual(client.refresh_token, response.get("refresh_token")) + self.assertEqual(client.token_type, response.get("token_type")) + + # Mismatching scope + self.assertRaises(Warning, client.parse_request_uri_response, self.response_uri, scope="invalid") diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_web_application.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_web_application.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/clients/test_web_application.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/clients/test_web_application.py 2013-10-04 10:14:29.000000000 +0000 @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals + +import datetime + +from mock import patch + +from oauthlib import common +from oauthlib.oauth2.rfc6749 import utils, errors +from oauthlib.oauth2 import Client +from oauthlib.oauth2 import WebApplicationClient +from oauthlib.oauth2 import MobileApplicationClient +from oauthlib.oauth2 import LegacyApplicationClient +from oauthlib.oauth2 import BackendApplicationClient +from oauthlib.oauth2.rfc6749.clients import AUTH_HEADER, URI_QUERY, BODY + +from ....unittest import TestCase + + +@patch('time.time', new=lambda: 1000) +class WebApplicationClientTest(TestCase): + + client_id = "someclientid" + uri = "https://example.com/path?query=world" + uri_id = uri + "&response_type=code&client_id=" + client_id + uri_redirect = uri_id + "&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback" + redirect_uri = "http://my.page.com/callback" + scope = ["/profile"] + state = "xyz" + uri_scope = uri_id + "&scope=%2Fprofile" + uri_state = uri_id + "&state=" + state + kwargs = { + "some": "providers", + "require": "extra arguments" + } + uri_kwargs = uri_id + "&some=providers&require=extra+arguments" + + code = "zzzzaaaa" + body = "not=empty" + + body_code = "not=empty&grant_type=authorization_code&code=%s&client_id=%s" % (code, client_id) + body_redirect = body_code + "&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback" + body_kwargs = body_code + "&some=providers&require=extra+arguments" + + response_uri = "https://client.example.com/cb?code=zzzzaaaa&state=xyz" + response = {"code": "zzzzaaaa", "state": "xyz"} + + token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' + ' "token_type":"example",' + ' "expires_in":3600,' + ' "scope":"/profile",' + ' "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",' + ' "example_parameter":"example_value"}') + token = { + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": 3600, + "expires_at": 4600, + "scope": scope, + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value" + } + + def test_auth_grant_uri(self): + client = WebApplicationClient(self.client_id) + + # Basic, no extra arguments + uri = client.prepare_request_uri(self.uri) + self.assertURLEqual(uri, self.uri_id) + + # With redirection uri + uri = client.prepare_request_uri(self.uri, redirect_uri=self.redirect_uri) + self.assertURLEqual(uri, self.uri_redirect) + + # With scope + uri = client.prepare_request_uri(self.uri, scope=self.scope) + self.assertURLEqual(uri, self.uri_scope) + + # With state + uri = client.prepare_request_uri(self.uri, state=self.state) + self.assertURLEqual(uri, self.uri_state) + + # With extra parameters through kwargs + uri = client.prepare_request_uri(self.uri, **self.kwargs) + self.assertURLEqual(uri, self.uri_kwargs) + + def test_request_body(self): + client = WebApplicationClient(self.client_id, code=self.code) + + # Basic, no extra arguments + body = client.prepare_request_body(body=self.body) + self.assertFormBodyEqual(body, self.body_code) + + rclient = WebApplicationClient(self.client_id) + body = rclient.prepare_request_body(code=self.code, body=self.body) + self.assertFormBodyEqual(body, self.body_code) + + # With redirection uri + body = client.prepare_request_body(body=self.body, redirect_uri=self.redirect_uri) + self.assertFormBodyEqual(body, self.body_redirect) + + # With extra parameters + body = client.prepare_request_body(body=self.body, **self.kwargs) + self.assertFormBodyEqual(body, self.body_kwargs) + + def test_parse_grant_uri_response(self): + client = WebApplicationClient(self.client_id) + + # Parse code and state + response = client.parse_request_uri_response(self.response_uri, state=self.state) + self.assertEqual(response, self.response) + self.assertEqual(client.code, self.code) + + # Mismatching state + self.assertRaises(errors.MismatchingStateError, + client.parse_request_uri_response, + self.response_uri, + state="invalid") + + def test_parse_token_response(self): + client = WebApplicationClient(self.client_id) + + # Parse code and state + response = client.parse_request_body_response(self.token_json, scope=self.scope) + self.assertEqual(response, self.token) + self.assertEqual(client.access_token, response.get("access_token")) + self.assertEqual(client.refresh_token, response.get("refresh_token")) + self.assertEqual(client.token_type, response.get("token_type")) + + # Mismatching state + self.assertRaises(Warning, client.parse_request_body_response, self.token_json, scope="invalid") diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_base_endpoint.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_base_endpoint.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_base_endpoint.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_base_endpoint.py 2013-09-11 18:06:52.000000000 +0000 @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ....unittest import TestCase + +from oauthlib.oauth2.rfc6749 import BaseEndpoint, catch_errors_and_unavailability +from oauthlib.oauth2 import Server, RequestValidator, FatalClientError, OAuth2Error + + +class BaseEndpointTest(TestCase): + + def test_default_config(self): + endpoint = BaseEndpoint() + self.assertFalse(endpoint.catch_errors) + self.assertTrue(endpoint.available) + endpoint.catch_errors = True + self.assertTrue(endpoint.catch_errors) + endpoint.available = False + self.assertFalse(endpoint.available) + + def test_error_catching(self): + validator = RequestValidator() + server = Server(validator) + server.catch_errors = True + h, b, s = server.create_authorization_response('https://example.com') + self.assertIn("server_error", b) + self.assertEqual(s, 500) + + def test_unavailability(self): + validator = RequestValidator() + server = Server(validator) + server.available = False + h, b, s = server.create_authorization_response('https://example.com') + self.assertIn("temporarily_unavailable", b) + self.assertEqual(s, 503) + + def test_wrapper(self): + + class TestServer(Server): + + @catch_errors_and_unavailability + def throw_error(self, uri): + raise ValueError() + + @catch_errors_and_unavailability + def throw_oauth_error(self, uri): + raise OAuth2Error() + + @catch_errors_and_unavailability + def throw_fatal_oauth_error(self, uri): + raise FatalClientError() + + validator = RequestValidator() + server = TestServer(validator) + + server.catch_errors = True + h, b, s = server.throw_error('a') + self.assertIn("server_error", b) + self.assertEqual(s, 500) + + server.available = False + h, b, s = server.throw_error('a') + self.assertIn("temporarily_unavailable", b) + self.assertEqual(s, 503) + + server.available = True + self.assertRaises(OAuth2Error, server.throw_oauth_error, 'a') + self.assertRaises(FatalClientError, server.throw_fatal_oauth_error, 'a') + server.catch_errors = False + self.assertRaises(OAuth2Error, server.throw_oauth_error, 'a') + self.assertRaises(FatalClientError, server.throw_fatal_oauth_error, 'a') diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_client_authentication.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_client_authentication.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_client_authentication.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_client_authentication.py 2013-09-12 08:52:16.000000000 +0000 @@ -0,0 +1,103 @@ +"""Client authentication tests across all endpoints. + +Client authentication in OAuth2 serve two purposes, to authenticate +confidential clients and to ensure public clients are in fact public. The +latter is achieved with authenticate_client_id and the former with +authenticate_client. + +We make sure authentication is done by requiring a client object to be set +on the request object with a client_id parameter. The client_id attribute +prevents this check from being circumvented with a client form parameter. +""" +from __future__ import absolute_import, unicode_literals +import json +import mock + +from .test_utils import get_fragment_credentials +from ....unittest import TestCase + +from oauthlib.oauth2 import RequestValidator +from oauthlib.oauth2 import WebApplicationServer, MobileApplicationServer +from oauthlib.oauth2 import LegacyApplicationServer, BackendApplicationServer + + +class ClientAuthenticationTest(TestCase): + + def inspect_client(self, request, refresh_token=False): + if not request.client or not request.client.client_id: + raise ValueError() + return 'abc' + + def setUp(self): + self.validator = mock.MagicMock(spec=RequestValidator) + self.validator.get_default_redirect_uri.return_value = 'http://i.b./path' + self.web = WebApplicationServer(self.validator, + token_generator=self.inspect_client) + self.mobile = MobileApplicationServer(self.validator, + token_generator=self.inspect_client) + self.legacy = LegacyApplicationServer(self.validator, + token_generator=self.inspect_client) + self.backend = BackendApplicationServer(self.validator, + token_generator=self.inspect_client) + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def set_client_id(self, client_id, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def set_username(self, username, password, client, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def test_client_id_authentication(self): + token_uri = 'http://example.com/path' + + # authorization code grant + self.validator.authenticate_client.return_value = False + self.validator.authenticate_client_id.return_value = False + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=mock') + self.assertEqual(json.loads(body)['error'], 'invalid_client') + + self.validator.authenticate_client_id.return_value = True + self.validator.authenticate_client.side_effect = self.set_client + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=mock') + self.assertIn('access_token', json.loads(body)) + + # implicit grant + auth_uri = 'http://example.com/path?client_id=abc&response_type=token' + self.assertRaises(ValueError, self.mobile.create_authorization_response, + auth_uri, scopes=['random']) + + self.validator.validate_client_id.side_effect = self.set_client_id + h, _, s = self.mobile.create_authorization_response(auth_uri, scopes=['random']) + self.assertEqual(302, s) + self.assertIn('Location', h) + self.assertIn('access_token', get_fragment_credentials(h['Location'])) + + def test_custom_authentication(self): + token_uri = 'http://example.com/path' + + # authorization code grant + self.assertRaises(NotImplementedError, + self.web.create_token_response, token_uri, + body='grant_type=authorization_code&code=mock') + + # password grant + self.validator.authenticate_client.return_value = True + self.assertRaises(NotImplementedError, + self.legacy.create_token_response, token_uri, + body='grant_type=password&username=abc&password=secret') + + # client credentials grant + self.validator.authenticate_client.return_value = True + self.assertRaises(NotImplementedError, + self.backend.create_token_response, token_uri, + body='grant_type=client_credentials') diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py 2013-09-12 08:52:10.000000000 +0000 @@ -0,0 +1,116 @@ +"""Ensure credentials are preserved through the authorization. + +The Authorization Code Grant will need to preserve state as well as redirect +uri and the Implicit Grant will need to preserve state. +""" +from __future__ import absolute_import, unicode_literals +import json +import mock + +from .test_utils import get_query_credentials, get_fragment_credentials +from ....unittest import TestCase + +from oauthlib.oauth2 import RequestValidator +from oauthlib.oauth2 import WebApplicationServer, MobileApplicationServer +from oauthlib.oauth2.rfc6749 import errors + + +class PreservationTest(TestCase): + + DEFAULT_REDIRECT_URI = 'http://i.b./path' + + def setUp(self): + self.validator = mock.MagicMock(spec=RequestValidator) + self.validator.get_default_redirect_uri.return_value = self.DEFAULT_REDIRECT_URI + self.validator.authenticate_client.side_effect = self.set_client + self.web = WebApplicationServer(self.validator) + self.mobile = MobileApplicationServer(self.validator) + + def set_state(self, state): + def set_request_state(client_id, code, client, request): + request.state = state + return True + return set_request_state + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def test_state_preservation(self): + auth_uri = 'http://example.com/path?state=xyz&client_id=abc&response_type=' + token_uri = 'http://example.com/path' + + # authorization grant + h, _, s = self.web.create_authorization_response( + auth_uri + 'code', scopes=['random']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] + self.validator.validate_code.side_effect = self.set_state('xyz') + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=%s' % code) + self.assertEqual(json.loads(body)['state'], 'xyz') + + # implicit grant + h, _, s = self.mobile.create_authorization_response( + auth_uri + 'token', scopes=['random']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['state'][0], 'xyz') + + def test_redirect_uri_preservation(self): + auth_uri = 'http://example.com/path?redirect_uri=http%3A%2F%2Fi.b%2Fpath&client_id=abc' + redirect_uri = 'http://i.b/path' + token_uri = 'http://example.com/path' + + # authorization grant + h, _, s = self.web.create_authorization_response( + auth_uri + '&response_type=code', scopes=['random']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertTrue(h['Location'].startswith(redirect_uri)) + + # confirm_redirect_uri should return false if the redirect uri + # was given in the authorization but not in the token request. + self.validator.confirm_redirect_uri.return_value = False + code = get_query_credentials(h['Location'])['code'][0] + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=%s' % code) + self.assertEqual(json.loads(body)['error'], 'access_denied') + + # implicit grant + h, _, s = self.mobile.create_authorization_response( + auth_uri + '&response_type=token', scopes=['random']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertTrue(h['Location'].startswith(redirect_uri)) + + def test_invalid_redirect_uri(self): + auth_uri = 'http://example.com/path?redirect_uri=http%3A%2F%2Fi.b%2Fpath&client_id=abc' + self.validator.validate_redirect_uri.return_value = False + + # authorization grant + self.assertRaises(errors.MismatchingRedirectURIError, + self.web.create_authorization_response, + auth_uri + '&response_type=code', scopes=['random']) + + # implicit grant + self.assertRaises(errors.MismatchingRedirectURIError, + self.mobile.create_authorization_response, + auth_uri + '&response_type=token', scopes=['random']) + + def test_default_uri(self): + auth_uri = 'http://example.com/path?state=xyz&client_id=abc' + + self.validator.get_default_redirect_uri.return_value = None + + # authorization grant + self.assertRaises(errors.MissingRedirectURIError, + self.web.create_authorization_response, + auth_uri + '&response_type=code', scopes=['random']) + + # implicit grant + self.assertRaises(errors.MissingRedirectURIError, + self.mobile.create_authorization_response, + auth_uri + '&response_type=token', scopes=['random']) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_error_responses.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_error_responses.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_error_responses.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_error_responses.py 2013-09-12 08:52:05.000000000 +0000 @@ -0,0 +1,372 @@ +"""Ensure the correct error responses are returned for all defined error types. +""" +from __future__ import absolute_import, unicode_literals +import json +import mock + +from ....unittest import TestCase + +from oauthlib.oauth2 import RequestValidator +from oauthlib.oauth2 import WebApplicationServer, MobileApplicationServer +from oauthlib.oauth2 import LegacyApplicationServer, BackendApplicationServer +from oauthlib.oauth2.rfc6749 import errors + + +class ErrorResponseTest(TestCase): + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def setUp(self): + self.validator = mock.MagicMock(spec=RequestValidator) + self.validator.get_default_redirect_uri.return_value = None + self.web = WebApplicationServer(self.validator) + self.mobile = MobileApplicationServer(self.validator) + self.legacy = LegacyApplicationServer(self.validator) + self.backend = BackendApplicationServer(self.validator) + + def test_invalid_redirect_uri(self): + uri = 'https://example.com/authorize?client_id=foo&redirect_uri=wrong' + # Authorization code grant + self.assertRaises(errors.InvalidRedirectURIError, + self.web.validate_authorization_request, uri) + self.assertRaises(errors.InvalidRedirectURIError, + self.web.create_authorization_response, uri, scopes=['foo']) + + # Implicit grant + self.assertRaises(errors.InvalidRedirectURIError, + self.mobile.validate_authorization_request, uri) + self.assertRaises(errors.InvalidRedirectURIError, + self.mobile.create_authorization_response, uri, scopes=['foo']) + + def test_missing_redirect_uri(self): + uri = 'https://example.com/authorize?client_id=foo' + # Authorization code grant + self.assertRaises(errors.MissingRedirectURIError, + self.web.validate_authorization_request, uri) + self.assertRaises(errors.MissingRedirectURIError, + self.web.create_authorization_response, uri, scopes=['foo']) + + # Implicit grant + self.assertRaises(errors.MissingRedirectURIError, + self.mobile.validate_authorization_request, uri) + self.assertRaises(errors.MissingRedirectURIError, + self.mobile.create_authorization_response, uri, scopes=['foo']) + + def test_mismatching_redirect_uri(self): + uri = 'https://example.com/authorize?client_id=foo&redirect_uri=https%3A%2F%2Fi.b%2Fback' + # Authorization code grant + self.validator.validate_redirect_uri.return_value = False + self.assertRaises(errors.MismatchingRedirectURIError, + self.web.validate_authorization_request, uri) + self.assertRaises(errors.MismatchingRedirectURIError, + self.web.create_authorization_response, uri, scopes=['foo']) + + # Implicit grant + self.assertRaises(errors.MismatchingRedirectURIError, + self.mobile.validate_authorization_request, uri) + self.assertRaises(errors.MismatchingRedirectURIError, + self.mobile.create_authorization_response, uri, scopes=['foo']) + + def test_missing_client_id(self): + uri = 'https://example.com/authorize?redirect_uri=https%3A%2F%2Fi.b%2Fback' + # Authorization code grant + self.validator.validate_redirect_uri.return_value = False + self.assertRaises(errors.MissingClientIdError, + self.web.validate_authorization_request, uri) + self.assertRaises(errors.MissingClientIdError, + self.web.create_authorization_response, uri, scopes=['foo']) + + # Implicit grant + self.assertRaises(errors.MissingClientIdError, + self.mobile.validate_authorization_request, uri) + self.assertRaises(errors.MissingClientIdError, + self.mobile.create_authorization_response, uri, scopes=['foo']) + + def test_invalid_client_id(self): + uri = 'https://example.com/authorize?client_id=foo&redirect_uri=https%3A%2F%2Fi.b%2Fback' + # Authorization code grant + self.validator.validate_client_id.return_value = False + self.assertRaises(errors.InvalidClientIdError, + self.web.validate_authorization_request, uri) + self.assertRaises(errors.InvalidClientIdError, + self.web.create_authorization_response, uri, scopes=['foo']) + + # Implicit grant + self.assertRaises(errors.InvalidClientIdError, + self.mobile.validate_authorization_request, uri) + self.assertRaises(errors.InvalidClientIdError, + self.mobile.create_authorization_response, uri, scopes=['foo']) + + def test_invalid_request(self): + self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' + token_uri = 'https://i.b/token' + invalid_uris = [ + # Duplicate parameters + 'https://i.b/auth?client_id=foo&client_id=bar&response_type={0}', + # Missing response type + 'https://i.b/auth?client_id=foo', + ] + + # Authorization code grant + for uri in invalid_uris: + self.assertRaises(errors.InvalidRequestError, + self.web.validate_authorization_request, + uri.format('code')) + h, _, s = self.web.create_authorization_response( + uri.format('code'), scopes=['foo']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertIn('error=invalid_request', h['Location']) + invalid_bodies = [ + # duplicate params + 'grant_type=authorization_code&client_id=nope&client_id=nope&code=foo' + ] + for body in invalid_bodies: + _, body, _ = self.web.create_token_response(token_uri, + body=body) + self.assertEqual('invalid_request', json.loads(body)['error']) + + # Implicit grant + for uri in invalid_uris: + self.assertRaises(errors.InvalidRequestError, + self.mobile.validate_authorization_request, + uri.format('token')) + h, _, s = self.mobile.create_authorization_response( + uri.format('token'), scopes=['foo']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertIn('error=invalid_request', h['Location']) + + # Password credentials grant + invalid_bodies = [ + # duplicate params + 'grant_type=password&username=foo&username=bar&password=baz' + # missing username + 'grant_type=password&password=baz' + # missing password + 'grant_type=password&username=foo' + ] + self.validator.authenticate_client.side_effect = self.set_client + for body in invalid_bodies: + _, body, _ = self.legacy.create_token_response(token_uri, + body=body) + self.assertEqual('invalid_request', json.loads(body)['error']) + + # Client credentials grant + invalid_bodies = [ + # duplicate params + 'grant_type=client_credentials&scope=foo&scope=bar' + ] + for body in invalid_bodies: + _, body, _ = self.backend.create_token_response(token_uri, + body=body) + self.assertEqual('invalid_request', json.loads(body)['error']) + + def test_unauthorized_client(self): + self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' + self.validator.validate_grant_type.return_value = False + self.validator.validate_response_type.return_value = False + self.validator.authenticate_client.side_effect = self.set_client + token_uri = 'https://i.b/token' + + # Authorization code grant + self.assertRaises(errors.UnauthorizedClientError, + self.web.validate_authorization_request, + 'https://i.b/auth?response_type=code&client_id=foo') + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=foo') + self.assertEqual('unauthorized_client', json.loads(body)['error']) + + # Implicit grant + self.assertRaises(errors.UnauthorizedClientError, + self.mobile.validate_authorization_request, + 'https://i.b/auth?response_type=token&client_id=foo') + + # Password credentials grant + _, body, _ = self.legacy.create_token_response(token_uri, + body='grant_type=password&username=foo&password=bar') + self.assertEqual('unauthorized_client', json.loads(body)['error']) + + # Client credentials grant + _, body, _ = self.backend.create_token_response(token_uri, + body='grant_type=client_credentials') + self.assertEqual('unauthorized_client', json.loads(body)['error']) + + def test_access_denied(self): + self.validator.authenticate_client.side_effect = self.set_client + self.validator.confirm_redirect_uri.return_value = False + token_uri = 'https://i.b/token' + # Authorization code grant + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=foo') + self.assertEqual('access_denied', json.loads(body)['error']) + + def test_unsupported_response_type(self): + self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' + + # Authorization code grant + self.assertRaises(errors.UnsupportedResponseTypeError, + self.web.validate_authorization_request, + 'https://i.b/auth?response_type=foo&client_id=foo') + + # Implicit grant + self.assertRaises(errors.UnsupportedResponseTypeError, + self.mobile.validate_authorization_request, + 'https://i.b/auth?response_type=foo&client_id=foo') + + def test_invalid_scope(self): + self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' + self.validator.validate_scopes.return_value = False + self.validator.authenticate_client.side_effect = self.set_client + + # Authorization code grant + self.assertRaises(errors.InvalidScopeError, + self.web.validate_authorization_request, + 'https://i.b/auth?response_type=code&client_id=foo') + + # Implicit grant + self.assertRaises(errors.InvalidScopeError, + self.mobile.validate_authorization_request, + 'https://i.b/auth?response_type=token&client_id=foo') + + # Password credentials grant + _, body, _ = self.legacy.create_token_response( + 'https://i.b/token', + body='grant_type=password&username=foo&password=bar') + self.assertEqual('invalid_scope', json.loads(body)['error']) + + # Client credentials grant + _, body, _ = self.backend.create_token_response( + 'https://i.b/token', + body='grant_type=client_credentials') + self.assertEqual('invalid_scope', json.loads(body)['error']) + + def test_server_error(self): + def raise_error(*args, **kwargs): + raise ValueError() + + self.validator.validate_client_id.side_effect = raise_error + self.validator.authenticate_client.side_effect = raise_error + self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' + + # Authorization code grant + self.web.catch_errors = True + _, _, s = self.web.create_authorization_response( + 'https://i.b/auth?client_id=foo&response_type=code', + scopes=['foo']) + self.assertEqual(s, 500) + _, _, s = self.web.create_token_response( + 'https://i.b/token', + body='grant_type=authorization_code&code=foo', + scopes=['foo']) + self.assertEqual(s, 500) + + # Implicit grant + self.mobile.catch_errors = True + _, _, s = self.mobile.create_authorization_response( + 'https://i.b/auth?client_id=foo&response_type=token', + scopes=['foo']) + self.assertEqual(s, 500) + + # Password credentials grant + self.legacy.catch_errors = True + _, _, s = self.legacy.create_token_response( + 'https://i.b/token', + body='grant_type=password&username=foo&password=foo') + self.assertEqual(s, 500) + + # Client credentials grant + self.backend.catch_errors = True + _, _, s = self.backend.create_token_response( + 'https://i.b/token', + body='grant_type=client_credentials') + self.assertEqual(s, 500) + + def test_temporarily_unavailable(self): + # Authorization code grant + self.web.available = False + _, _, s = self.web.create_authorization_response( + 'https://i.b/auth?client_id=foo&response_type=code', + scopes=['foo']) + self.assertEqual(s, 503) + _, _, s = self.web.create_token_response( + 'https://i.b/token', + body='grant_type=authorization_code&code=foo', + scopes=['foo']) + self.assertEqual(s, 503) + + # Implicit grant + self.mobile.available = False + _, _, s = self.mobile.create_authorization_response( + 'https://i.b/auth?client_id=foo&response_type=token', + scopes=['foo']) + self.assertEqual(s, 503) + + # Password credentials grant + self.legacy.available = False + _, _, s = self.legacy.create_token_response( + 'https://i.b/token', + body='grant_type=password&username=foo&password=foo') + self.assertEqual(s, 503) + + # Client credentials grant + self.backend.available = False + _, _, s = self.backend.create_token_response( + 'https://i.b/token', + body='grant_type=client_credentials') + self.assertEqual(s, 503) + + def test_invalid_client(self): + self.validator.authenticate_client.return_value = False + self.validator.authenticate_client_id.return_value = False + + # Authorization code grant + _, body, _ = self.web.create_token_response('https://i.b/token', + body='grant_type=authorization_code&code=foo') + self.assertEqual('invalid_client', json.loads(body)['error']) + + # Password credentials grant + _, body, _ = self.legacy.create_token_response('https://i.b/token', + body='grant_type=password&username=foo&password=bar') + self.assertEqual('invalid_client', json.loads(body)['error']) + + # Client credentials grant + _, body, _ = self.legacy.create_token_response('https://i.b/token', + body='grant_type=client_credentials') + self.assertEqual('invalid_client', json.loads(body)['error']) + + def test_invalid_grant(self): + self.validator.authenticate_client.side_effect = self.set_client + + # Authorization code grant + self.validator.validate_code.return_value = False + _, body, _ = self.web.create_token_response('https://i.b/token', + body='grant_type=authorization_code&code=foo') + self.assertEqual('invalid_grant', json.loads(body)['error']) + + # Password credentials grant + self.validator.validate_user.return_value = False + _, body, _ = self.legacy.create_token_response('https://i.b/token', + body='grant_type=password&username=foo&password=bar') + self.assertEqual('invalid_grant', json.loads(body)['error']) + + def test_unsupported_grant_type(self): + self.validator.authenticate_client.side_effect = self.set_client + + # Authorization code grant + _, body, _ = self.web.create_token_response('https://i.b/token', + body='grant_type=bar&code=foo') + self.assertEqual('unsupported_grant_type', json.loads(body)['error']) + + # Password credentials grant + _, body, _ = self.legacy.create_token_response('https://i.b/token', + body='grant_type=bar&username=foo&password=bar') + self.assertEqual('unsupported_grant_type', json.loads(body)['error']) + + # Client credentials grant + _, body, _ = self.backend.create_token_response('https://i.b/token', + body='grant_type=bar') + self.assertEqual('unsupported_grant_type', json.loads(body)['error']) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_extra_credentials.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_extra_credentials.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_extra_credentials.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_extra_credentials.py 2013-09-12 08:52:00.000000000 +0000 @@ -0,0 +1,69 @@ +"""Ensure extra credentials can be supplied for inclusion in tokens. +""" +from __future__ import absolute_import, unicode_literals +import mock + +from ....unittest import TestCase + +from oauthlib.oauth2 import RequestValidator +from oauthlib.oauth2 import WebApplicationServer, MobileApplicationServer +from oauthlib.oauth2 import LegacyApplicationServer, BackendApplicationServer + + +class ExtraCredentialsTest(TestCase): + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def setUp(self): + self.validator = mock.MagicMock(spec=RequestValidator) + self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' + self.web = WebApplicationServer(self.validator) + self.mobile = MobileApplicationServer(self.validator) + self.legacy = LegacyApplicationServer(self.validator) + self.backend = BackendApplicationServer(self.validator) + + def test_post_authorization_request(self): + def save_code(client_id, token, request): + self.assertEqual('creds', request.extra) + + def save_token(token, request): + self.assertEqual('creds', request.extra) + + # Authorization code grant + self.validator.save_authorization_code.side_effect = save_code + self.web.create_authorization_response( + 'https://i.b/auth?client_id=foo&response_type=code', + scopes=['foo'], + credentials={'extra': 'creds'}) + + # Implicit grant + self.validator.save_bearer_token.side_effect = save_token + self.web.create_authorization_response( + 'https://i.b/auth?client_id=foo&response_type=token', + scopes=['foo'], + credentials={'extra': 'creds'}) + + def test_token_request(self): + def save_token(token, request): + self.assertIn('extra', token) + + self.validator.save_bearer_token.side_effect = save_token + self.validator.authenticate_client.side_effect = self.set_client + + # Authorization code grant + self.web.create_token_response('https://i.b/token', + body='grant_type=authorization_code&code=foo', + credentials={'extra': 'creds'}) + + # Password credentials grant + self.legacy.create_token_response('https://i.b/token', + body='grant_type=password&username=foo&password=bar', + credentials={'extra': 'creds'}) + + # Client credentials grant + self.backend.create_token_response('https://i.b/token', + body='grant_type=client_credentials', + credentials={'extra': 'creds'}) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py 2013-09-12 08:51:49.000000000 +0000 @@ -0,0 +1,106 @@ +"""Ensure all tokens are associated with a resource owner. +""" +from __future__ import absolute_import, unicode_literals +import json +import mock + +from .test_utils import get_query_credentials, get_fragment_credentials +from ....unittest import TestCase + +from oauthlib.oauth2 import RequestValidator +from oauthlib.oauth2 import WebApplicationServer, MobileApplicationServer +from oauthlib.oauth2 import LegacyApplicationServer, BackendApplicationServer + + +class ResourceOwnerAssociationTest(TestCase): + + auth_uri = 'http://example.com/path?client_id=abc' + token_uri = 'http://example.com/path' + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def set_user(self, client_id, code, client, request): + request.user = 'test' + return True + + def set_user_from_username(self, username, password, client, request): + request.user = 'test' + return True + + def set_user_from_credentials(self, request): + request.user = 'test' + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def inspect_client(self, request, refresh_token=False): + if not request.user: + raise ValueError() + return 'abc' + + def setUp(self): + self.validator = mock.MagicMock(spec=RequestValidator) + self.validator.get_default_redirect_uri.return_value = 'http://i.b./path' + self.validator.authenticate_client.side_effect = self.set_client + self.web = WebApplicationServer(self.validator, + token_generator=self.inspect_client) + self.mobile = MobileApplicationServer(self.validator, + token_generator=self.inspect_client) + self.legacy = LegacyApplicationServer(self.validator, + token_generator=self.inspect_client) + self.backend = BackendApplicationServer(self.validator, + token_generator=self.inspect_client) + + def test_web_application(self): + # TODO: code generator + intercept test + h, _, s = self.web.create_authorization_response( + self.auth_uri + '&response_type=code', + credentials={'user': 'test'}, scopes=['random']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] + self.assertRaises(ValueError, + self.web.create_token_response, self.token_uri, + body='grant_type=authorization_code&code=%s' % code) + + self.validator.validate_code.side_effect = self.set_user + _, body, _ = self.web.create_token_response(self.token_uri, + body='grant_type=authorization_code&code=%s' % code) + self.assertEqual(json.loads(body)['access_token'], 'abc') + + def test_mobile_application(self): + self.assertRaises(ValueError, + self.mobile.create_authorization_response, + self.auth_uri + '&response_type=token') + + h, _, s = self.mobile.create_authorization_response( + self.auth_uri + '&response_type=token', + credentials={'user': 'test'}, scopes=['random']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['access_token'][0], 'abc') + + def test_legacy_application(self): + body = 'grant_type=password&username=abc&password=secret' + self.assertRaises(ValueError, + self.legacy.create_token_response, + self.token_uri, body=body) + + self.validator.validate_user.side_effect = self.set_user_from_username + _, body, _ = self.legacy.create_token_response( + self.token_uri, body=body) + self.assertEqual(json.loads(body)['access_token'], 'abc') + + def test_backend_application(self): + body = 'grant_type=client_credentials' + self.assertRaises(ValueError, + self.backend.create_token_response, + self.token_uri, body=body) + + self.validator.authenticate_client.side_effect = self.set_user_from_credentials + _, body, _ = self.backend.create_token_response( + self.token_uri, body=body) + self.assertEqual(json.loads(body)['access_token'], 'abc') diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py 2013-09-17 12:19:37.000000000 +0000 @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals + +from json import loads +from mock import MagicMock + +from oauthlib.common import urlencode +from oauthlib.oauth2 import RequestValidator, RevocationEndpoint + +from ....unittest import TestCase + + +class RevocationEndpointTest(TestCase): + + def setUp(self): + self.validator = MagicMock(wraps=RequestValidator()) + self.validator.authenticate_client.return_value = True + self.validator.revoke_token.return_value = True + self.endpoint = RevocationEndpoint(self.validator) + + self.uri = 'https://example.com/revoke_token' + self.headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + } + + def test_revoke_token(self): + for token_type in ('access_token', 'refresh_token', 'invalid'): + body = urlencode([('token', 'foo'), + ('token_type_hint', token_type)]) + h, b, s = self.endpoint.create_revocation_response(self.uri, + headers=self.headers, body=body) + self.assertEqual(h, {}) + self.assertEqual(b, None) + self.assertEqual(s, 200) + + def test_revoke_with_callback(self): + callback = 'package.hello_world' + for token_type in ('access_token', 'refresh_token', 'invalid'): + body = urlencode([('token', 'foo'), + ('token_type_hint', token_type), + ('callback', callback)]) + h, b, s = self.endpoint.create_revocation_response(self.uri, + headers=self.headers, body=body) + self.assertEqual(h, {}) + self.assertEqual(b, callback + '()') + self.assertEqual(s, 200) + + def test_revoke_unsupported_token(self): + endpoint = RevocationEndpoint(self.validator, + supported_token_types=['access_token']) + body = urlencode([('token', 'foo'), + ('token_type_hint', 'refresh_token')]) + h, b, s = endpoint.create_revocation_response(self.uri, + headers=self.headers, body=body) + self.assertEqual(h, {}) + self.assertEqual(loads(b)['error'], 'unsupported_token_type') + self.assertEqual(s, 400) + + h, b, s = endpoint.create_revocation_response(self.uri, + headers=self.headers, body='') + self.assertEqual(h, {}) + self.assertEqual(loads(b)['error'], 'invalid_request') + self.assertEqual(s, 400) + + diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_scope_handling.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_scope_handling.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_scope_handling.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_scope_handling.py 2013-09-12 08:51:40.000000000 +0000 @@ -0,0 +1,182 @@ +"""Ensure scope is preserved across authorization. + +Fairly trivial in all grants except the Authorization Code Grant where scope +need to be persisted temporarily in an authorization code. +""" +from __future__ import absolute_import, unicode_literals +import json +import mock + +from .test_utils import get_query_credentials, get_fragment_credentials +from ....unittest import TestCase + +from oauthlib.oauth2 import RequestValidator +from oauthlib.oauth2 import WebApplicationServer, MobileApplicationServer +from oauthlib.oauth2 import LegacyApplicationServer, BackendApplicationServer + + +class TestScopeHandling(TestCase): + + DEFAULT_REDIRECT_URI = 'http://i.b./path' + + def set_scopes(self, scopes): + def set_request_scopes(client_id, code, client, request): + request.scopes = scopes + return True + return set_request_scopes + + def set_user(self, request): + request.user = 'foo' + request.client_id = 'bar' + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def setUp(self): + self.validator = mock.MagicMock(spec=RequestValidator) + self.validator.get_default_redirect_uri.return_value = TestScopeHandling.DEFAULT_REDIRECT_URI + self.validator.authenticate_client.side_effect = self.set_client + self.web = WebApplicationServer(self.validator) + self.mobile = MobileApplicationServer(self.validator) + self.legacy = LegacyApplicationServer(self.validator) + self.backend = BackendApplicationServer(self.validator) + + def test_scope_extraction(self): + scopes = ( + ('images', ['images']), + ('images+videos', ['images', 'videos']), + ('http%3A%2f%2fa.b%2fvideos', ['http://a.b/videos']), + ('http%3A%2f%2fa.b%2fvideos+pics', ['http://a.b/videos', 'pics']), + ('pics+http%3A%2f%2fa.b%2fvideos', ['pics', 'http://a.b/videos']), + ('http%3A%2f%2fa.b%2fvideos+https%3A%2f%2fc.d%2Fsecret', ['http://a.b/videos', 'https://c.d/secret']), + ) + + uri = 'http://example.com/path?client_id=abc&scope=%s&response_type=%s' + for scope, correct_scopes in scopes: + scopes, _ = self.web.validate_authorization_request( + uri % (scope, 'code')) + self.assertItemsEqual(scopes, correct_scopes) + scopes, _ = self.mobile.validate_authorization_request( + uri % (scope, 'token')) + self.assertItemsEqual(scopes, correct_scopes) + + def test_scope_preservation(self): + scope = 'pics+http%3A%2f%2fa.b%2fvideos' + decoded_scope = 'pics http://a.b/videos' + auth_uri = 'http://example.com/path?client_id=abc&response_type=' + token_uri = 'http://example.com/path' + + # authorization grant + h, _, s = self.web.create_authorization_response( + auth_uri + 'code', scopes=decoded_scope.split(' ')) + self.validator.validate_code.side_effect = self.set_scopes(decoded_scope.split(' ')) + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=%s' % code) + self.assertEqual(json.loads(body)['scope'], decoded_scope) + + # implicit grant + h, _, s = self.mobile.create_authorization_response( + auth_uri + 'token', scopes=decoded_scope.split(' ')) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['scope'][0], decoded_scope) + + # resource owner password credentials grant + body = 'grant_type=password&username=abc&password=secret&scope=%s' + + _, body, _ = self.legacy.create_token_response(token_uri, + body=body % scope) + self.assertEqual(json.loads(body)['scope'], decoded_scope) + + # client credentials grant + body = 'grant_type=client_credentials&scope=%s' + self.validator.authenticate_client.side_effect = self.set_user + _, body, _ = self.backend.create_token_response(token_uri, + body=body % scope) + self.assertEqual(json.loads(body)['scope'], decoded_scope) + + def test_scope_changed(self): + scope = 'pics+http%3A%2f%2fa.b%2fvideos' + scopes = ['images', 'http://a.b/videos'] + decoded_scope = 'images http://a.b/videos' + auth_uri = 'http://example.com/path?client_id=abc&response_type=' + token_uri = 'http://example.com/path' + + # authorization grant + h, _, s = self.web.create_authorization_response( + auth_uri + 'code', scopes=scopes) + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] + self.validator.validate_code.side_effect = self.set_scopes(scopes) + _, body, _ = self.web.create_token_response(token_uri, + body='grant_type=authorization_code&code=%s' % code) + self.assertEqual(json.loads(body)['scope'], decoded_scope) + + # implicit grant + self.validator.validate_scopes.side_effect = self.set_scopes(scopes) + h, _, s = self.mobile.create_authorization_response( + auth_uri + 'token', scopes=scopes) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['scope'][0], decoded_scope) + + # resource owner password credentials grant + self.validator.validate_scopes.side_effect = self.set_scopes(scopes) + body = 'grant_type=password&username=abc&password=secret&scope=%s' + _, body, _ = self.legacy.create_token_response(token_uri, + body=body % scope) + self.assertEqual(json.loads(body)['scope'], decoded_scope) + + # client credentials grant + self.validator.validate_scopes.side_effect = self.set_scopes(scopes) + self.validator.authenticate_client.side_effect = self.set_user + body = 'grant_type=client_credentials&scope=%s' + _, body, _ = self.backend.create_token_response(token_uri, + body=body % scope) + + self.assertEqual(json.loads(body)['scope'], decoded_scope) + + def test_invalid_scope(self): + scope = 'pics+http%3A%2f%2fa.b%2fvideos' + auth_uri = 'http://example.com/path?client_id=abc&response_type=' + token_uri = 'http://example.com/path' + + self.validator.validate_scopes.return_value = False + + # authorization grant + h, _, s = self.web.create_authorization_response( + auth_uri + 'code', scopes=['invalid']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + error = get_query_credentials(h['Location'])['error'][0] + self.assertEqual(error, 'invalid_scope') + + # implicit grant + h, _, s = self.mobile.create_authorization_response( + auth_uri + 'token', scopes=['invalid']) + self.assertEqual(s, 302) + self.assertIn('Location', h) + error = get_fragment_credentials(h['Location'])['error'][0] + self.assertEqual(error, 'invalid_scope') + + # resource owner password credentials grant + body = 'grant_type=password&username=abc&password=secret&scope=%s' + _, body, _ = self.legacy.create_token_response(token_uri, + body=body % scope) + self.assertEqual(json.loads(body)['error'], 'invalid_scope') + + # client credentials grant + self.validator.authenticate_client.side_effect = self.set_user + body = 'grant_type=client_credentials&scope=%s' + _, body, _ = self.backend.create_token_response(token_uri, + body=body % scope) + self.assertEqual(json.loads(body)['error'], 'invalid_scope') diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_utils.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_utils.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/endpoints/test_utils.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/endpoints/test_utils.py 2013-09-12 08:38:47.000000000 +0000 @@ -0,0 +1,14 @@ +try: + import urlparse +except ImportError: + import urllib.parse as urlparse + + +def get_query_credentials(uri): + return urlparse.parse_qs(urlparse.urlparse(uri).query, + keep_blank_values=True) + + +def get_fragment_credentials(uri): + return urlparse.parse_qs(urlparse.urlparse(uri).fragment, + keep_blank_values=True) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py 2013-10-04 10:24:45.000000000 +0000 @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ....unittest import TestCase + +import json +import mock +from oauthlib.common import Request +from oauthlib.oauth2.rfc6749 import errors +from oauthlib.oauth2.rfc6749.grant_types import AuthorizationCodeGrant +from oauthlib.oauth2.rfc6749.tokens import BearerToken + + +class AuthorizationCodeGrantTest(TestCase): + + def setUp(self): + self.request = Request('http://a.b/path') + self.request.scopes = ('hello', 'world') + self.request.expires_in = 1800 + self.request.client = 'batman' + self.request.client_id = 'abcdef' + self.request.code = '1234' + self.request.response_type = 'code' + self.request.grant_type = 'authorization_code' + + self.request_state = Request('http://a.b/path') + self.request_state.state = 'abc' + + self.mock_validator = mock.MagicMock() + self.mock_validator.authenticate_client.side_effect = self.set_client + self.auth = AuthorizationCodeGrant(request_validator=self.mock_validator) + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def test_create_authorization_grant(self): + grant = self.auth.create_authorization_code(self.request) + self.assertIn('code', grant) + + grant = self.auth.create_authorization_code(self.request_state) + self.assertIn('code', grant) + self.assertIn('state', grant) + + def test_create_token_response(self): + bearer = BearerToken(self.mock_validator) + h, token, s = self.auth.create_token_response(self.request, bearer) + token = json.loads(token) + self.assertIn('access_token', token) + self.assertIn('refresh_token', token) + self.assertIn('expires_in', token) + self.assertIn('scope', token) + + def test_invalid_request(self): + del self.request.code + self.assertRaises(errors.InvalidRequestError, self.auth.validate_token_request, + self.request) + + def test_invalid_request_duplicates(self): + request = mock.MagicMock(wraps=self.request) + request.grant_type = 'authorization_code' + request.duplicate_params = ['client_id'] + self.assertRaises(errors.InvalidRequestError, self.auth.validate_token_request, + request) + + def test_authentication_required(self): + """ + ensure client_authentication_required() is properly called + """ + self.auth.validate_token_request(self.request) + self.mock_validator.client_authentication_required.assert_called_once_with(self.request) + + def test_authenticate_client(self): + self.mock_validator.authenticate_client.side_effect = None + self.mock_validator.authenticate_client.return_value = False + self.assertRaises(errors.InvalidClientError, self.auth.validate_token_request, + self.request) + + def test_client_id_missing(self): + self.mock_validator.authenticate_client.side_effect = None + request = mock.MagicMock(wraps=self.request) + request.grant_type = 'authorization_code' + del request.client.client_id + self.assertRaises(NotImplementedError, self.auth.validate_token_request, + request) + + def test_invalid_grant(self): + self.request.client = 'batman' + self.mock_validator.authenticate_client = self.set_client + self.mock_validator.validate_code.return_value = False + self.assertRaises(errors.InvalidGrantError, + self.auth.validate_token_request, self.request) + + def test_invalid_grant_type(self): + self.request.grant_type = 'foo' + self.assertRaises(errors.UnsupportedGrantTypeError, + self.auth.validate_token_request, self.request) + + def test_authenticate_client_id(self): + self.mock_validator.client_authentication_required.return_value = False + self.mock_validator.authenticate_client_id.return_value = False + self.request.state = 'abc' + self.assertRaises(errors.InvalidClientError, + self.auth.validate_token_request, self.request) + + def test_invalid_redirect_uri(self): + self.mock_validator.confirm_redirect_uri.return_value = False + self.assertRaises(errors.AccessDeniedError, + self.auth.validate_token_request, self.request) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_client_credentials.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_client_credentials.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_client_credentials.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_client_credentials.py 2014-01-20 10:40:48.000000000 +0000 @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ....unittest import TestCase + +import json +import mock +from oauthlib.common import Request +from oauthlib.oauth2.rfc6749.grant_types import ClientCredentialsGrant +from oauthlib.oauth2.rfc6749.tokens import BearerToken + + +class ClientCredentialsGrantTest(TestCase): + + def setUp(self): + mock_client = mock.MagicMock() + mock_client.user.return_value = 'mocked user' + self.request = Request('http://a.b/path') + self.request.grant_type = 'client_credentials' + self.request.client = mock_client + self.request.scopes = ('mocked', 'scopes') + self.mock_validator = mock.MagicMock() + self.auth = ClientCredentialsGrant( + request_validator=self.mock_validator) + + def test_create_token_response(self): + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertIn('access_token', token) + self.assertIn('token_type', token) + self.assertIn('expires_in', token) + self.assertIn('Content-Type', headers) + self.assertEqual(headers['Content-Type'], 'application/json') + + def test_error_response(self): + bearer = BearerToken(self.mock_validator) + self.mock_validator.authenticate_client.return_value = False + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + error_msg = json.loads(body) + self.assertIn('error', error_msg) + self.assertEqual(error_msg['error'], 'invalid_client') + self.assertIn('Content-Type', headers) + self.assertEqual(headers['Content-Type'], 'application/json') + + def test_validate_token_response(self): + # wrong grant type, scope + pass diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_implicit.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_implicit.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_implicit.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_implicit.py 2013-09-12 09:00:29.000000000 +0000 @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ....unittest import TestCase + +import mock +from oauthlib import common +from oauthlib.common import Request +from oauthlib.oauth2.rfc6749.grant_types import ImplicitGrant +from oauthlib.oauth2.rfc6749.tokens import BearerToken + + +class ImplicitGrantTest(TestCase): + + def setUp(self): + mock_client = mock.MagicMock() + mock_client.user.return_value = 'mocked user' + self.request = Request('http://a.b/path') + self.request.scopes = ('hello', 'world') + self.request.client = mock_client + self.request.client_id = 'abcdef' + self.request.response_type = 'token' + self.request.state = 'xyz' + self.request.redirect_uri = 'https://b.c/p' + + self.mock_validator = mock.MagicMock() + self.auth = ImplicitGrant(request_validator=self.mock_validator) + + def test_create_token_response(self): + bearer = BearerToken(self.mock_validator, expires_in=1800) + orig_generate_token = common.generate_token + self.addCleanup(setattr, common, 'generate_token', orig_generate_token) + common.generate_token = lambda *args, **kwargs: '1234' + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + correct_uri = 'https://b.c/p#access_token=1234&token_type=Bearer&expires_in=1800&state=xyz&scope=hello+world' + self.assertEqual(status_code, 302) + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], correct_uri, parse_fragment=True) + + def test_error_response(self): + pass diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_refresh_token.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_refresh_token.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_refresh_token.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_refresh_token.py 2014-01-20 10:11:11.000000000 +0000 @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ....unittest import TestCase + +import json +import mock +from oauthlib.common import Request +from oauthlib.oauth2.rfc6749.grant_types import RefreshTokenGrant +from oauthlib.oauth2.rfc6749.tokens import BearerToken +from oauthlib.oauth2.rfc6749 import errors + + +class RefreshTokenGrantTest(TestCase): + + def setUp(self): + mock_client = mock.MagicMock() + mock_client.user.return_value = 'mocked user' + self.request = Request('http://a.b/path') + self.request.grant_type = 'refresh_token' + self.request.refresh_token = 'lsdkfhj230' + self.request.client = mock_client + self.request.scope = 'foo' + self.mock_validator = mock.MagicMock() + self.auth = RefreshTokenGrant( + request_validator=self.mock_validator) + + def test_create_token_response(self): + self.mock_validator.get_original_scopes.return_value = ['foo', 'bar'] + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertIn('access_token', token) + self.assertIn('token_type', token) + self.assertIn('expires_in', token) + self.assertEqual(token['scope'], 'foo') + + def test_create_token_inherit_scope(self): + self.request.scope = None + self.mock_validator.get_original_scopes.return_value = ['foo', 'bar'] + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertIn('access_token', token) + self.assertIn('token_type', token) + self.assertIn('expires_in', token) + self.assertEqual(token['scope'], 'foo bar') + + def test_create_token_within_original_scope(self): + self.mock_validator.get_original_scopes.return_value = ['baz'] + self.mock_validator.is_within_original_scope.return_value = True + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertIn('access_token', token) + self.assertIn('token_type', token) + self.assertIn('expires_in', token) + self.assertEqual(token['scope'], 'foo') + + def test_invalid_scope(self): + self.mock_validator.get_original_scopes.return_value = ['baz'] + self.mock_validator.is_within_original_scope.return_value = False + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertEqual(token['error'], 'invalid_scope') + self.assertEqual(status_code, 401) + + def test_invalid_token(self): + self.mock_validator.validate_refresh_token.return_value = False + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertEqual(token['error'], 'invalid_grant') + self.assertEqual(status_code, 400) + + def test_invalid_client(self): + self.mock_validator.authenticate_client.return_value = False + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertEqual(token['error'], 'invalid_client') + self.assertEqual(status_code, 401) + + def test_authentication_required(self): + """ + ensure client_authentication_required() is properly called + """ + self.mock_validator.authenticate_client.return_value = False + self.mock_validator.authenticate_client_id.return_value = False + self.request.code = 'waffles' + self.assertRaises(errors.InvalidClientError, self.auth.validate_token_request, + self.request) + self.mock_validator.client_authentication_required.assert_called_once_with(self.request) + + def test_invalid_grant_type(self): + self.request.grant_type = 'wrong_type' + self.assertRaises(errors.UnsupportedGrantTypeError, + self.auth.validate_token_request, self.request) + + def test_authenticate_client_id(self): + self.mock_validator.client_authentication_required.return_value = False + self.request.refresh_token = mock.MagicMock() + self.mock_validator.authenticate_client_id.return_value = False + self.assertRaises(errors.InvalidClientError, + self.auth.validate_token_request, self.request) + + def test_invalid_refresh_token(self): + # invalid refresh token + self.mock_validator.authenticate_client_id.return_value = True + self.mock_validator.validate_refresh_token.return_value = False + self.assertRaises(errors.InvalidGrantError, + self.auth.validate_token_request, self.request) + # no token provided + del self.request.refresh_token + self.assertRaises(errors.InvalidRequestError, + self.auth.validate_token_request, self.request) + + def test_invalid_scope_original_scopes_empty(self): + self.mock_validator.validate_refresh_token.return_value = True + self.mock_validator.is_within_original_scope.return_value = False + self.assertRaises(errors.InvalidScopeError, + self.auth.validate_token_request, self.request) + + def test_valid_token_request(self): + self.request.scope = 'foo bar' + self.mock_validator.get_original_scopes = mock.Mock() + self.mock_validator.get_original_scopes.return_value = 'foo bar baz' + self.auth.validate_token_request(self.request) + self.assertEqual(self.request.scopes, self.request.scope.split()) + # all ok but without request.scope + del self.request.scope + self.auth.validate_token_request(self.request) + self.assertEqual(self.request.scopes, 'foo bar baz'.split()) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py 2013-10-04 10:24:45.000000000 +0000 @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ....unittest import TestCase + +import json +import mock +from oauthlib.common import Request +from oauthlib.oauth2.rfc6749.grant_types import ResourceOwnerPasswordCredentialsGrant +from oauthlib.oauth2.rfc6749.tokens import BearerToken +from oauthlib.oauth2.rfc6749 import errors + + +class ResourceOwnerPasswordCredentialsGrantTest(TestCase): + + def setUp(self): + mock_client = mock.MagicMock() + mock_client.user.return_value = 'mocked user' + self.request = Request('http://a.b/path') + self.request.grant_type = 'password' + self.request.username = 'john' + self.request.password = 'doe' + self.request.client = mock_client + self.request.scopes = ('mocked', 'scopes') + self.mock_validator = mock.MagicMock() + self.auth = ResourceOwnerPasswordCredentialsGrant( + request_validator=self.mock_validator) + + def set_client(self, request, *args, **kwargs): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + def test_create_token_response(self): + bearer = BearerToken(self.mock_validator) + headers, body, status_code = self.auth.create_token_response( + self.request, bearer) + token = json.loads(body) + self.assertIn('access_token', token) + self.assertIn('token_type', token) + self.assertIn('expires_in', token) + self.assertIn('refresh_token', token) + # ensure client_authentication_required() is properly called + self.mock_validator.client_authentication_required.assert_called_once_with(self.request) + # fail client authentication + self.mock_validator.validate_user.return_value = True + self.mock_validator.authenticate_client.return_value = False + status_code = self.auth.create_token_response(self.request, bearer)[2] + self.assertEqual(status_code, 400) + # mock client_authentication_required() returning False then fail + self.mock_validator.client_authentication_required.return_value = False + self.mock_validator.authenticate_client_id.return_value = False + status_code = self.auth.create_token_response(self.request, bearer)[2] + self.assertEqual(status_code, 400) + + def test_error_response(self): + pass + + def test_scopes(self): + pass + + def test_invalid_request_missing_params(self): + del self.request.grant_type + self.assertRaises(errors.InvalidRequestError, self.auth.validate_token_request, + self.request) + + def test_invalid_request_duplicates(self): + request = mock.MagicMock(wraps=self.request) + request.duplicate_params = ['scope'] + self.assertRaises(errors.InvalidRequestError, self.auth.validate_token_request, + request) + + def test_invalid_grant_type(self): + self.request.grant_type = 'foo' + self.assertRaises(errors.UnsupportedGrantTypeError, + self.auth.validate_token_request, self.request) + + def test_invalid_user(self): + self.mock_validator.validate_user.return_value = False + self.assertRaises(errors.InvalidGrantError, self.auth.validate_token_request, + self.request) + + def test_client_id_missing(self): + del self.request.client.client_id + self.assertRaises(NotImplementedError, self.auth.validate_token_request, + self.request) + + def test_valid_token_request(self): + self.mock_validator.validate_grant_type.return_value = True + self.auth.validate_token_request(self.request) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_client.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_client.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_client.py 2013-05-30 10:26:31.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,460 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals -from ...unittest import TestCase - -import datetime -from oauthlib import common -from oauthlib.oauth2.rfc6749 import utils, errors -from oauthlib.oauth2 import Client -from oauthlib.oauth2 import WebApplicationClient -from oauthlib.oauth2 import MobileApplicationClient -from oauthlib.oauth2 import LegacyApplicationClient -from oauthlib.oauth2 import BackendApplicationClient -from oauthlib.oauth2.rfc6749.clients import AUTH_HEADER, URI_QUERY, BODY - - -class ClientTest(TestCase): - - client_id = "someclientid" - uri = "https://example.com/path?query=world" - body = "not=empty" - headers = {} - access_token = "token" - mac_key = "secret" - - bearer_query = uri + "&access_token=" + access_token - bearer_header = { - "Authorization": "Bearer " + access_token - } - bearer_body = body + "&access_token=" + access_token - - mac_00_header = { - "Authorization": 'MAC id="' + access_token + '", nonce="0:abc123",' + - ' bodyhash="Yqyso8r3hR5Nm1ZFv+6AvNHrxjE=",' + - ' mac="0X6aACoBY0G6xgGZVJ1IeE8dF9k="' - } - mac_01_header = { - "Authorization": 'MAC id="' + access_token + '", ts="123456789",' + - ' nonce="abc123", mac="Xuk+9oqaaKyhitkgh1CD0xrI6+s="' - } - - def test_add_bearer_token(self): - """Test a number of bearer token placements""" - - # Invalid token type - client = Client(self.client_id, token_type="invalid") - self.assertRaises(ValueError, client.add_token, self.uri) - - # Case-insensitive token type - client = Client(self.client_id, access_token=self.access_token, token_type="bEAreR") - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers) - self.assertURLEqual(uri, self.uri) - self.assertFormBodyEqual(body, self.body) - self.assertEqual(headers, self.bearer_header) - - # Missing access token - client = Client(self.client_id) - self.assertRaises(ValueError, client.add_token, self.uri) - - # The default token placement, bearer in auth header - client = Client(self.client_id, access_token=self.access_token) - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers) - self.assertURLEqual(uri, self.uri) - self.assertFormBodyEqual(body, self.body) - self.assertEqual(headers, self.bearer_header) - - # Setting default placements of tokens - client = Client(self.client_id, access_token=self.access_token, - default_token_placement=AUTH_HEADER) - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers) - self.assertURLEqual(uri, self.uri) - self.assertFormBodyEqual(body, self.body) - self.assertEqual(headers, self.bearer_header) - - client = Client(self.client_id, access_token=self.access_token, - default_token_placement=URI_QUERY) - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers) - self.assertURLEqual(uri, self.bearer_query) - self.assertFormBodyEqual(body, self.body) - self.assertEqual(headers, self.headers) - - client = Client(self.client_id, access_token=self.access_token, - default_token_placement=BODY) - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers) - self.assertURLEqual(uri, self.uri) - self.assertFormBodyEqual(body, self.bearer_body) - self.assertEqual(headers, self.headers) - - # Asking for specific placement in the add_token method - client = Client(self.client_id, access_token=self.access_token) - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers, token_placement=AUTH_HEADER) - self.assertURLEqual(uri, self.uri) - self.assertFormBodyEqual(body, self.body) - self.assertEqual(headers, self.bearer_header) - - client = Client(self.client_id, access_token=self.access_token) - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers, token_placement=URI_QUERY) - self.assertURLEqual(uri, self.bearer_query) - self.assertFormBodyEqual(body, self.body) - self.assertEqual(headers, self.headers) - - client = Client(self.client_id, access_token=self.access_token) - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers, token_placement=BODY) - self.assertURLEqual(uri, self.uri) - self.assertFormBodyEqual(body, self.bearer_body) - self.assertEqual(headers, self.headers) - - # Invalid token placement - client = Client(self.client_id, access_token=self.access_token) - self.assertRaises(ValueError, client.add_token, self.uri, body=self.body, - headers=self.headers, token_placement="invalid") - - client = Client(self.client_id, access_token=self.access_token, - default_token_placement="invalid") - self.assertRaises(ValueError, client.add_token, self.uri, body=self.body, - headers=self.headers) - - def test_add_mac_token(self): - # Missing access token - client = Client(self.client_id, token_type="MAC") - self.assertRaises(ValueError, client.add_token, self.uri) - - # Invalid hash algorithm - client = Client(self.client_id, token_type="MAC", - access_token=self.access_token, mac_key=self.mac_key, - mac_algorithm="hmac-sha-2") - self.assertRaises(ValueError, client.add_token, self.uri) - - orig_generate_timestamp = common.generate_timestamp - orig_generate_nonce = common.generate_nonce - orig_generate_age = utils.generate_age - self.addCleanup(setattr, common, 'generage_timestamp', orig_generate_timestamp) - self.addCleanup(setattr, common, 'generage_nonce', orig_generate_nonce) - self.addCleanup(setattr, utils, 'generate_age', orig_generate_age) - common.generate_timestamp = lambda: '123456789' - common.generate_nonce = lambda: 'abc123' - utils.generate_age = lambda *args: 0 - - # Add the Authorization header (draft 00) - client = Client(self.client_id, token_type="MAC", - access_token=self.access_token, mac_key=self.mac_key, - mac_algorithm="hmac-sha-1") - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers, issue_time=datetime.datetime.now()) - self.assertEqual(uri, self.uri) - self.assertEqual(body, self.body) - self.assertEqual(headers, self.mac_00_header) - - # Add the Authorization header (draft 00) - client = Client(self.client_id, token_type="MAC", - access_token=self.access_token, mac_key=self.mac_key, - mac_algorithm="hmac-sha-1") - uri, headers, body = client.add_token(self.uri, body=self.body, - headers=self.headers, draft=1) - self.assertEqual(uri, self.uri) - self.assertEqual(body, self.body) - self.assertEqual(headers, self.mac_01_header) - - -class WebApplicationClientTest(TestCase): - - client_id = "someclientid" - uri = "https://example.com/path?query=world" - uri_id = uri + "&response_type=code&client_id=" + client_id - uri_redirect = uri_id + "&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback" - redirect_uri = "http://my.page.com/callback" - scope = ["/profile"] - state = "xyz" - uri_scope = uri_id + "&scope=%2Fprofile" - uri_state = uri_id + "&state=" + state - kwargs = { - "some": "providers", - "require": "extra arguments" - } - uri_kwargs = uri_id + "&some=providers&require=extra+arguments" - - code = "zzzzaaaa" - body = "not=empty" - - body_code = "not=empty&grant_type=authorization_code&code=%s&client_id=%s" % (code, client_id) - body_redirect = body_code + "&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback" - body_kwargs = body_code + "&some=providers&require=extra+arguments" - - response_uri = "https://client.example.com/cb?code=zzzzaaaa&state=xyz" - response = {"code": "zzzzaaaa", "state": "xyz"} - - token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' - ' "token_type":"example",' - ' "expires_in":3600,' - ' "scope":"/profile",' - ' "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",' - ' "example_parameter":"example_value"}') - token = { - "access_token": "2YotnFZFEjr1zCsicMWpAA", - "token_type": "example", - "expires_in": 3600, - "scope": scope, - "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", - "example_parameter": "example_value" - } - - def test_auth_grant_uri(self): - client = WebApplicationClient(self.client_id) - - # Basic, no extra arguments - uri = client.prepare_request_uri(self.uri) - self.assertURLEqual(uri, self.uri_id) - - # With redirection uri - uri = client.prepare_request_uri(self.uri, redirect_uri=self.redirect_uri) - self.assertURLEqual(uri, self.uri_redirect) - - # With scope - uri = client.prepare_request_uri(self.uri, scope=self.scope) - self.assertURLEqual(uri, self.uri_scope) - - # With state - uri = client.prepare_request_uri(self.uri, state=self.state) - self.assertURLEqual(uri, self.uri_state) - - # With extra parameters through kwargs - uri = client.prepare_request_uri(self.uri, **self.kwargs) - self.assertURLEqual(uri, self.uri_kwargs) - - def test_request_body(self): - client = WebApplicationClient(self.client_id, code=self.code) - - # Basic, no extra arguments - body = client.prepare_request_body(body=self.body) - self.assertFormBodyEqual(body, self.body_code) - - rclient = WebApplicationClient(self.client_id) - body = rclient.prepare_request_body(code=self.code, body=self.body) - self.assertFormBodyEqual(body, self.body_code) - - # With redirection uri - body = client.prepare_request_body(body=self.body, redirect_uri=self.redirect_uri) - self.assertFormBodyEqual(body, self.body_redirect) - - # With extra parameters - body = client.prepare_request_body(body=self.body, **self.kwargs) - self.assertFormBodyEqual(body, self.body_kwargs) - - def test_parse_grant_uri_response(self): - client = WebApplicationClient(self.client_id) - - # Parse code and state - response = client.parse_request_uri_response(self.response_uri, state=self.state) - self.assertEqual(response, self.response) - self.assertEqual(client.code, self.code) - - # Mismatching state - self.assertRaises(errors.MismatchingStateError, - client.parse_request_uri_response, - self.response_uri, - state="invalid") - - def test_parse_token_response(self): - client = WebApplicationClient(self.client_id) - - # Parse code and state - response = client.parse_request_body_response(self.token_json, scope=self.scope) - self.assertEqual(response, self.token) - self.assertEqual(client.access_token, response.get("access_token")) - self.assertEqual(client.refresh_token, response.get("refresh_token")) - self.assertEqual(client.token_type, response.get("token_type")) - - # Mismatching state - self.assertRaises(Warning, client.parse_request_body_response, self.token_json, scope="invalid") - - -class MobileApplicationClientTest(TestCase): - - client_id = "someclientid" - uri = "https://example.com/path?query=world" - uri_id = uri + "&response_type=token&client_id=" + client_id - uri_redirect = uri_id + "&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback" - redirect_uri = "http://my.page.com/callback" - scope = ["/profile"] - state = "xyz" - uri_scope = uri_id + "&scope=%2Fprofile" - uri_state = uri_id + "&state=" + state - kwargs = { - "some": "providers", - "require": "extra arguments" - } - uri_kwargs = uri_id + "&some=providers&require=extra+arguments" - - code = "zzzzaaaa" - - response_uri = ('https://client.example.com/cb?#' - 'access_token=2YotnFZFEjr1zCsicMWpAA&' - 'token_type=example&' - 'expires_in=3600&' - 'scope=%2Fprofile&' - 'example_parameter=example_value') - token = { - "access_token": "2YotnFZFEjr1zCsicMWpAA", - "token_type": "example", - "expires_in": "3600", - "scope": scope, - "example_parameter": "example_value" - } - - def test_implicit_token_uri(self): - client = MobileApplicationClient(self.client_id) - - # Basic, no extra arguments - uri = client.prepare_request_uri(self.uri) - self.assertURLEqual(uri, self.uri_id) - - # With redirection uri - uri = client.prepare_request_uri(self.uri, redirect_uri=self.redirect_uri) - self.assertURLEqual(uri, self.uri_redirect) - - # With scope - uri = client.prepare_request_uri(self.uri, scope=self.scope) - self.assertURLEqual(uri, self.uri_scope) - - # With state - uri = client.prepare_request_uri(self.uri, state=self.state) - self.assertURLEqual(uri, self.uri_state) - - # With extra parameters through kwargs - uri = client.prepare_request_uri(self.uri, **self.kwargs) - self.assertURLEqual(uri, self.uri_kwargs) - - def test_parse_token_response(self): - client = MobileApplicationClient(self.client_id) - - # Parse code and state - response = client.parse_request_uri_response(self.response_uri, scope=self.scope) - self.assertEqual(response, self.token) - self.assertEqual(client.access_token, response.get("access_token")) - self.assertEqual(client.refresh_token, response.get("refresh_token")) - self.assertEqual(client.token_type, response.get("token_type")) - - # Mismatching scope - self.assertRaises(Warning, client.parse_request_uri_response, self.response_uri, scope="invalid") - - -class LegacyApplicationClientTest(TestCase): - - client_id = "someclientid" - scope = ["/profile"] - kwargs = { - "some": "providers", - "require": "extra arguments" - } - - username = "foo" - password = "bar" - body = "not=empty" - - body_up = "not=empty&grant_type=password&username=%s&password=%s" % (username, password) - body_kwargs = body_up + "&some=providers&require=extra+arguments" - - token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' - ' "token_type":"example",' - ' "expires_in":3600,' - ' "scope":"/profile",' - ' "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",' - ' "example_parameter":"example_value"}') - token = { - "access_token": "2YotnFZFEjr1zCsicMWpAA", - "token_type": "example", - "expires_in": 3600, - "scope": scope, - "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", - "example_parameter": "example_value" - } - - def test_request_body(self): - client = LegacyApplicationClient(self.client_id) - - # Basic, no extra arguments - body = client.prepare_request_body(self.username, self.password, - body=self.body) - self.assertFormBodyEqual(body, self.body_up) - - # With extra parameters - body = client.prepare_request_body(self.username, self.password, - body=self.body, **self.kwargs) - self.assertFormBodyEqual(body, self.body_kwargs) - - def test_parse_token_response(self): - client = LegacyApplicationClient(self.client_id) - - # Parse code and state - response = client.parse_request_body_response(self.token_json, scope=self.scope) - self.assertEqual(response, self.token) - self.assertEqual(client.access_token, response.get("access_token")) - self.assertEqual(client.refresh_token, response.get("refresh_token")) - self.assertEqual(client.token_type, response.get("token_type")) - - # Mismatching state - self.assertRaises(Warning, client.parse_request_body_response, self.token_json, scope="invalid") - - -class BackendApplicationClientTest(TestCase): - - client_id = "someclientid" - scope = ["/profile"] - kwargs = { - "some": "providers", - "require": "extra arguments" - } - - body = "not=empty" - - body_up = "not=empty&grant_type=client_credentials" - body_kwargs = body_up + "&some=providers&require=extra+arguments" - - token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' - ' "token_type":"example",' - ' "expires_in":3600,' - ' "scope":"/profile",' - ' "example_parameter":"example_value"}') - token = { - "access_token": "2YotnFZFEjr1zCsicMWpAA", - "token_type": "example", - "expires_in": 3600, - "scope": ["/profile"], - "example_parameter": "example_value" - } - - def test_request_body(self): - client = BackendApplicationClient(self.client_id) - - # Basic, no extra arguments - body = client.prepare_request_body(body=self.body) - self.assertFormBodyEqual(body, self.body_up) - - rclient = BackendApplicationClient(self.client_id) - body = rclient.prepare_request_body(body=self.body) - self.assertFormBodyEqual(body, self.body_up) - - # With extra parameters - body = client.prepare_request_body(body=self.body, **self.kwargs) - self.assertFormBodyEqual(body, self.body_kwargs) - - def test_parse_token_response(self): - client = BackendApplicationClient(self.client_id) - - # Parse code and state - response = client.parse_request_body_response(self.token_json, scope=self.scope) - self.assertEqual(response, self.token) - self.assertEqual(client.access_token, response.get("access_token")) - self.assertEqual(client.refresh_token, response.get("refresh_token")) - self.assertEqual(client.token_type, response.get("token_type")) - - # Mismatching state - self.assertRaises(Warning, client.parse_request_body_response, self.token_json, scope="invalid") diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_grant_types.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_grant_types.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_grant_types.py 2013-06-21 10:28:49.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_grant_types.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,258 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals -from ...unittest import TestCase - -import json -import mock -from oauthlib import common -from oauthlib.common import Request -from oauthlib.oauth2.rfc6749.errors import UnsupportedGrantTypeError -from oauthlib.oauth2.rfc6749.errors import InvalidRequestError -from oauthlib.oauth2.rfc6749.errors import InvalidClientError -from oauthlib.oauth2.rfc6749.errors import InvalidGrantError -from oauthlib.oauth2.rfc6749.grant_types import AuthorizationCodeGrant -from oauthlib.oauth2.rfc6749.grant_types import ImplicitGrant -from oauthlib.oauth2.rfc6749.grant_types import ResourceOwnerPasswordCredentialsGrant -from oauthlib.oauth2.rfc6749.grant_types import ClientCredentialsGrant -from oauthlib.oauth2.rfc6749.grant_types import RefreshTokenGrant -from oauthlib.oauth2.rfc6749.tokens import BearerToken - - -class RequestValidatorTest(TestCase): - - def test_client_id(self): - pass - - def test_client(self): - pass - - def test_response_type(self): - pass - - def test_scopes(self): - pass - - def test_redirect_uri(self): - pass - - -class AuthorizationCodeGrantTest(TestCase): - - def setUp(self): - self.request = Request('http://a.b/path') - self.request.scopes = ('hello', 'world') - self.request.expires_in = 1800 - self.request.client = 'batman' - self.request.client_id = 'abcdef' - self.request.code = '1234' - self.request.response_type = 'code' - self.request.grant_type = 'authorization_code' - - self.request_state = Request('http://a.b/path') - self.request_state.state = 'abc' - - self.mock_validator = mock.MagicMock() - self.mock_validator.authenticate_client.side_effect = self.set_client - self.auth = AuthorizationCodeGrant(request_validator=self.mock_validator) - - def set_client(self, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def test_create_authorization_grant(self): - grant = self.auth.create_authorization_code(self.request) - self.assertIn('code', grant) - - grant = self.auth.create_authorization_code(self.request_state) - self.assertIn('code', grant) - self.assertIn('state', grant) - - def test_create_token_response(self): - bearer = BearerToken(self.mock_validator) - u, h, token, s = self.auth.create_token_response(self.request, bearer) - token = json.loads(token) - self.assertIn('access_token', token) - self.assertIn('refresh_token', token) - self.assertIn('expires_in', token) - self.assertIn('scope', token) - - def test_validate_token_request(self): - mock_validator = mock.MagicMock() - auth = AuthorizationCodeGrant(request_validator=mock_validator) - request = Request('http://a.b/path') - self.assertRaises(UnsupportedGrantTypeError, - auth.validate_token_request, request) - - request.grant_type = 'authorization_code' - self.assertRaises(InvalidRequestError, - auth.validate_token_request, request) - - mock_validator.authenticate_client.return_value = False - mock_validator.authenticate_client_id.return_value = False - request.code = 'waffles' - self.assertRaises(InvalidClientError, - auth.validate_token_request, request) - - request.client = 'batman' - mock_validator.authenticate_client = self.set_client - mock_validator.validate_code.return_value = False - self.assertRaises(InvalidGrantError, - auth.validate_token_request, request) - - -class ImplicitGrantTest(TestCase): - - def setUp(self): - mock_client = mock.MagicMock() - mock_client.user.return_value = 'mocked user' - self.request = Request('http://a.b/path') - self.request.scopes = ('hello', 'world') - self.request.client = mock_client - self.request.client_id = 'abcdef' - self.request.response_type = 'token' - self.request.state = 'xyz' - self.request.redirect_uri = 'https://b.c/p' - - self.mock_validator = mock.MagicMock() - self.auth = ImplicitGrant(request_validator=self.mock_validator) - - def test_create_token_response(self): - bearer = BearerToken(self.mock_validator, expires_in=1800) - orig_generate_token = common.generate_token - self.addCleanup(setattr, common, 'generate_token', orig_generate_token) - common.generate_token = lambda *args, **kwargs: '1234' - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - correct_uri = 'https://b.c/p#access_token=1234&token_type=Bearer&expires_in=1800&state=xyz&scope=hello+world' - self.assertURLEqual(uri, correct_uri, parse_fragment=True) - - def test_error_response(self): - pass - - -class ResourceOwnerPasswordCredentialsGrantTest(TestCase): - - def setUp(self): - mock_client = mock.MagicMock() - mock_client.user.return_value = 'mocked user' - self.request = Request('http://a.b/path') - self.request.grant_type = 'password' - self.request.username = 'john' - self.request.password = 'doe' - self.request.client = mock_client - self.request.scopes = ('mocked', 'scopes') - self.mock_validator = mock.MagicMock() - self.auth = ResourceOwnerPasswordCredentialsGrant( - request_validator=self.mock_validator) - - def test_create_token_response(self): - bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - token = json.loads(body) - self.assertIn('access_token', token) - self.assertIn('token_type', token) - self.assertIn('expires_in', token) - self.assertIn('refresh_token', token) - - def test_error_response(self): - pass - - def test_scopes(self): - pass - - -class ClientCredentialsGrantTest(TestCase): - - def setUp(self): - mock_client = mock.MagicMock() - mock_client.user.return_value = 'mocked user' - self.request = Request('http://a.b/path') - self.request.grant_type = 'client_credentials' - self.request.client = mock_client - self.request.scopes = ('mocked', 'scopes') - self.mock_validator = mock.MagicMock() - self.auth = ClientCredentialsGrant( - request_validator=self.mock_validator) - - def test_create_token_response(self): - bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - token = json.loads(body) - self.assertIn('access_token', token) - self.assertIn('token_type', token) - self.assertIn('expires_in', token) - - def test_error_response(self): - pass - - def test_validate_token_response(self): - # wrong grant type, scope - pass - - -class RefreshTokenGrantTest(TestCase): - - def setUp(self): - mock_client = mock.MagicMock() - mock_client.user.return_value = 'mocked user' - self.request = Request('http://a.b/path') - self.request.grant_type = 'refresh_token' - self.request.refresh_token = 'lsdkfhj230' - self.request.client = mock_client - self.request.scope = 'foo' - self.mock_validator = mock.MagicMock() - self.auth = RefreshTokenGrant( - request_validator=self.mock_validator) - - def test_create_token_response(self): - self.mock_validator.get_original_scopes.return_value = ['foo', 'bar'] - bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - token = json.loads(body) - self.assertIn('access_token', token) - self.assertIn('token_type', token) - self.assertIn('expires_in', token) - self.assertEqual(token['scope'], 'foo') - - def test_create_token_inherit_scope(self): - self.request.scope = None - self.mock_validator.get_original_scopes.return_value = ['foo', 'bar'] - bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - token = json.loads(body) - self.assertIn('access_token', token) - self.assertIn('token_type', token) - self.assertIn('expires_in', token) - self.assertEqual(token['scope'], 'foo bar') - - def test_invalid_scope(self): - self.mock_validator.get_original_scopes.return_value = ['baz'] - bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - token = json.loads(body) - self.assertEqual(token['error'], 'invalid_scope') - self.assertEqual(status_code, 401) - - def test_invalid_token(self): - self.mock_validator.validate_refresh_token.return_value = False - bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - token = json.loads(body) - self.assertEqual(token['error'], 'invalid_grant') - self.assertEqual(status_code, 400) - - def test_invalid_client(self): - self.mock_validator.authenticate_client.return_value = False - bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( - self.request, bearer) - token = json.loads(body) - self.assertEqual(token['error'], 'invalid_client') - self.assertEqual(status_code, 401) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_parameters.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_parameters.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_parameters.py 2013-05-30 08:57:29.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_parameters.py 2013-10-04 10:12:27.000000000 +0000 @@ -1,10 +1,13 @@ from __future__ import absolute_import, unicode_literals +from mock import patch + from ...unittest import TestCase from oauthlib.oauth2.rfc6749.parameters import * from oauthlib.oauth2.rfc6749.errors import * +@patch('time.time', new=lambda: 1000) class ParameterTests(TestCase): state = 'xyz' @@ -82,6 +85,7 @@ 'state': state, 'token_type': 'example', 'expires_in': '3600', + 'expires_at': 4600, 'scope': ['abc'] } @@ -108,6 +112,7 @@ 'access_token': '2YotnFZFEjr1zCsicMWpAA', 'token_type': 'example', 'expires_in': 3600, + 'expires_at': 4600, 'refresh_token': 'tGzv3JOkF0XG5Qx2TlKWIA', 'example_parameter': 'example_value', 'scope': ['abc', 'def'] diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_request_validator.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_request_validator.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_request_validator.py 1970-01-01 00:00:00.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_request_validator.py 2014-01-20 10:11:11.000000000 +0000 @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from ...unittest import TestCase + +from oauthlib.oauth2 import RequestValidator + +class RequestValidatorTest(TestCase): + + def test_method_contracts(self): + v = RequestValidator() + self.assertRaises(NotImplementedError, v.authenticate_client, 'r') + self.assertRaises(NotImplementedError, v.authenticate_client_id, + 'client_id', 'r') + self.assertRaises(NotImplementedError, v.confirm_redirect_uri, + 'client_id', 'code', 'redirect_uri', 'client', 'request') + self.assertRaises(NotImplementedError, v.get_default_redirect_uri, + 'client_id', 'request') + self.assertRaises(NotImplementedError, v.get_default_scopes, + 'client_id', 'request') + self.assertRaises(NotImplementedError, v.get_original_scopes, + 'refresh_token', 'request') + self.assertFalse(v.is_within_original_scope( + ['scope'], 'refresh_token', 'request')) + self.assertRaises(NotImplementedError, v.invalidate_authorization_code, + 'client_id', 'code', 'request') + self.assertRaises(NotImplementedError, v.save_authorization_code, + 'client_id', 'code', 'request') + self.assertRaises(NotImplementedError, v.save_bearer_token, + 'token', 'request') + self.assertRaises(NotImplementedError, v.validate_bearer_token, + 'token', 'scopes', 'request') + self.assertRaises(NotImplementedError, v.validate_client_id, + 'client_id', 'request') + self.assertRaises(NotImplementedError, v.validate_code, + 'client_id', 'code', 'client', 'request') + self.assertRaises(NotImplementedError, v.validate_grant_type, + 'client_id', 'grant_type', 'client', 'request') + self.assertRaises(NotImplementedError, v.validate_redirect_uri, + 'client_id', 'redirect_uri', 'request') + self.assertRaises(NotImplementedError, v.validate_refresh_token, + 'refresh_token', 'client', 'request') + self.assertRaises(NotImplementedError, v.validate_response_type, + 'client_id', 'response_type', 'client', 'request') + self.assertRaises(NotImplementedError, v.validate_scopes, + 'client_id', 'scopes', 'client', 'request') + self.assertRaises(NotImplementedError, v.validate_user, + 'username', 'password', 'client', 'request') + self.assertTrue(v.client_authentication_required('r')) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_server.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_server.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_server.py 2013-05-30 10:33:56.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_server.py 2013-08-13 21:12:37.000000000 +0000 @@ -41,35 +41,39 @@ def test_authorization_grant(self): uri = 'http://i.b/l?response_type=code&client_id=me&scope=all+of+them&state=xyz' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me?code=abc&state=xyz') + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me?code=abc&state=xyz') @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_implicit_grant(self): uri = 'http://i.b/l?response_type=token&client_id=me&scope=all+of+them&state=xyz' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me#access_token=abc&expires_in=' + str(self.expires_in) + '&token_type=Bearer&state=xyz&scope=all+of+them', parse_fragment=True) + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me#access_token=abc&expires_in=' + str(self.expires_in) + '&token_type=Bearer&state=xyz&scope=all+of+them', parse_fragment=True) def test_missing_type(self): uri = 'http://i.b/l?client_id=me&scope=all+of+them' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' self.mock_validator.validate_request = mock.MagicMock( side_effect=errors.InvalidRequestError()) - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me?error=invalid_request&error_description=Missing+response_type+parameter.') + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me?error=invalid_request&error_description=Missing+response_type+parameter.') def test_invalid_type(self): uri = 'http://i.b/l?response_type=invalid&client_id=me&scope=all+of+them' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' self.mock_validator.validate_request = mock.MagicMock( side_effect=errors.UnsupportedResponseTypeError()) - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me?error=unsupported_response_type') + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me?error=unsupported_response_type') class TokenEndpointTest(TestCase): @@ -104,7 +108,7 @@ @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_authorization_grant(self): body = 'grant_type=authorization_code&code=abc&scope=all+of+them&state=xyz' - uri, headers, body, status_code = self.endpoint.create_token_response( + headers, body, status_code = self.endpoint.create_token_response( '', body=body) token = { 'token_type': 'Bearer', @@ -118,7 +122,7 @@ @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_password_grant(self): body = 'grant_type=password&username=a&password=hello&scope=all+of+them' - uri, headers, body, status_code = self.endpoint.create_token_response( + headers, body, status_code = self.endpoint.create_token_response( '', body=body) token = { 'token_type': 'Bearer', @@ -132,7 +136,7 @@ @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_client_grant(self): body = 'grant_type=client_credentials&scope=all+of+them' - uri, headers, body, status_code = self.endpoint.create_token_response( + headers, body, status_code = self.endpoint.create_token_response( '', body=body) token = { 'token_type': 'Bearer', @@ -143,13 +147,13 @@ self.assertEqual(json.loads(body), token) def test_missing_type(self): - _, _, body, _ = self.endpoint.create_token_response('', body='') + _, body, _ = self.endpoint.create_token_response('', body='') token = {'error': 'unsupported_grant_type'} self.assertEqual(json.loads(body), token) def test_invalid_type(self): body = 'grant_type=invalid' - _, _, body, _ = self.endpoint.create_token_response('', body=body) + _, body, _ = self.endpoint.create_token_response('', body=body) token = {'error': 'unsupported_grant_type'} self.assertEqual(json.loads(body), token) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_servers.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_servers.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_servers.py 2013-05-30 08:57:13.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_servers.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,858 +0,0 @@ - -"""Test for the various oauth 2 provider endpoints. - -The tests focus on shared functionality between the different endpoints to -ensure consistency in both behaviour and provided interfaces. -""" -from __future__ import absolute_import, unicode_literals -import json -import mock -try: - import urlparse -except ImportError: - import urllib.parse as urlparse -from ...unittest import TestCase - -from oauthlib.oauth2 import RequestValidator -from oauthlib.oauth2 import WebApplicationServer, MobileApplicationServer -from oauthlib.oauth2 import LegacyApplicationServer, BackendApplicationServer -from oauthlib.oauth2.rfc6749 import errors - - -def get_query_credentials(uri): - return urlparse.parse_qs(urlparse.urlparse(uri).query, - keep_blank_values=True) - - -def get_fragment_credentials(uri): - return urlparse.parse_qs(urlparse.urlparse(uri).fragment, - keep_blank_values=True) - - -class TestScopeHandling(TestCase): - - DEFAULT_REDIRECT_URI = 'http://i.b./path' - - def set_scopes(self, scopes): - def set_request_scopes(client_id, code, client, request): - request.scopes = scopes - return True - return set_request_scopes - - def set_user(self, request): - request.user = 'foo' - request.client_id = 'bar' - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def set_client(self, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def setUp(self): - self.validator = mock.MagicMock(spec=RequestValidator) - self.validator.get_default_redirect_uri.return_value = TestScopeHandling.DEFAULT_REDIRECT_URI - self.validator.authenticate_client.side_effect = self.set_client - self.web = WebApplicationServer(self.validator) - self.mobile = MobileApplicationServer(self.validator) - self.legacy = LegacyApplicationServer(self.validator) - self.backend = BackendApplicationServer(self.validator) - - def test_scope_extraction(self): - scopes = ( - ('images', ['images']), - ('images+videos', ['images', 'videos']), - ('http%3A%2f%2fa.b%2fvideos', ['http://a.b/videos']), - ('http%3A%2f%2fa.b%2fvideos+pics', ['http://a.b/videos', 'pics']), - ('pics+http%3A%2f%2fa.b%2fvideos', ['pics', 'http://a.b/videos']), - ('http%3A%2f%2fa.b%2fvideos+https%3A%2f%2fc.d%2Fsecret', ['http://a.b/videos', 'https://c.d/secret']), - ) - - uri = 'http://example.com/path?client_id=abc&scope=%s&response_type=%s' - for scope, correct_scopes in scopes: - scopes, _ = self.web.validate_authorization_request( - uri % (scope, 'code')) - self.assertItemsEqual(scopes, correct_scopes) - scopes, _ = self.mobile.validate_authorization_request( - uri % (scope, 'token')) - self.assertItemsEqual(scopes, correct_scopes) - - def test_scope_preservation(self): - scope = 'pics+http%3A%2f%2fa.b%2fvideos' - decoded_scope = 'pics http://a.b/videos' - auth_uri = 'http://example.com/path?client_id=abc&response_type=' - token_uri = 'http://example.com/path' - - # authorization grant - uri, _, _, _ = self.web.create_authorization_response( - auth_uri + 'code', scopes=decoded_scope.split(' ')) - self.validator.validate_code.side_effect = self.set_scopes(decoded_scope.split(' ')) - code = get_query_credentials(uri)['code'][0] - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=%s' % code) - self.assertEqual(json.loads(body)['scope'], decoded_scope) - - # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( - auth_uri + 'token', scopes=decoded_scope.split(' ')) - self.assertEqual(get_fragment_credentials(uri)['scope'][0], decoded_scope) - - # resource owner password credentials grant - body = 'grant_type=password&username=abc&password=secret&scope=%s' - _, _, body, _ = self.legacy.create_token_response(token_uri, - body=body % scope) - self.assertEqual(json.loads(body)['scope'], decoded_scope) - - # client credentials grant - body = 'grant_type=client_credentials&scope=%s' - self.validator.authenticate_client.side_effect = self.set_user - _, _, body, _ = self.backend.create_token_response(token_uri, - body=body % scope) - self.assertEqual(json.loads(body)['scope'], decoded_scope) - - def test_scope_changed(self): - scope = 'pics+http%3A%2f%2fa.b%2fvideos' - scopes = ['images', 'http://a.b/videos'] - decoded_scope = 'images http://a.b/videos' - auth_uri = 'http://example.com/path?client_id=abc&response_type=' - token_uri = 'http://example.com/path' - - # authorization grant - uri, _, _, _ = self.web.create_authorization_response( - auth_uri + 'code', scopes=scopes) - code = get_query_credentials(uri)['code'][0] - self.validator.validate_code.side_effect = self.set_scopes(scopes) - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=%s' % code) - self.assertEqual(json.loads(body)['scope'], decoded_scope) - - # implicit grant - self.validator.validate_scopes.side_effect = self.set_scopes(scopes) - uri, _, _, _ = self.mobile.create_authorization_response( - auth_uri + 'token', scopes=scopes) - self.assertEqual(get_fragment_credentials(uri)['scope'][0], decoded_scope) - - # resource owner password credentials grant - self.validator.validate_scopes.side_effect = self.set_scopes(scopes) - body = 'grant_type=password&username=abc&password=secret&scope=%s' - _, _, body, _ = self.legacy.create_token_response(token_uri, - body=body % scope) - self.assertEqual(json.loads(body)['scope'], decoded_scope) - - # client credentials grant - self.validator.validate_scopes.side_effect = self.set_scopes(scopes) - self.validator.authenticate_client.side_effect = self.set_user - body = 'grant_type=client_credentials&scope=%s' - _, _, body, _ = self.backend.create_token_response(token_uri, - body=body % scope) - self.assertEqual(json.loads(body)['scope'], decoded_scope) - - def test_invalid_scope(self): - scope = 'pics+http%3A%2f%2fa.b%2fvideos' - auth_uri = 'http://example.com/path?client_id=abc&response_type=' - token_uri = 'http://example.com/path' - - self.validator.validate_scopes.return_value = False - - # authorization grant - uri, _, _, _ = self.web.create_authorization_response( - auth_uri + 'code', scopes=['invalid']) - error = get_query_credentials(uri)['error'][0] - self.assertEqual(error, 'invalid_scope') - - # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( - auth_uri + 'token', scopes=['invalid']) - error = get_fragment_credentials(uri)['error'][0] - self.assertEqual(error, 'invalid_scope') - - # resource owner password credentials grant - body = 'grant_type=password&username=abc&password=secret&scope=%s' - _, _, body, _ = self.legacy.create_token_response(token_uri, - body=body % scope) - self.assertEqual(json.loads(body)['error'], 'invalid_scope') - - # client credentials grant - self.validator.authenticate_client.side_effect = self.set_user - body = 'grant_type=client_credentials&scope=%s' - _, _, body, _ = self.backend.create_token_response(token_uri, - body=body % scope) - self.assertEqual(json.loads(body)['error'], 'invalid_scope') - - -class PreservationTest(TestCase): - - def setUp(self): - self.validator = mock.MagicMock(spec=RequestValidator) - self.validator.get_default_redirect_uri.return_value = TestScopeHandling.DEFAULT_REDIRECT_URI - self.validator.authenticate_client.side_effect = self.set_client - self.web = WebApplicationServer(self.validator) - self.mobile = MobileApplicationServer(self.validator) - - def set_state(self, state): - def set_request_state(client_id, code, client, request): - request.state = state - return True - return set_request_state - - def set_client(self, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def test_state_preservation(self): - auth_uri = 'http://example.com/path?state=xyz&client_id=abc&response_type=' - token_uri = 'http://example.com/path' - - # authorization grant - uri, _, _, _ = self.web.create_authorization_response( - auth_uri + 'code', scopes=['random']) - code = get_query_credentials(uri)['code'][0] - self.validator.validate_code.side_effect = self.set_state('xyz') - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=%s' % code) - self.assertEqual(json.loads(body)['state'], 'xyz') - - # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( - auth_uri + 'token', scopes=['random']) - self.assertEqual(get_fragment_credentials(uri)['state'][0], 'xyz') - - def test_redirect_uri_preservation(self): - auth_uri = 'http://example.com/path?redirect_uri=http%3A%2F%2Fi.b%2Fpath&client_id=abc' - redirect_uri = 'http://i.b/path' - token_uri = 'http://example.com/path' - - # authorization grant - uri, _, _, _ = self.web.create_authorization_response( - auth_uri + '&response_type=code', scopes=['random']) - self.assertTrue(uri.startswith(redirect_uri)) - - # confirm_redirect_uri should return false if the redirect uri - # was given in the authorization but not in the token request. - self.validator.confirm_redirect_uri.return_value = False - code = get_query_credentials(uri)['code'][0] - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=%s' % code) - self.assertEqual(json.loads(body)['error'], 'access_denied') - - # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( - auth_uri + '&response_type=token', scopes=['random']) - self.assertTrue(uri.startswith(redirect_uri)) - - def test_invalid_redirect_uri(self): - auth_uri = 'http://example.com/path?redirect_uri=http%3A%2F%2Fi.b%2Fpath&client_id=abc' - self.validator.validate_redirect_uri.return_value = False - - # authorization grant - self.assertRaises(errors.MismatchingRedirectURIError, - self.web.create_authorization_response, - auth_uri + '&response_type=code', scopes=['random']) - - # implicit grant - self.assertRaises(errors.MismatchingRedirectURIError, - self.mobile.create_authorization_response, - auth_uri + '&response_type=token', scopes=['random']) - - def test_default_uri(self): - auth_uri = 'http://example.com/path?state=xyz&client_id=abc' - - self.validator.get_default_redirect_uri.return_value = None - - # authorization grant - self.assertRaises(errors.MissingRedirectURIError, - self.web.create_authorization_response, - auth_uri + '&response_type=code', scopes=['random']) - - # implicit grant - self.assertRaises(errors.MissingRedirectURIError, - self.mobile.create_authorization_response, - auth_uri + '&response_type=token', scopes=['random']) - - -class ClientAuthenticationTest(TestCase): - - def inspect_client(self, request, refresh_token=False): - if not request.client or not request.client.client_id: - raise ValueError() - return 'abc' - - def setUp(self): - self.validator = mock.MagicMock(spec=RequestValidator) - self.validator.get_default_redirect_uri.return_value = 'http://i.b./path' - self.web = WebApplicationServer(self.validator, - token_generator=self.inspect_client) - self.mobile = MobileApplicationServer(self.validator, - token_generator=self.inspect_client) - self.legacy = LegacyApplicationServer(self.validator, - token_generator=self.inspect_client) - self.backend = BackendApplicationServer(self.validator, - token_generator=self.inspect_client) - - def set_client(self, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def set_client_id(self, client_id, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def set_username(self, username, password, client, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def test_client_id_authentication(self): - token_uri = 'http://example.com/path' - - # authorization code grant - self.validator.authenticate_client.return_value = False - self.validator.authenticate_client_id.return_value = False - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=mock') - self.assertEqual(json.loads(body)['error'], 'invalid_client') - - self.validator.authenticate_client_id.return_value = True - self.validator.authenticate_client.side_effect = self.set_client - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=mock') - self.assertIn('access_token', json.loads(body)) - - # implicit grant - auth_uri = 'http://example.com/path?client_id=abc&response_type=token' - self.assertRaises(ValueError, self.mobile.create_authorization_response, - auth_uri, scopes=['random']) - - self.validator.validate_client_id.side_effect = self.set_client_id - uri, _, _, _ = self.mobile.create_authorization_response(auth_uri, scopes=['random']) - self.assertIn('access_token', get_fragment_credentials(uri)) - - def test_custom_authentication(self): - token_uri = 'http://example.com/path' - - # authorization code grant - self.assertRaises(NotImplementedError, - self.web.create_token_response, token_uri, - body='grant_type=authorization_code&code=mock') - - # password grant - self.validator.authenticate_client.return_value = True - self.assertRaises(NotImplementedError, - self.legacy.create_token_response, token_uri, - body='grant_type=password&username=abc&password=secret') - - # client credentials grant - self.validator.authenticate_client.return_value = True - self.assertRaises(NotImplementedError, - self.backend.create_token_response, token_uri, - body='grant_type=client_credentials') - - -class ResourceOwnerAssociationTest(TestCase): - - auth_uri = 'http://example.com/path?client_id=abc' - token_uri = 'http://example.com/path' - - def set_client(self, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def set_user(self, client_id, code, client, request): - request.user = 'test' - return True - - def set_user_from_username(self, username, password, client, request): - request.user = 'test' - return True - - def set_user_from_credentials(self, request): - request.user = 'test' - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def inspect_client(self, request, refresh_token=False): - if not request.user: - raise ValueError() - return 'abc' - - def setUp(self): - self.validator = mock.MagicMock(spec=RequestValidator) - self.validator.get_default_redirect_uri.return_value = 'http://i.b./path' - self.validator.authenticate_client.side_effect = self.set_client - self.web = WebApplicationServer(self.validator, - token_generator=self.inspect_client) - self.mobile = MobileApplicationServer(self.validator, - token_generator=self.inspect_client) - self.legacy = LegacyApplicationServer(self.validator, - token_generator=self.inspect_client) - self.backend = BackendApplicationServer(self.validator, - token_generator=self.inspect_client) - - def test_web_application(self): - # TODO: code generator + intercept test - uri, _, _, _ = self.web.create_authorization_response( - self.auth_uri + '&response_type=code', - credentials={'user': 'test'}, scopes=['random']) - code = get_query_credentials(uri)['code'][0] - self.assertRaises(ValueError, - self.web.create_token_response, self.token_uri, - body='grant_type=authorization_code&code=%s' % code) - - self.validator.validate_code.side_effect = self.set_user - _, _, body, _ = self.web.create_token_response(self.token_uri, - body='grant_type=authorization_code&code=%s' % code) - self.assertEqual(json.loads(body)['access_token'], 'abc') - - def test_mobile_application(self): - self.assertRaises(ValueError, - self.mobile.create_authorization_response, - self.auth_uri + '&response_type=token') - - uri, _, _, _ = self.mobile.create_authorization_response( - self.auth_uri + '&response_type=token', - credentials={'user': 'test'}, scopes=['random']) - self.assertEqual(get_fragment_credentials(uri)['access_token'][0], 'abc') - - def test_legacy_application(self): - body = 'grant_type=password&username=abc&password=secret' - self.assertRaises(ValueError, - self.legacy.create_token_response, - self.token_uri, body=body) - - self.validator.validate_user.side_effect = self.set_user_from_username - _, _, body, _ = self.legacy.create_token_response( - self.token_uri, body=body) - self.assertEqual(json.loads(body)['access_token'], 'abc') - - def test_backend_application(self): - body = 'grant_type=client_credentials' - self.assertRaises(ValueError, - self.backend.create_token_response, - self.token_uri, body=body) - - self.validator.authenticate_client.side_effect = self.set_user_from_credentials - _, _, body, _ = self.backend.create_token_response( - self.token_uri, body=body) - self.assertEqual(json.loads(body)['access_token'], 'abc') - - -class ErrorResponseTest(TestCase): - - def set_client(self, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def setUp(self): - self.validator = mock.MagicMock(spec=RequestValidator) - self.validator.get_default_redirect_uri.return_value = None - self.web = WebApplicationServer(self.validator) - self.mobile = MobileApplicationServer(self.validator) - self.legacy = LegacyApplicationServer(self.validator) - self.backend = BackendApplicationServer(self.validator) - - def test_invalid_redirect_uri(self): - uri = 'https://example.com/authorize?client_id=foo&redirect_uri=wrong' - # Authorization code grant - self.assertRaises(errors.InvalidRedirectURIError, - self.web.validate_authorization_request, uri) - self.assertRaises(errors.InvalidRedirectURIError, - self.web.create_authorization_response, uri, scopes=['foo']) - - # Implicit grant - self.assertRaises(errors.InvalidRedirectURIError, - self.mobile.validate_authorization_request, uri) - self.assertRaises(errors.InvalidRedirectURIError, - self.mobile.create_authorization_response, uri, scopes=['foo']) - - def test_missing_redirect_uri(self): - uri = 'https://example.com/authorize?client_id=foo' - # Authorization code grant - self.assertRaises(errors.MissingRedirectURIError, - self.web.validate_authorization_request, uri) - self.assertRaises(errors.MissingRedirectURIError, - self.web.create_authorization_response, uri, scopes=['foo']) - - # Implicit grant - self.assertRaises(errors.MissingRedirectURIError, - self.mobile.validate_authorization_request, uri) - self.assertRaises(errors.MissingRedirectURIError, - self.mobile.create_authorization_response, uri, scopes=['foo']) - - def test_mismatching_redirect_uri(self): - uri = 'https://example.com/authorize?client_id=foo&redirect_uri=https%3A%2F%2Fi.b%2Fback' - # Authorization code grant - self.validator.validate_redirect_uri.return_value = False - self.assertRaises(errors.MismatchingRedirectURIError, - self.web.validate_authorization_request, uri) - self.assertRaises(errors.MismatchingRedirectURIError, - self.web.create_authorization_response, uri, scopes=['foo']) - - # Implicit grant - self.assertRaises(errors.MismatchingRedirectURIError, - self.mobile.validate_authorization_request, uri) - self.assertRaises(errors.MismatchingRedirectURIError, - self.mobile.create_authorization_response, uri, scopes=['foo']) - - def test_missing_client_id(self): - uri = 'https://example.com/authorize?redirect_uri=https%3A%2F%2Fi.b%2Fback' - # Authorization code grant - self.validator.validate_redirect_uri.return_value = False - self.assertRaises(errors.MissingClientIdError, - self.web.validate_authorization_request, uri) - self.assertRaises(errors.MissingClientIdError, - self.web.create_authorization_response, uri, scopes=['foo']) - - # Implicit grant - self.assertRaises(errors.MissingClientIdError, - self.mobile.validate_authorization_request, uri) - self.assertRaises(errors.MissingClientIdError, - self.mobile.create_authorization_response, uri, scopes=['foo']) - - def test_invalid_client_id(self): - uri = 'https://example.com/authorize?client_id=foo&redirect_uri=https%3A%2F%2Fi.b%2Fback' - # Authorization code grant - self.validator.validate_client_id.return_value = False - self.assertRaises(errors.InvalidClientIdError, - self.web.validate_authorization_request, uri) - self.assertRaises(errors.InvalidClientIdError, - self.web.create_authorization_response, uri, scopes=['foo']) - - # Implicit grant - self.assertRaises(errors.InvalidClientIdError, - self.mobile.validate_authorization_request, uri) - self.assertRaises(errors.InvalidClientIdError, - self.mobile.create_authorization_response, uri, scopes=['foo']) - - def test_invalid_request(self): - self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' - token_uri = 'https://i.b/token' - invalid_uris = [ - # Duplicate parameters - 'https://i.b/auth?client_id=foo&client_id=bar&response_type={0}', - # Missing response type - 'https://i.b/auth?client_id=foo', - ] - - # Authorization code grant - for uri in invalid_uris: - self.assertRaises(errors.InvalidRequestError, - self.web.validate_authorization_request, - uri.format('code')) - url, _, _, _ = self.web.create_authorization_response( - uri.format('code'), scopes=['foo']) - self.assertIn('error=invalid_request', url) - invalid_bodies = [ - # duplicate params - 'grant_type=authorization_code&client_id=nope&client_id=nope&code=foo' - ] - for body in invalid_bodies: - _, _, body, _ = self.web.create_token_response(token_uri, - body=body) - self.assertEqual('invalid_request', json.loads(body)['error']) - - # Implicit grant - for uri in invalid_uris: - self.assertRaises(errors.InvalidRequestError, - self.mobile.validate_authorization_request, - uri.format('token')) - url, _, _, _ = self.mobile.create_authorization_response( - uri.format('token'), scopes=['foo']) - self.assertIn('error=invalid_request', url) - - # Password credentials grant - invalid_bodies = [ - # duplicate params - 'grant_type=password&username=foo&username=bar&password=baz' - # missing username - 'grant_type=password&password=baz' - # missing password - 'grant_type=password&username=foo' - ] - self.validator.authenticate_client.side_effect = self.set_client - for body in invalid_bodies: - _, _, body, _ = self.legacy.create_token_response(token_uri, - body=body) - self.assertEqual('invalid_request', json.loads(body)['error']) - - # Client credentials grant - invalid_bodies = [ - # duplicate params - 'grant_type=client_credentials&scope=foo&scope=bar' - ] - for body in invalid_bodies: - _, _, body, _ = self.backend.create_token_response(token_uri, - body=body) - self.assertEqual('invalid_request', json.loads(body)['error']) - - def test_unauthorized_client(self): - self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' - self.validator.validate_grant_type.return_value = False - self.validator.validate_response_type.return_value = False - self.validator.authenticate_client.side_effect = self.set_client - token_uri = 'https://i.b/token' - - # Authorization code grant - self.assertRaises(errors.UnauthorizedClientError, - self.web.validate_authorization_request, - 'https://i.b/auth?response_type=code&client_id=foo') - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=foo') - self.assertEqual('unauthorized_client', json.loads(body)['error']) - - # Implicit grant - self.assertRaises(errors.UnauthorizedClientError, - self.mobile.validate_authorization_request, - 'https://i.b/auth?response_type=token&client_id=foo') - - # Password credentials grant - _, _, body, _ = self.legacy.create_token_response(token_uri, - body='grant_type=password&username=foo&password=bar') - self.assertEqual('unauthorized_client', json.loads(body)['error']) - - # Client credentials grant - _, _, body, _ = self.backend.create_token_response(token_uri, - body='grant_type=client_credentials') - self.assertEqual('unauthorized_client', json.loads(body)['error']) - - def test_access_denied(self): - self.validator.authenticate_client.side_effect = self.set_client - self.validator.confirm_redirect_uri.return_value = False - token_uri = 'https://i.b/token' - # Authorization code grant - _, _, body, _ = self.web.create_token_response(token_uri, - body='grant_type=authorization_code&code=foo') - self.assertEqual('access_denied', json.loads(body)['error']) - - def test_unsupported_response_type(self): - self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' - - # Authorization code grant - self.assertRaises(errors.UnsupportedResponseTypeError, - self.web.validate_authorization_request, - 'https://i.b/auth?response_type=foo&client_id=foo') - - # Implicit grant - self.assertRaises(errors.UnsupportedResponseTypeError, - self.mobile.validate_authorization_request, - 'https://i.b/auth?response_type=foo&client_id=foo') - - def test_invalid_scope(self): - self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' - self.validator.validate_scopes.return_value = False - self.validator.authenticate_client.side_effect = self.set_client - - # Authorization code grant - self.assertRaises(errors.InvalidScopeError, - self.web.validate_authorization_request, - 'https://i.b/auth?response_type=code&client_id=foo') - - # Implicit grant - self.assertRaises(errors.InvalidScopeError, - self.mobile.validate_authorization_request, - 'https://i.b/auth?response_type=token&client_id=foo') - - # Password credentials grant - _, _, body, _ = self.legacy.create_token_response( - 'https://i.b/token', - body='grant_type=password&username=foo&password=bar') - self.assertEqual('invalid_scope', json.loads(body)['error']) - - # Client credentials grant - _, _, body, _ = self.backend.create_token_response( - 'https://i.b/token', - body='grant_type=client_credentials') - self.assertEqual('invalid_scope', json.loads(body)['error']) - - def test_server_error(self): - def raise_error(*args, **kwargs): - raise ValueError() - - self.validator.validate_client_id.side_effect = raise_error - self.validator.authenticate_client.side_effect = raise_error - self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' - - # Authorization code grant - self.web.catch_errors = True - _, _, _, s = self.web.create_authorization_response( - 'https://i.b/auth?client_id=foo&response_type=code', - scopes=['foo']) - self.assertEqual(s, 500) - _, _, _, s = self.web.create_token_response( - 'https://i.b/token', - body='grant_type=authorization_code&code=foo', - scopes=['foo']) - self.assertEqual(s, 500) - - # Implicit grant - self.mobile.catch_errors = True - _, _, _, s = self.mobile.create_authorization_response( - 'https://i.b/auth?client_id=foo&response_type=token', - scopes=['foo']) - self.assertEqual(s, 500) - - # Password credentials grant - self.legacy.catch_errors = True - _, _, _, s = self.legacy.create_token_response( - 'https://i.b/token', - body='grant_type=password&username=foo&password=foo') - self.assertEqual(s, 500) - - # Client credentials grant - self.backend.catch_errors = True - _, _, _, s = self.backend.create_token_response( - 'https://i.b/token', - body='grant_type=client_credentials') - self.assertEqual(s, 500) - - def test_temporarily_unavailable(self): - # Authorization code grant - self.web.available = False - _, _, _, s = self.web.create_authorization_response( - 'https://i.b/auth?client_id=foo&response_type=code', - scopes=['foo']) - self.assertEqual(s, 503) - _, _, _, s = self.web.create_token_response( - 'https://i.b/token', - body='grant_type=authorization_code&code=foo', - scopes=['foo']) - self.assertEqual(s, 503) - - # Implicit grant - self.mobile.available = False - _, _, _, s = self.mobile.create_authorization_response( - 'https://i.b/auth?client_id=foo&response_type=token', - scopes=['foo']) - self.assertEqual(s, 503) - - # Password credentials grant - self.legacy.available = False - _, _, _, s = self.legacy.create_token_response( - 'https://i.b/token', - body='grant_type=password&username=foo&password=foo') - self.assertEqual(s, 503) - - # Client credentials grant - self.backend.available = False - _, _, _, s = self.backend.create_token_response( - 'https://i.b/token', - body='grant_type=client_credentials') - self.assertEqual(s, 503) - - def test_invalid_client(self): - self.validator.authenticate_client.return_value = False - self.validator.authenticate_client_id.return_value = False - - # Authorization code grant - _, _, body, _ = self.web.create_token_response('https://i.b/token', - body='grant_type=authorization_code&code=foo') - self.assertEqual('invalid_client', json.loads(body)['error']) - - # Password credentials grant - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', - body='grant_type=password&username=foo&password=bar') - self.assertEqual('invalid_client', json.loads(body)['error']) - - # Client credentials grant - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', - body='grant_type=client_credentials') - self.assertEqual('invalid_client', json.loads(body)['error']) - - def test_invalid_grant(self): - self.validator.authenticate_client.side_effect = self.set_client - - # Authorization code grant - self.validator.validate_code.return_value = False - _, _, body, _ = self.web.create_token_response('https://i.b/token', - body='grant_type=authorization_code&code=foo') - self.assertEqual('invalid_grant', json.loads(body)['error']) - - # Password credentials grant - self.validator.validate_user.return_value = False - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', - body='grant_type=password&username=foo&password=bar') - self.assertEqual('invalid_grant', json.loads(body)['error']) - - def test_unsupported_grant_type(self): - self.validator.authenticate_client.side_effect = self.set_client - - # Authorization code grant - _, _, body, _ = self.web.create_token_response('https://i.b/token', - body='grant_type=bar&code=foo') - self.assertEqual('unsupported_grant_type', json.loads(body)['error']) - - # Password credentials grant - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', - body='grant_type=bar&username=foo&password=bar') - self.assertEqual('unsupported_grant_type', json.loads(body)['error']) - - # Client credentials grant - _, _, body, _ = self.backend.create_token_response('https://i.b/token', - body='grant_type=bar') - self.assertEqual('unsupported_grant_type', json.loads(body)['error']) - - -class ExtraCredentialsTest(TestCase): - - def set_client(self, request): - request.client = mock.MagicMock() - request.client.client_id = 'mocked' - return True - - def setUp(self): - self.validator = mock.MagicMock(spec=RequestValidator) - self.validator.get_default_redirect_uri.return_value = 'https://i.b/cb' - self.web = WebApplicationServer(self.validator) - self.mobile = MobileApplicationServer(self.validator) - self.legacy = LegacyApplicationServer(self.validator) - self.backend = BackendApplicationServer(self.validator) - - def test_post_authorization_request(self): - def save_code(client_id, token, request): - self.assertEqual('creds', request.extra) - - def save_token(token, request): - self.assertEqual('creds', request.extra) - - # Authorization code grant - self.validator.save_authorization_code.side_effect = save_code - self.web.create_authorization_response( - 'https://i.b/auth?client_id=foo&response_type=code', - scopes=['foo'], - credentials={'extra': 'creds'}) - - # Implicit grant - self.validator.save_bearer_token.side_effect = save_token - self.web.create_authorization_response( - 'https://i.b/auth?client_id=foo&response_type=token', - scopes=['foo'], - credentials={'extra': 'creds'}) - - def test_token_request(self): - def save_token(token, request): - self.assertIn('extra', token) - - self.validator.save_bearer_token.side_effect = save_token - self.validator.authenticate_client.side_effect = self.set_client - - # Authorization code grant - self.web.create_token_response('https://i.b/token', - body='grant_type=authorization_code&code=foo', - credentials={'extra': 'creds'}) - - # Password credentials grant - self.legacy.create_token_response('https://i.b/token', - body='grant_type=password&username=foo&password=bar', - credentials={'extra': 'creds'}) - - # Client credentials grant - self.backend.create_token_response('https://i.b/token', - body='grant_type=client_credentials', - credentials={'extra': 'creds'}) diff -Nru python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_utils.py python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_utils.py --- python-oauthlib-0.5.1/tests/oauth2/rfc6749/test_utils.py 2013-05-31 10:11:50.000000000 +0000 +++ python-oauthlib-0.6.1/tests/oauth2/rfc6749/test_utils.py 2013-09-11 17:23:28.000000000 +0000 @@ -1,9 +1,13 @@ from __future__ import absolute_import, unicode_literals +import datetime import os + from ...unittest import TestCase from oauthlib.oauth2.rfc6749.utils import escape, host_from_uri +from oauthlib.oauth2.rfc6749.utils import generate_age from oauthlib.oauth2.rfc6749.utils import is_secure_transport +from oauthlib.oauth2.rfc6749.utils import params_from_uri class UtilsTests(TestCase): @@ -35,3 +39,12 @@ os.environ['DEBUG'] = '1' self.assertTrue(is_secure_transport('http://example.com')) del os.environ['DEBUG'] + + def test_params_from_uri(self): + self.assertEqual(params_from_uri('http://i.b/?foo=bar&g&scope=a+d'), + {'foo': 'bar', 'g': '', 'scope': ['a', 'd']}) + + def test_generate_age(self): + issue_time = datetime.datetime.now() - datetime.timedelta( + days=3, minutes=1, seconds=4) + self.assertGreater(float(generate_age(issue_time)), 259263.0) diff -Nru python-oauthlib-0.5.1/tests/test_common.py python-oauthlib-0.6.1/tests/test_common.py --- python-oauthlib-0.5.1/tests/test_common.py 2013-07-26 18:05:17.000000000 +0000 +++ python-oauthlib-0.6.1/tests/test_common.py 2013-09-13 09:29:50.000000000 +0000 @@ -1,19 +1,33 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, unicode_literals import sys -from oauthlib.common import * + from .unittest import TestCase +from oauthlib.common import add_params_to_uri +from oauthlib.common import CaseInsensitiveDict +from oauthlib.common import extract_params +from oauthlib.common import generate_client_id +from oauthlib.common import generate_nonce +from oauthlib.common import generate_timestamp +from oauthlib.common import generate_token +from oauthlib.common import Request +from oauthlib.common import unicode_type +from oauthlib.common import urldecode + + if sys.version_info[0] == 3: bytes_type = bytes else: bytes_type = lambda s, e: str(s) -class CommonTests(TestCase): - params_dict = {'foo': 'bar', 'baz': '123', } - params_twotuple = [('foo', 'bar'), ('baz', '123')] - params_formencoded = 'foo=bar&baz=123' - uri = 'http://www.someuri.com' +PARAMS_DICT = {'foo': 'bar', 'baz': '123', } +PARAMS_TWOTUPLE = [('foo', 'bar'), ('baz', '123')] +PARAMS_FORMENCODED = 'foo=bar&baz=123' +URI = 'http://www.someuri.com' + + +class EncodingTest(TestCase): def test_urldecode(self): self.assertItemsEqual(urldecode(''), []) @@ -23,7 +37,8 @@ self.assertItemsEqual(urldecode('c2'), [('c2', '')]) self.assertItemsEqual(urldecode('c2='), [('c2', '')]) self.assertItemsEqual(urldecode('foo=bar'), [('foo', 'bar')]) - self.assertItemsEqual(urldecode('foo_%20~=.bar-'), [('foo_ ~', '.bar-')]) + self.assertItemsEqual(urldecode('foo_%20~=.bar-'), + [('foo_ ~', '.bar-')]) self.assertItemsEqual(urldecode('foo=1,2,3'), [('foo', '1,2,3')]) self.assertItemsEqual(urldecode('foo=bar.*'), [('foo', 'bar.*')]) self.assertRaises(ValueError, urldecode, 'foo bar') @@ -33,14 +48,18 @@ self.assertRaises(ValueError, urldecode, '%AR') self.assertRaises(ValueError, urldecode, '%RR') + +class ParameterTest(TestCase): + def test_extract_params_dict(self): - self.assertItemsEqual(extract_params(self.params_dict), self.params_twotuple) + self.assertItemsEqual(extract_params(PARAMS_DICT), PARAMS_TWOTUPLE) def test_extract_params_twotuple(self): - self.assertItemsEqual(extract_params(self.params_twotuple), self.params_twotuple) + self.assertItemsEqual(extract_params(PARAMS_TWOTUPLE), PARAMS_TWOTUPLE) def test_extract_params_formencoded(self): - self.assertItemsEqual(extract_params(self.params_formencoded), self.params_twotuple) + self.assertItemsEqual(extract_params(PARAMS_FORMENCODED), + PARAMS_TWOTUPLE) def test_extract_params_blank_string(self): self.assertItemsEqual(extract_params(''), []) @@ -53,62 +72,24 @@ def test_extract_invalid(self): self.assertEqual(extract_params(object()), None) + self.assertEqual(extract_params([('')]), None) - def test_non_unicode_params(self): - r = Request(bytes_type('http://a.b/path?query', 'utf-8'), - http_method=bytes_type('GET', 'utf-8'), - body=bytes_type('you=shall+pass', 'utf-8'), - headers={bytes_type('a', 'utf-8'): bytes_type('b', 'utf-8')}) - self.assertEqual(r.uri, 'http://a.b/path?query') - self.assertEqual(r.http_method, 'GET') - self.assertEqual(r.body, 'you=shall+pass') - self.assertEqual(r.decoded_body, [('you', 'shall pass')]) - self.assertEqual(r.headers, {'a': 'b'}) - - def test_none_body(self): - r = Request(self.uri) - self.assertEqual(r.decoded_body, None) + def test_add_params_to_uri(self): + correct = '%s?%s' % (URI, PARAMS_FORMENCODED) + self.assertURLEqual(add_params_to_uri(URI, PARAMS_DICT), correct) + self.assertURLEqual(add_params_to_uri(URI, PARAMS_TWOTUPLE), correct) - def test_empty_list_body(self): - r = Request(self.uri, body=[]) - self.assertEqual(r.decoded_body, []) - def test_empty_dict_body(self): - r = Request(self.uri, body={}) - self.assertEqual(r.decoded_body, []) - - def test_empty_string_body(self): - r = Request(self.uri, body='') - self.assertEqual(r.decoded_body, []) - - def test_non_formencoded_string_body(self): - body = 'foo bar' - r = Request(self.uri, body=body) - self.assertEqual(r.decoded_body, None) - - def test_param_free_sequence_body(self): - body = [1, 1, 2, 3, 5, 8, 13] - r = Request(self.uri, body=body) - self.assertEqual(r.decoded_body, None) - - def test_list_body(self): - r = Request(self.uri, body=self.params_twotuple) - self.assertItemsEqual(r.decoded_body, self.params_twotuple) - - def test_dict_body(self): - r = Request(self.uri, body=self.params_dict) - self.assertItemsEqual(r.decoded_body, self.params_twotuple) +class GeneratorTest(TestCase): def test_generate_timestamp(self): - """ TODO: Better test here """ timestamp = generate_timestamp() self.assertIsInstance(timestamp, unicode_type) self.assertTrue(int(timestamp)) - self.assertGreater(int(timestamp), 1331672335) # is this increasing? + self.assertGreater(int(timestamp), 1331672335) def test_generate_nonce(self): - """ TODO: better test here """ - + """Ping me (ib-lundgren) when you discover how to test randomness.""" nonce = generate_nonce() for i in range(50): self.assertNotEqual(nonce, generate_nonce()) @@ -122,7 +103,8 @@ token = generate_token(length=6, chars="python") self.assertEqual(len(token), 6) - self.assertNotIn("a", token) + for c in token: + self.assertIn(c, "python") def test_generate_client_id(self): client_id = generate_client_id() @@ -133,7 +115,60 @@ client_id = generate_client_id(length=6, chars="python") self.assertEqual(len(client_id), 6) - self.assertNotIn("a", client_id) + for c in client_id: + self.assertIn(c, "python") + + +class RequestTest(TestCase): + + def test_non_unicode_params(self): + r = Request( + bytes_type('http://a.b/path?query', 'utf-8'), + http_method=bytes_type('GET', 'utf-8'), + body=bytes_type('you=shall+pass', 'utf-8'), + headers={ + bytes_type('a', 'utf-8'): bytes_type('b', 'utf-8') + } + ) + self.assertEqual(r.uri, 'http://a.b/path?query') + self.assertEqual(r.http_method, 'GET') + self.assertEqual(r.body, 'you=shall+pass') + self.assertEqual(r.decoded_body, [('you', 'shall pass')]) + self.assertEqual(r.headers, {'a': 'b'}) + + def test_none_body(self): + r = Request(URI) + self.assertEqual(r.decoded_body, None) + + def test_empty_list_body(self): + r = Request(URI, body=[]) + self.assertEqual(r.decoded_body, []) + + def test_empty_dict_body(self): + r = Request(URI, body={}) + self.assertEqual(r.decoded_body, []) + + def test_empty_string_body(self): + r = Request(URI, body='') + self.assertEqual(r.decoded_body, []) + + def test_non_formencoded_string_body(self): + body = 'foo bar' + r = Request(URI, body=body) + self.assertEqual(r.decoded_body, None) + + def test_param_free_sequence_body(self): + body = [1, 1, 2, 3, 5, 8, 13] + r = Request(URI, body=body) + self.assertEqual(r.decoded_body, None) + + def test_list_body(self): + r = Request(URI, body=PARAMS_TWOTUPLE) + self.assertItemsEqual(r.decoded_body, PARAMS_TWOTUPLE) + + def test_dict_body(self): + r = Request(URI, body=PARAMS_DICT) + self.assertItemsEqual(r.decoded_body, PARAMS_TWOTUPLE) class CaseInsensitiveDictTest(TestCase):