Hello community, here is the log from the commit of package python-oauthlib for openSUSE:Factory checked in at 2017-08-22 11:10:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-oauthlib (Old) and /work/SRC/openSUSE:Factory/.python-oauthlib.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-oauthlib" Tue Aug 22 11:10:28 2017 rev:17 rq:517961 version:2.0.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-oauthlib/python-oauthlib.changes 2017-05-10 20:50:38.517793815 +0200 +++ /work/SRC/openSUSE:Factory/.python-oauthlib.new/python-oauthlib.changes 2017-08-22 11:10:31.316827838 +0200 @@ -1,0 +2,8 @@ +Mon Aug 21 14:03:34 UTC 2017 - [email protected] + +- update to 2.0.2: + * Dropped support for Python 2.6, 3.2 & 3.3. + * (FIX) `OpenIDConnector` will no longer raise an AttributeError when calling + `openid_authorization_validator()` twice. + +------------------------------------------------------------------- Old: ---- oauthlib-2.0.1.tar.gz New: ---- oauthlib-2.0.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-oauthlib.spec ++++++ --- /var/tmp/diff_new_pack.95ZvSp/_old 2017-08-22 11:10:32.140711831 +0200 +++ /var/tmp/diff_new_pack.95ZvSp/_new 2017-08-22 11:10:32.148710704 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-oauthlib -Version: 2.0.1 +Version: 2.0.2 Release: 0 Url: https://github.com/idangazit/oauthlib Summary: A Generic Implementation of the OAuth Request-Signing Logic @@ -26,27 +26,22 @@ Group: Development/Languages/Python Source: https://pypi.io/packages/source/o/oauthlib/oauthlib-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build - -BuildRequires: fdupes -BuildRequires: python-rpm-macros - +BuildRequires: %{python_module PyJWT} BuildRequires: %{python_module blinker} BuildRequires: %{python_module cryptography} BuildRequires: %{python_module devel} BuildRequires: %{python_module mock} BuildRequires: %{python_module nose} -BuildRequires: %{python_module PyJWT} BuildRequires: %{python_module pyasn1} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module unittest2} - +BuildRequires: fdupes +BuildRequires: python-rpm-macros +Requires: python-PyJWT Requires: python-blinker Requires: python-cryptography -Requires: python-PyJWT - BuildArch: noarch - %python_subpackages %description ++++++ oauthlib-2.0.1.tar.gz -> oauthlib-2.0.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/CHANGELOG.rst new/oauthlib-2.0.2/CHANGELOG.rst --- old/oauthlib-2.0.1/CHANGELOG.rst 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/CHANGELOG.rst 2017-03-19 13:50:31.000000000 +0100 @@ -1,6 +1,14 @@ Changelog ========= +Development +----------- + +2.0.2 (2017-03-19) +------------------ +* Dropped support for Python 2.6, 3.2 & 3.3. +* (FIX) `OpenIDConnector` will no longer raise an AttributeError when calling `openid_authorization_validator()` twice. + 2.0.1 (2016-11-23) ------------------ * (FIX) Normalize handling of request.scopes list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/PKG-INFO new/oauthlib-2.0.2/PKG-INFO --- old/oauthlib-2.0.1/PKG-INFO 2016-11-23 11:31:05.000000000 +0100 +++ new/oauthlib-2.0.2/PKG-INFO 2017-03-19 13:51:07.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: oauthlib -Version: 2.0.1 +Version: 2.0.2 Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic Home-page: https://github.com/idan/oauthlib Author: Ib Lundgren @@ -109,7 +109,7 @@ For a full changelog see ``CHANGELOG.rst``. Platform: any -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved @@ -118,11 +118,10 @@ Classifier: Operating System :: POSIX Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/__init__.py new/oauthlib-2.0.2/oauthlib/__init__.py --- old/oauthlib-2.0.1/oauthlib/__init__.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/__init__.py 2017-03-19 13:50:31.000000000 +0100 @@ -10,7 +10,7 @@ """ __author__ = 'Idan Gazit <[email protected]>' -__version__ = '2.0.1' +__version__ = '2.0.2' import logging diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth1/rfc5849/__init__.py new/oauthlib-2.0.2/oauthlib/oauth1/rfc5849/__init__.py --- old/oauthlib-2.0.1/oauthlib/oauth1/rfc5849/__init__.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth1/rfc5849/__init__.py 2017-03-19 13:50:31.000000000 +0100 @@ -105,6 +105,7 @@ def __repr__(self): attrs = vars(self).copy() attrs['client_secret'] = '****' if attrs['client_secret'] else None + attrs['rsa_key'] = '****' if attrs['rsa_key'] else None attrs[ 'resource_owner_secret'] = '****' if attrs['resource_owner_secret'] else None attribute_str = ', '.join('%s=%s' % (k, v) for k, v in attrs.items()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py --- old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py 2017-03-19 13:50:31.000000000 +0100 @@ -13,7 +13,6 @@ from .base import GrantTypeBase from .. import errors -from ..request_validator import RequestValidator log = logging.getLogger(__name__) @@ -96,31 +95,7 @@ """ default_response_mode = 'query' - - def __init__(self, request_validator=None, refresh_token=True): - self.request_validator = request_validator or RequestValidator() - self.refresh_token = refresh_token - - self._authorization_validators = [] - self._token_validators = [] - self._code_modifiers = [] - self._token_modifiers = [] - self.response_types = ['code'] - - def register_response_type(self, response_type): - self.response_types.append(response_type) - - def register_authorization_validator(self, validator): - self._authorization_validators.append(validator) - - def register_token_validator(self, validator): - self._token_validators.append(validator) - - def register_code_modifier(self, modifier): - self._code_modifiers.append(modifier) - - def register_token_modifier(self, modifier): - self._token_modifiers.append(modifier) + response_types = ['code'] def create_authorization_code(self, request): """Generates an authorization grant represented as a dictionary.""" @@ -347,6 +322,10 @@ # Note that the correct parameters to be added are automatically # populated through the use of specific exceptions. + request_info = {} + for validator in self.custom_validators.pre_auth: + request_info.update(validator(request)) + # REQUIRED. if request.response_type is None: raise errors.MissingResponseTypeError(request=request) @@ -367,15 +346,15 @@ # http://tools.ietf.org/html/rfc6749#section-3.3 self.validate_scopes(request) - request_info = { + request_info.update({ 'client_id': request.client_id, 'redirect_uri': request.redirect_uri, 'response_type': request.response_type, 'state': request.state, 'request': request - } + }) - for validator in self._authorization_validators: + for validator in self.custom_validators.post_auth: request_info.update(validator(request)) return request.scopes, request_info @@ -385,6 +364,9 @@ if request.grant_type not in ('authorization_code', 'openid'): raise errors.UnsupportedGrantTypeError(request=request) + for validator in self.custom_validators.pre_token: + validator(request) + if request.code is None: raise errors.InvalidRequestError( description='Missing code parameter.', request=request) @@ -415,6 +397,8 @@ 'request.client.client_id attribute ' 'in authenticate_client.') + request.client_id = request.client_id or request.client.client_id + # Ensure client is authorized use of this grant type self.validate_grant_type(request) @@ -439,6 +423,5 @@ request.redirect_uri, request.client_id, request.client) raise errors.AccessDeniedError(request=request) - for validator in self._token_validators: + for validator in self.custom_validators.post_token: validator(request) - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/base.py new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/base.py --- old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/base.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/base.py 2017-03-19 13:50:31.000000000 +0100 @@ -4,19 +4,117 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """ from __future__ import unicode_literals, absolute_import +from itertools import chain import logging from oauthlib.common import add_params_to_uri from oauthlib.oauth2.rfc6749 import errors, utils +from ..request_validator import RequestValidator log = logging.getLogger(__name__) +class ValidatorsContainer(object): + """ + Container object for holding custom validator callables to be invoked + as part of the grant type `validate_authorization_request()` or + `validate_authorization_request()` methods on the various grant types. + + Authorization validators must be callables that take a request object and + return a dict, which may contain items to be added to the `request_info` + returned from the grant_type after validation. + + Token validators must be callables that take a request object and + return None. + + Both authorization validators and token validators may raise OAuth2 + exceptions if validation conditions fail. + + Authorization validators added to `pre_auth` will be run BEFORE + the standard validations (but after the critical ones that raise + fatal errors) as part of `validate_authorization_request()` + + Authorization validators added to `post_auth` will be run AFTER + the standard validations as part of `validate_authorization_request()` + + Token validators added to `pre_token` will be run BEFORE + the standard validations as part of `validate_token_request()` + + Token validators added to `post_token` will be run AFTER + the standard validations as part of `validate_token_request()` + + For example: + + >>> def my_auth_validator(request): + ... return {'myval': True} + >>> auth_code_grant = AuthorizationCodeGrant(request_validator) + >>> auth_code_grant.custom_validators.pre_auth.append(my_auth_validator) + >>> def my_token_validator(request): + ... if not request.everything_okay: + ... raise errors.OAuth2Error("uh-oh") + >>> auth_code_grant.custom_validators.post_token.append(my_token_validator) + """ + + def __init__(self, post_auth, post_token, + pre_auth, pre_token): + self.pre_auth = pre_auth + self.post_auth = post_auth + self.pre_token = pre_token + self.post_token = post_token + + @property + def all_pre(self): + return chain(self.pre_auth, self.pre_token) + + @property + def all_post(self): + return chain(self.post_auth, self.post_token) + class GrantTypeBase(object): error_uri = None request_validator = None default_response_mode = 'fragment' + refresh_token = True + response_types = ['code'] + + def __init__(self, request_validator=None, **kwargs): + self.request_validator = request_validator or RequestValidator() + + # Transforms class variables into instance variables: + self.response_types = self.response_types + self.refresh_token = self.refresh_token + self._setup_custom_validators(kwargs) + self._code_modifiers = [] + self._token_modifiers = [] + + for kw, val in kwargs.items(): + setattr(self, kw, val) + + def _setup_custom_validators(self, kwargs): + post_auth = kwargs.get('post_auth', []) + post_token = kwargs.get('post_token', []) + pre_auth = kwargs.get('pre_auth', []) + pre_token = kwargs.get('pre_token', []) + if not hasattr(self, 'validate_authorization_request'): + if post_auth or pre_auth: + msg = ("{} does not support authorization validators. Use " + "token validators instead.").format(self.__class__.__name__) + raise ValueError(msg) + # Using tuples here because they can't be appended to: + post_auth, pre_auth = (), () + self.custom_validators = ValidatorsContainer(post_auth, post_token, + pre_auth, pre_token) + + def register_response_type(self, response_type): + self.response_types.append(response_type) + + def register_code_modifier(self, modifier): + self._code_modifiers.append(modifier) + + def register_token_modifier(self, modifier): + self._token_modifiers.append(modifier) + def create_authorization_response(self, request, token_handler): raise NotImplementedError('Subclasses must implement this method.') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py --- old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py 2017-03-19 13:50:31.000000000 +0100 @@ -50,13 +50,6 @@ .. _`Client Credentials Grant`: http://tools.ietf.org/html/rfc6749#section-4.4 """ - def __init__(self, request_validator=None): - self.request_validator = request_validator or RequestValidator() - self._token_modifiers = [] - - def register_token_modifier(self, modifier): - self._token_modifiers.append(modifier) - def create_token_response(self, request, token_handler): """Return token or error in JSON format. @@ -92,6 +85,9 @@ return headers, json.dumps(token), 200 def validate_token_request(self, request): + for validator in self.custom_validators.pre_token: + validator(request) + if not getattr(request, 'grant_type', None): raise errors.InvalidRequestError('Request is missing grant type.', request=request) @@ -119,3 +115,6 @@ log.debug('Authorizing access to user %r.', request.user) request.client_id = request.client_id or request.client.client_id self.validate_scopes(request) + + for validator in self.custom_validators.post_token: + validator(request) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/implicit.py new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/implicit.py --- old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/implicit.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/implicit.py 2017-03-19 13:50:31.000000000 +0100 @@ -117,20 +117,8 @@ .. _`Section 10.16`: http://tools.ietf.org/html/rfc6749#section-10.16 """ - def __init__(self, request_validator=None): - self.request_validator = request_validator or RequestValidator() - self._authorization_validators = [] - self._token_modifiers = [] - self.response_types = ['token'] - - def register_response_type(self, response_type): - self.response_types.append(response_type) - - def register_authorization_validator(self, validator): - self._authorization_validators.append(validator) - - def register_token_modifier(self, modifier): - self._token_modifiers.append(modifier) + response_types = ['token'] + grant_allows_refresh_token = False def create_authorization_response(self, request, token_handler): """Create an authorization response. @@ -328,6 +316,10 @@ # Then check for normal errors. + request_info = self._run_custom_validators(request, + self.custom_validators.all_pre) + + # If the resource owner denies the access request or if the request # fails for reasons other than a missing or invalid redirection URI, # the authorization server informs the client by adding the following @@ -359,15 +351,32 @@ # http://tools.ietf.org/html/rfc6749#section-3.3 self.validate_scopes(request) - request_info = { + request_info.update({ 'client_id': request.client_id, 'redirect_uri': request.redirect_uri, 'response_type': request.response_type, 'state': request.state, 'request': request, - } + }) - for validator in self._authorization_validators: - request_info.update(validator(request)) + request_info = self._run_custom_validators(request, + self.custom_validators.all_post, + request_info) return request.scopes, request_info + + + def _run_custom_validators(self, + request, + validations, + request_info=None): + # Make a copy so we don't modify the existing request_info dict + request_info = {} if request_info is None else request_info.copy() + # For implicit grant, auth_validators and token_validators are + # basically equivalent since the token is returned from the + # authorization endpoint. + for validator in validations: + result = validator(request) + if result is not None: + request_info.update(result) + return request_info diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py --- old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py 2017-03-19 13:50:31.000000000 +0100 @@ -77,10 +77,33 @@ return self._handler_for_request(request).validate_authorization_request(request) -class OpenIDConnectBase(GrantTypeBase): +class OpenIDConnectBase(object): - def __init__(self, request_validator=None): - self.request_validator = request_validator or RequestValidator() + # Just proxy the majority of method calls through to the + # proxy_target grant type handler, which will usually be either + # the standard OAuth2 AuthCode or Implicit grant types. + def __getattr__(self, attr): + return getattr(self.proxy_target, attr) + + def __setattr__(self, attr, value): + proxied_attrs = set(('refresh_token', 'response_types')) + if attr in proxied_attrs: + setattr(self.proxy_target, attr, value) + else: + super(OpenIDConnectBase, self).__setattr__(attr, value) + + def validate_authorization_request(self, request): + """Validates the OpenID Connect authorization request parameters. + + :returns: (list of scopes, dict of request info) + """ + # If request.prompt is 'none' then no login/authorization form should + # be presented to the user. Instead, a silent login/authorization + # should be performed. + if request.prompt == 'none': + raise OIDCNoPrompt() + else: + return self.proxy_target.validate_authorization_request(request) def _inflate_claims(self, request): # this may be called multiple times in a single request so make sure we only de-serialize the claims once @@ -270,10 +293,15 @@ msg = "Session user does not match client supplied user." raise LoginRequired(request=request, description=msg) + prompt = [] + if request.prompt: + prompt = request.prompt + if hasattr(prompt, 'split'): + prompt = prompt.split() request_info = { 'display': request.display, - 'prompt': request.prompt.split() if request.prompt else [], + 'prompt': prompt, 'ui_locales': request.ui_locales.split() if request.ui_locales else [], 'id_token_hint': request.id_token_hint, 'login_hint': request.login_hint, @@ -309,135 +337,40 @@ class OpenIDConnectAuthCode(OpenIDConnectBase): - def __init__(self, request_validator=None): - self.request_validator = request_validator or RequestValidator() - super(OpenIDConnectAuthCode, self).__init__( - request_validator=self.request_validator) - self.auth_code = AuthorizationCodeGrant( - request_validator=self.request_validator) - self.auth_code.register_authorization_validator( + def __init__(self, request_validator=None, **kwargs): + self.proxy_target = AuthorizationCodeGrant( + request_validator=request_validator, **kwargs) + self.custom_validators.post_auth.append( self.openid_authorization_validator) - self.auth_code.register_token_modifier(self.add_id_token) - - @property - def refresh_token(self): - return self.auth_code.refresh_token - - @refresh_token.setter - def refresh_token(self, value): - self.auth_code.refresh_token = value - - def create_authorization_code(self, request): - return self.auth_code.create_authorization_code(request) - - def create_authorization_response(self, request, token_handler): - return self.auth_code.create_authorization_response( - request, token_handler) - - def create_token_response(self, request, token_handler): - return self.auth_code.create_token_response(request, token_handler) - - def validate_authorization_request(self, request): - """Validates the OpenID Connect authorization request parameters. - - :returns: (list of scopes, dict of request info) - """ - # If request.prompt is 'none' then no login/authorization form should - # be presented to the user. Instead, a silent login/authorization - # should be performed. - if request.prompt == 'none': - raise OIDCNoPrompt() - else: - return self.auth_code.validate_authorization_request(request) - - def validate_token_request(self, request): - return self.auth_code.validate_token_request(request) - + self.register_token_modifier(self.add_id_token) class OpenIDConnectImplicit(OpenIDConnectBase): - def __init__(self, request_validator=None): - self.request_validator = request_validator or RequestValidator() - super(OpenIDConnectImplicit, self).__init__( - request_validator=self.request_validator) - self.implicit = ImplicitGrant( - request_validator=request_validator) - self.implicit.register_response_type('id_token') - self.implicit.register_response_type('id_token token') - self.implicit.register_authorization_validator( + def __init__(self, request_validator=None, **kwargs): + self.proxy_target = ImplicitGrant( + request_validator=request_validator, **kwargs) + self.register_response_type('id_token') + self.register_response_type('id_token token') + self.custom_validators.post_auth.append( self.openid_authorization_validator) - self.implicit.register_authorization_validator( + self.custom_validators.post_auth.append( self.openid_implicit_authorization_validator) - self.implicit.register_token_modifier(self.add_id_token) - - def create_authorization_response(self, request, token_handler): - return self.create_token_response(request, token_handler) - - def create_token_response(self, request, token_handler): - return self.implicit.create_authorization_response( - request, token_handler) - - def validate_authorization_request(self, request): - """Validates the OpenID Connect authorization request parameters. - - :returns: (list of scopes, dict of request info) - """ - # If request.prompt is 'none' then no login/authorization form should - # be presented to the user. Instead, a silent login/authorization - # should be performed. - if request.prompt == 'none': - raise OIDCNoPrompt() - else: - return self.implicit.validate_authorization_request(request) - + self.register_token_modifier(self.add_id_token) class OpenIDConnectHybrid(OpenIDConnectBase): - def __init__(self, request_validator=None): + def __init__(self, request_validator=None, **kwargs): self.request_validator = request_validator or RequestValidator() - self.auth_code = AuthorizationCodeGrant( - request_validator=request_validator) - self.auth_code.register_response_type('code id_token') - self.auth_code.register_response_type('code token') - self.auth_code.register_response_type('code id_token token') - self.auth_code.register_authorization_validator( + self.proxy_target = AuthorizationCodeGrant( + request_validator=request_validator, **kwargs) + self.register_response_type('code id_token') + self.register_response_type('code token') + self.register_response_type('code id_token token') + self.custom_validators.post_auth.append( self.openid_authorization_validator) - self.auth_code.register_code_modifier(self.add_token) - self.auth_code.register_code_modifier(self.add_id_token) - self.auth_code.register_token_modifier(self.add_id_token) - - @property - def refresh_token(self): - return self.auth_code.refresh_token - - @refresh_token.setter - def refresh_token(self, value): - self.auth_code.refresh_token = value - - def create_authorization_code(self, request): - return self.auth_code.create_authorization_code(request) - - def create_authorization_response(self, request, token_handler): - return self.auth_code.create_authorization_response( - request, token_handler) - - def create_token_response(self, request, token_handler): - return self.auth_code.create_token_response(request, token_handler) - - def validate_authorization_request(self, request): - """Validates the OpenID Connect authorization request parameters. - - :returns: (list of scopes, dict of request info) - """ - # If request.prompt is 'none' then no login/authorization form should - # be presented to the user. Instead, a silent login/authorization - # should be performed. - if request.prompt == 'none': - raise OIDCNoPrompt() - else: - return self.auth_code.validate_authorization_request(request) - - def validate_token_request(self, request): - return self.auth_code.validate_token_request(request) - + # Hybrid flows can return the id_token from the authorization + # endpoint as part of the 'code' response + self.register_code_modifier(self.add_token) + self.register_code_modifier(self.add_id_token) + self.register_token_modifier(self.add_id_token) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py --- old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py 2017-03-19 13:50:31.000000000 +0100 @@ -22,13 +22,13 @@ .. _`Refresh token grant`: http://tools.ietf.org/html/rfc6749#section-6 """ - def __init__(self, request_validator=None, issue_new_refresh_tokens=True): - self.request_validator = request_validator or RequestValidator() - self.issue_new_refresh_tokens = issue_new_refresh_tokens - self._token_modifiers = [] - - def register_token_modifier(self, modifier): - self._token_modifiers.append(modifier) + def __init__(self, request_validator=None, + issue_new_refresh_tokens=True, + **kwargs): + super(RefreshTokenGrant, self).__init__( + request_validator, + issue_new_refresh_tokens=issue_new_refresh_tokens, + **kwargs) def create_token_response(self, request, token_handler): """Create a new access token from a refresh_token. @@ -76,6 +76,9 @@ if request.grant_type != 'refresh_token': raise errors.UnsupportedGrantTypeError(request=request) + for validator in self.custom_validators.pre_token: + validator(request) + if request.refresh_token is None: raise errors.InvalidRequestError( description='Missing refresh token parameter.', @@ -123,3 +126,6 @@ raise errors.InvalidScopeError(request=request) else: request.scopes = original_scopes + + for validator in self.custom_validators.post_token: + validator(request) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py --- old/oauthlib-2.0.1/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py 2017-03-19 13:50:31.000000000 +0100 @@ -70,18 +70,6 @@ .. _`Resource Owner Password Credentials Grant`: http://tools.ietf.org/html/rfc6749#section-4.3 """ - def __init__(self, request_validator=None, refresh_token=True): - """ - If the refresh_token keyword argument is False, do not return - a refresh token in the response. - """ - self.request_validator = request_validator or RequestValidator() - self.refresh_token = refresh_token - self._token_modifiers = [] - - def register_token_modifier(self, modifier): - self._token_modifiers.append(modifier) - def create_token_response(self, request, token_handler): """Return token or error in json format. @@ -168,6 +156,9 @@ .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3 .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1 """ + for validator in self.custom_validators.pre_token: + validator(request) + for param in ('grant_type', 'username', 'password'): if not getattr(request, param, None): raise errors.InvalidRequestError( @@ -201,3 +192,6 @@ if request.client: request.client_id = request.client_id or request.client.client_id self.validate_scopes(request) + + for validator in self.custom_validators.post_token: + validator(request) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib.egg-info/PKG-INFO new/oauthlib-2.0.2/oauthlib.egg-info/PKG-INFO --- old/oauthlib-2.0.1/oauthlib.egg-info/PKG-INFO 2016-11-23 11:31:05.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib.egg-info/PKG-INFO 2017-03-19 13:51:07.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: oauthlib -Version: 2.0.1 +Version: 2.0.2 Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic Home-page: https://github.com/idan/oauthlib Author: Ib Lundgren @@ -109,7 +109,7 @@ For a full changelog see ``CHANGELOG.rst``. Platform: any -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved @@ -118,11 +118,10 @@ Classifier: Operating System :: POSIX Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/oauthlib.egg-info/SOURCES.txt new/oauthlib-2.0.2/oauthlib.egg-info/SOURCES.txt --- old/oauthlib-2.0.1/oauthlib.egg-info/SOURCES.txt 2016-11-23 11:31:05.000000000 +0100 +++ new/oauthlib-2.0.2/oauthlib.egg-info/SOURCES.txt 2017-03-19 13:51:07.000000000 +0100 @@ -93,6 +93,7 @@ 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_prompt_handling.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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/setup.cfg new/oauthlib-2.0.2/setup.cfg --- old/oauthlib-2.0.1/setup.cfg 2016-11-23 11:31:05.000000000 +0100 +++ new/oauthlib-2.0.2/setup.cfg 2017-03-19 13:51:07.000000000 +0100 @@ -1,5 +1,4 @@ [egg_info] -tag_date = 0 tag_build = -tag_svn_revision = 0 +tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/setup.py new/oauthlib-2.0.2/setup.py --- old/oauthlib-2.0.1/setup.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/setup.py 2017-03-19 13:50:31.000000000 +0100 @@ -50,7 +50,7 @@ }, install_requires=requires, classifiers=[ - 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved', @@ -59,11 +59,10 @@ 'Operating System :: POSIX', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/tests/oauth2/rfc6749/endpoints/test_prompt_handling.py new/oauthlib-2.0.2/tests/oauth2/rfc6749/endpoints/test_prompt_handling.py --- old/oauthlib-2.0.1/tests/oauth2/rfc6749/endpoints/test_prompt_handling.py 1970-01-01 01:00:00.000000000 +0100 +++ new/oauthlib-2.0.2/tests/oauth2/rfc6749/endpoints/test_prompt_handling.py 2017-03-19 13:50:31.000000000 +0100 @@ -0,0 +1,50 @@ +from __future__ import absolute_import, unicode_literals +try: + from urllib.parse import urlencode +except ImportError: + from urllib import urlencode + +import mock + +from ....unittest import TestCase +from oauthlib.oauth2.rfc6749.tokens import BearerToken +from oauthlib.oauth2.rfc6749.grant_types import OpenIDConnectAuthCode +from oauthlib.oauth2.rfc6749.endpoints.authorization import AuthorizationEndpoint + +class OpenIDConnectEndpointTest(TestCase): + + def setUp(self): + self.mock_validator = mock.MagicMock() + self.mock_validator.authenticate_client.side_effect = self.set_client + grant = OpenIDConnectAuthCode(request_validator=self.mock_validator) + bearer = BearerToken(self.mock_validator) + self.endpoint = AuthorizationEndpoint(grant, bearer, + response_types={'code': grant}) + params = { + 'prompt': 'consent', + 'state': 'abc', + 'redirect_uri': 'https://a.b/cb', + 'response_type': 'code', + 'client_id': 'abcdef', + 'scope': 'hello openid' + } + self.url = 'http://a.b/path?' + urlencode(params) + + def set_client(self, request): + request.client = mock.MagicMock() + request.client.client_id = 'mocked' + return True + + @mock.patch('oauthlib.common.generate_token') + def test_authorization_endpoint_handles_prompt(self, generate_token): + generate_token.return_value = "MOCK_CODE" + # In the GET view: + scopes, creds = self.endpoint.validate_authorization_request(self.url) + # In the POST view: + creds['scopes'] = scopes + h, b, s = self.endpoint.create_authorization_response(self.url, + credentials=creds) + expected = 'https://a.b/cb?state=abc&code=MOCK_CODE' + self.assertURLEqual(h['Location'], expected) + self.assertEqual(b, None) + self.assertEqual(s, 302) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_authorization_code.py --- old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_authorization_code.py 2017-03-19 13:50:31.000000000 +0100 @@ -32,6 +32,38 @@ request.client.client_id = 'mocked' return True + def setup_validators(self): + self.authval1, self.authval2 = mock.Mock(), mock.Mock() + self.authval1.return_value = {} + self.authval2.return_value = {} + self.tknval1, self.tknval2 = mock.Mock(), mock.Mock() + self.tknval1.return_value = None + self.tknval2.return_value = None + self.auth.custom_validators.pre_token.append(self.tknval1) + self.auth.custom_validators.post_token.append(self.tknval2) + self.auth.custom_validators.pre_auth.append(self.authval1) + self.auth.custom_validators.post_auth.append(self.authval2) + + def test_custom_auth_validators(self): + self.setup_validators() + + bearer = BearerToken(self.mock_validator) + self.auth.create_authorization_response(self.request, bearer) + self.assertTrue(self.authval1.called) + self.assertTrue(self.authval2.called) + self.assertFalse(self.tknval1.called) + self.assertFalse(self.tknval2.called) + + def test_custom_token_validators(self): + self.setup_validators() + + bearer = BearerToken(self.mock_validator) + self.auth.create_token_response(self.request, bearer) + self.assertTrue(self.tknval1.called) + self.assertTrue(self.tknval2.called) + self.assertFalse(self.authval1.called) + self.assertFalse(self.authval2.called) + def test_create_authorization_grant(self): bearer = BearerToken(self.mock_validator) h, b, s = self.auth.create_authorization_response(self.request, bearer) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_client_credentials.py new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_client_credentials.py --- old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_client_credentials.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_client_credentials.py 2017-03-19 13:50:31.000000000 +0100 @@ -22,6 +22,31 @@ self.auth = ClientCredentialsGrant( request_validator=self.mock_validator) + def test_custom_auth_validators_unsupported(self): + authval1, authval2 = mock.Mock(), mock.Mock() + expected = ('ClientCredentialsGrant does not support authorization ' + 'validators. Use token validators instead.') + with self.assertRaises(ValueError) as caught: + ClientCredentialsGrant(self.mock_validator, pre_auth=[authval1]) + self.assertEqual(caught.exception.args[0], expected) + with self.assertRaises(ValueError) as caught: + ClientCredentialsGrant(self.mock_validator, post_auth=[authval2]) + self.assertEqual(caught.exception.args[0], expected) + with self.assertRaises(AttributeError): + self.auth.custom_validators.pre_auth.append(authval1) + with self.assertRaises(AttributeError): + self.auth.custom_validators.pre_auth.append(authval2) + + def test_custom_token_validators(self): + tknval1, tknval2 = mock.Mock(), mock.Mock() + self.auth.custom_validators.pre_token.append(tknval1) + self.auth.custom_validators.post_token.append(tknval2) + + bearer = BearerToken(self.mock_validator) + self.auth.create_token_response(self.request, bearer) + self.assertTrue(tknval1.called) + self.assertTrue(tknval2.called) + def test_create_token_response(self): bearer = BearerToken(self.mock_validator) headers, body, status_code = self.auth.create_token_response( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_implicit.py new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_implicit.py --- old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_implicit.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_implicit.py 2017-03-19 13:50:31.000000000 +0100 @@ -40,5 +40,24 @@ h, b, s = self.auth.create_token_response(self.request, bearer) self.assertURLEqual(h['Location'], correct_uri) + def test_custom_validators(self): + self.authval1, self.authval2 = mock.Mock(), mock.Mock() + self.tknval1, self.tknval2 = mock.Mock(), mock.Mock() + for val in (self.authval1, self.authval2): + val.return_value = {} + for val in (self.tknval1, self.tknval2): + val.return_value = None + self.auth.custom_validators.pre_token.append(self.tknval1) + self.auth.custom_validators.post_token.append(self.tknval2) + self.auth.custom_validators.pre_auth.append(self.authval1) + self.auth.custom_validators.post_auth.append(self.authval2) + + bearer = BearerToken(self.mock_validator) + self.auth.create_token_response(self.request, bearer) + self.assertTrue(self.tknval1.called) + self.assertTrue(self.tknval2.called) + self.assertTrue(self.authval1.called) + self.assertTrue(self.authval2.called) + def test_error_response(self): pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_refresh_token.py new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_refresh_token.py --- old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_refresh_token.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_refresh_token.py 2017-03-19 13:50:31.000000000 +0100 @@ -36,6 +36,31 @@ self.assertIn('expires_in', token) self.assertEqual(token['scope'], 'foo') + def test_custom_auth_validators_unsupported(self): + authval1, authval2 = mock.Mock(), mock.Mock() + expected = ('RefreshTokenGrant does not support authorization ' + 'validators. Use token validators instead.') + with self.assertRaises(ValueError) as caught: + RefreshTokenGrant(self.mock_validator, pre_auth=[authval1]) + self.assertEqual(caught.exception.args[0], expected) + with self.assertRaises(ValueError) as caught: + RefreshTokenGrant(self.mock_validator, post_auth=[authval2]) + self.assertEqual(caught.exception.args[0], expected) + with self.assertRaises(AttributeError): + self.auth.custom_validators.pre_auth.append(authval1) + with self.assertRaises(AttributeError): + self.auth.custom_validators.pre_auth.append(authval2) + + def test_custom_token_validators(self): + tknval1, tknval2 = mock.Mock(), mock.Mock() + self.auth.custom_validators.pre_token.append(tknval1) + self.auth.custom_validators.post_token.append(tknval2) + + bearer = BearerToken(self.mock_validator) + self.auth.create_token_response(self.request, bearer) + self.assertTrue(tknval1.called) + self.assertTrue(tknval2.called) + def test_create_token_inherit_scope(self): self.request.scope = None self.mock_validator.get_original_scopes.return_value = ['foo', 'bar'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py --- old/oauthlib-2.0.1/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py 2016-11-23 11:30:34.000000000 +0100 +++ new/oauthlib-2.0.2/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py 2017-03-19 13:50:31.000000000 +0100 @@ -89,6 +89,34 @@ self.assertEqual(status_code, 401) self.assertEqual(self.mock_validator.save_token.call_count, 0) + def test_custom_auth_validators_unsupported(self): + authval1, authval2 = mock.Mock(), mock.Mock() + expected = ('ResourceOwnerPasswordCredentialsGrant does not ' + 'support authorization validators. Use token ' + 'validators instead.') + with self.assertRaises(ValueError) as caught: + ResourceOwnerPasswordCredentialsGrant(self.mock_validator, + pre_auth=[authval1]) + self.assertEqual(caught.exception.args[0], expected) + with self.assertRaises(ValueError) as caught: + ResourceOwnerPasswordCredentialsGrant(self.mock_validator, + post_auth=[authval2]) + self.assertEqual(caught.exception.args[0], expected) + with self.assertRaises(AttributeError): + self.auth.custom_validators.pre_auth.append(authval1) + with self.assertRaises(AttributeError): + self.auth.custom_validators.pre_auth.append(authval2) + + def test_custom_token_validators(self): + tknval1, tknval2 = mock.Mock(), mock.Mock() + self.auth.custom_validators.pre_token.append(tknval1) + self.auth.custom_validators.post_token.append(tknval2) + + bearer = BearerToken(self.mock_validator) + self.auth.create_token_response(self.request, bearer) + self.assertTrue(tknval1.called) + self.assertTrue(tknval2.called) + def test_error_response(self): pass
