From 5482dc97984dc9a314a4f06044599c7da23a344f Mon Sep 17 00:00:00 2001 From: John Dennis Date: Aug 03 2018 21:35:04 +0000 Subject: upgrade to latest upstream 2.1.0 --- diff --git a/.gitignore b/.gitignore index dbee9c9..83bb5b7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /oauthlib-514cad74bde4cd7781b496155058a79507245798.tar.gz /oauthlib-1.0.3.tar.gz /oauthlib-2.0.1.tar.gz +/v2.1.0.tar.gz diff --git a/jwcrypto.patch b/jwcrypto.patch deleted file mode 100644 index 48d70e3..0000000 --- a/jwcrypto.patch +++ /dev/null @@ -1,307 +0,0 @@ -commit 388ddb783c7bff39feea87dd7e2ac0bfd410d82a -Author: John Dennis -Date: Thu Mar 16 10:18:57 2017 -0400 - - Use jwcrypto instead of jwt - - * Replace all imports of jwt with jwcrypto, continue to rely on cryptography - * Update doc replacing jwt with jwcrypto - * Update dependencies and requirements - * Add utility functions get_rsa_public_key() and get_rsa_private_key() - to obtain cryptography's RSAPublicKey and RSAPrivateKey - * Add utility normalize_claims() replicate type conversion jwt offered, - primarially datatime conversion - * Refactor to use common code and separate concerns - * Replace jwt's RSA-SHA1 signature with primitives from cryptography - - Signed-off-by: John Dennis - -diff --git a/oauthlib/common.py b/oauthlib/common.py -index 5d999b2..5fc0a3c 100644 ---- a/oauthlib/common.py -+++ b/oauthlib/common.py -@@ -15,6 +15,16 @@ import random - import re - import sys - import time -+from calendar import timegm -+ -+from jwcrypto.jwk import JWK -+from jwcrypto.jwt import JWT -+from jwcrypto.common import json_decode -+from cryptography.hazmat.backends import default_backend -+from cryptography.hazmat.primitives.serialization import ( -+ load_pem_private_key, load_pem_public_key) -+from cryptography.hazmat.primitives.asymmetric.rsa import ( -+ RSAPrivateKey, RSAPublicKey) - - try: - from urllib import quote as _quote -@@ -229,28 +239,69 @@ def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET): - return ''.join(rand.choice(chars) for x in range(length)) - - --def generate_signed_token(private_pem, request): -- import jwt -+def get_rsa_private_key(data): -+ if isinstance(data, (bytes_type, unicode_type)): -+ if isinstance(data, unicode_type): -+ data = data.encode('ascii') -+ # load_pem_private_key() requires the data to be str or bytes -+ private_key = load_pem_private_key(data, None, default_backend()) -+ else: -+ private_key = data -+ -+ if not isinstance(private_key, RSAPrivateKey): -+ raise TypeError("Expected RSAPrivateKey, but got %s" % -+ private_key.__class__.__name__) -+ -+ return private_key -+ -+ -+def get_rsa_public_key(data): -+ if isinstance(data, (bytes_type, unicode_type)): -+ if isinstance(data, unicode_type): -+ data = data.encode('ascii') -+ # load_pem_public_key() requires the data to be str or bytes -+ public_key = load_pem_public_key(data, default_backend()) -+ else: -+ public_key = data -+ -+ if not isinstance(public_key, RSAPublicKey): -+ raise TypeError("Expected RSAPublicKey, but got %s" % -+ public_key.__class__.__name__) -+ -+ return public_key - -- now = datetime.datetime.utcnow() - -+def normalize_claims(claims): -+ for claim in ['exp', 'iat', 'nbf']: -+ # Convert datetime to a intDate value in known time-format claims -+ if isinstance(claims.get(claim), datetime.datetime): -+ claims[claim] = timegm(claims[claim].utctimetuple()) -+ return claims -+ -+ -+def generate_jwt_assertion(private_key, claims): -+ rsa_private_key = get_rsa_private_key(private_key) -+ jwkey = JWK.from_pyca(rsa_private_key) -+ token = JWT(header={'alg': 'RS256'}, claims=normalize_claims(claims)) -+ token.make_signed_token(jwkey) -+ return to_unicode(token.serialize(), "UTF-8") -+ -+ -+def generate_signed_token(private_pem, request): -+ now = datetime.datetime.utcnow() - claims = { - 'scope': request.scope, - 'exp': now + datetime.timedelta(seconds=request.expires_in) - } -- - claims.update(request.claims) -+ return generate_jwt_assertion(private_pem, claims) - -- token = jwt.encode(claims, private_pem, 'RS256') -- token = to_unicode(token, "UTF-8") -- -- return token -- -- --def verify_signed_token(public_pem, token): -- import jwt - -- return jwt.decode(token, public_pem, algorithms=['RS256']) -+def verify_signed_token(public_key, token): -+ rsa_public_key = get_rsa_public_key(public_key) -+ jwkey = JWK.from_pyca(rsa_public_key) -+ signed_token = JWT(key=jwkey, jwt=token) -+ return json_decode(signed_token.claims) - - - def generate_client_id(length=30, chars=CLIENT_ID_CHARACTER_SET): -diff --git a/oauthlib/oauth1/rfc5849/signature.py b/oauthlib/oauth1/rfc5849/signature.py -index 8fa22ba..6956732 100644 ---- a/oauthlib/oauth1/rfc5849/signature.py -+++ b/oauthlib/oauth1/rfc5849/signature.py -@@ -31,8 +31,15 @@ try: - except ImportError: - import urllib.parse as urlparse - from . import utils --from oauthlib.common import urldecode, extract_params, safe_string_equals --from oauthlib.common import bytes_type, unicode_type -+ -+from cryptography.hazmat.primitives import hashes -+from cryptography.hazmat.primitives.asymmetric import padding -+from jwcrypto.jwk import JWK -+from jwcrypto.jws import JWS -+ -+from oauthlib.common import (urldecode, extract_params, safe_string_equals, -+ bytes_type, unicode_type, -+ get_rsa_private_key, get_rsa_public_key) - - - def construct_base_string(http_method, base_string_uri, -@@ -464,17 +471,8 @@ def sign_hmac_sha1(base_string, client_secret, resource_owner_secret): - # .. _`RFC2045, Section 6.8`: http://tools.ietf.org/html/rfc2045#section-6.8 - return binascii.b2a_base64(signature.digest())[:-1].decode('utf-8') - --_jwtrs1 = None -- --#jwt has some nice pycrypto/cryptography abstractions --def _jwt_rs1_signing_algorithm(): -- global _jwtrs1 -- if _jwtrs1 is None: -- import jwt.algorithms as jwtalgo -- _jwtrs1 = jwtalgo.RSAAlgorithm(jwtalgo.hashes.SHA1) -- return _jwtrs1 - --def sign_rsa_sha1(base_string, rsa_private_key): -+def sign_rsa_sha1(base_string, private_key): - """**RSA-SHA1** - - Per `section 3.4.3`_ of the spec. -@@ -493,10 +491,11 @@ def sign_rsa_sha1(base_string, rsa_private_key): - if isinstance(base_string, unicode_type): - base_string = base_string.encode('utf-8') - # TODO: finish RSA documentation -- alg = _jwt_rs1_signing_algorithm() -- key = _prepare_key_plus(alg, rsa_private_key) -- s=alg.sign(base_string, key) -- return binascii.b2a_base64(s)[:-1].decode('utf-8') -+ key = get_rsa_private_key(private_key) -+ signer = key.signer(padding.PKCS1v15(), hashes.SHA1()) -+ signer.update(base_string) -+ signature = signer.finalize() -+ return binascii.b2a_base64(signature)[:-1].decode('utf-8') - - - def sign_rsa_sha1_with_client(base_string, client): -@@ -568,17 +567,13 @@ def verify_hmac_sha1(request, client_secret=None, - resource_owner_secret) - return safe_string_equals(signature, request.signature) - --def _prepare_key_plus(alg, keystr): -- if isinstance(keystr, bytes_type): -- keystr = keystr.decode('utf-8') -- return alg.prepare_key(keystr) - - def verify_rsa_sha1(request, rsa_public_key): - """Verify a RSASSA-PKCS #1 v1.5 base64 encoded signature. - - Per `section 3.4.3`_ of the spec. - -- Note this method requires the jwt and cryptography libraries. -+ Note this method requires the cryptography library. - - .. _`section 3.4.3`: http://tools.ietf.org/html/rfc5849#section-3.4.3 - -@@ -595,9 +590,14 @@ def verify_rsa_sha1(request, rsa_public_key): - message = construct_base_string(request.http_method, uri, norm_params).encode('utf-8') - sig = binascii.a2b_base64(request.signature.encode('utf-8')) - -- alg = _jwt_rs1_signing_algorithm() -- key = _prepare_key_plus(alg, rsa_public_key) -- return alg.verify(message, key, sig) -+ key = get_rsa_public_key(rsa_public_key) -+ verifier = key.verifier(sig, padding.PKCS1v15(), hashes.SHA1()) -+ verifier.update(message) -+ try: -+ verifier.verify() -+ return True -+ except InvalidSignature: -+ return False - - - def verify_plaintext(request, client_secret=None, resource_owner_secret=None): -diff --git a/oauthlib/oauth2/rfc6749/clients/service_application.py b/oauthlib/oauth2/rfc6749/clients/service_application.py -index 36da98b..d974b1d 100644 ---- a/oauthlib/oauth2/rfc6749/clients/service_application.py -+++ b/oauthlib/oauth2/rfc6749/clients/service_application.py -@@ -10,7 +10,7 @@ from __future__ import absolute_import, unicode_literals - - import time - --from oauthlib.common import to_unicode -+from oauthlib.common import generate_jwt_assertion - - from .base import Client - from ..parameters import prepare_token_request -@@ -139,7 +139,6 @@ class ServiceApplicationClient(Client): - - .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1 - """ -- import jwt - - key = private_key or self.private_key - if not key: -@@ -166,8 +165,7 @@ class ServiceApplicationClient(Client): - - claim.update(extra_claims or {}) - -- assertion = jwt.encode(claim, key, 'RS256') -- assertion = to_unicode(assertion) -+ assertion = generate_jwt_assertion(key, claim) - - return prepare_token_request(self.grant_type, - body=body, -diff --git a/setup.py b/setup.py -index 718db43..77cd6c8 100755 ---- a/setup.py -+++ b/setup.py -@@ -18,11 +18,11 @@ def fread(fn): - return f.read() - - if sys.version_info[0] == 3: -- tests_require = ['nose', 'cryptography', 'pyjwt>=1.0.0', 'blinker'] -+ tests_require = ['nose', 'cryptography', 'jwcrypto>=0.3.2', 'blinker'] - else: -- tests_require = ['nose', 'unittest2', 'cryptography', 'mock', 'pyjwt>=1.0.0', 'blinker'] -+ tests_require = ['nose', 'unittest2', 'cryptography', 'mock', 'jwcrypto>=0.3.2', 'blinker'] - rsa_require = ['cryptography'] --signedtoken_require = ['cryptography', 'pyjwt>=1.0.0'] -+signedtoken_require = ['cryptography', 'jwcrypto>=0.3.2'] - signals_require = ['blinker'] - - requires = [] -diff --git a/tests/oauth2/rfc6749/clients/test_service_application.py b/tests/oauth2/rfc6749/clients/test_service_application.py -index de57291..842bbcb 100644 ---- a/tests/oauth2/rfc6749/clients/test_service_application.py -+++ b/tests/oauth2/rfc6749/clients/test_service_application.py -@@ -4,10 +4,9 @@ from __future__ import absolute_import, unicode_literals - import os - from time import time - --import jwt - from mock import patch - --from oauthlib.common import Request -+from oauthlib.common import Request, verify_signed_token - from oauthlib.oauth2 import ServiceApplicationClient - - from ....unittest import TestCase -@@ -92,10 +91,10 @@ mfvGGg3xNjTMO7IdrwIDAQAB - self.assertEqual(r.isnot, 'empty') - self.assertEqual(r.grant_type, ServiceApplicationClient.grant_type) - -- claim = jwt.decode(r.assertion, self.public_key, audience=self.audience, algorithms=['RS256']) -+ claim = verify_signed_token(self.public_key, r.assertion) - - self.assertEqual(claim['iss'], self.issuer) -- # audience verification is handled during decode now -+ self.assertEqual(claim['aud'], self.audience) - self.assertEqual(claim['sub'], self.subject) - self.assertEqual(claim['iat'], int(t.return_value)) - -diff --git a/tests/oauth2/rfc6749/test_server.py b/tests/oauth2/rfc6749/test_server.py -index aff0d84..850d73e 100644 ---- a/tests/oauth2/rfc6749/test_server.py -+++ b/tests/oauth2/rfc6749/test_server.py -@@ -2,7 +2,6 @@ - from __future__ import absolute_import, unicode_literals - from ...unittest import TestCase - import json --import jwt - import mock - - from oauthlib import common diff --git a/python-oauthlib.spec b/python-oauthlib.spec index 90e3296..395c699 100644 --- a/python-oauthlib.spec +++ b/python-oauthlib.spec @@ -1,5 +1,3 @@ -%bcond_with jwcrypto - %if (0%{?fedora} > 0 && 0%{?fedora} < 32) || (0%{?rhel} > 0 && 0%{?rhel} <= 7) %bcond_without python2 %bcond_without python3 @@ -18,32 +16,20 @@ %{!?py2_install: %global py2_install %{__python2} setup.py install --skip-build --root %{buildroot} %{?*}} %endif -# commit corresponds to v2.0.1 tag -%global commit 3eb6fe934c8c8d6c34e22b4e4fc1bd01d0266df6 -%global shortcommit %(c=%{commit}; echo ${c:0:7}) -%global checkout 20150520git%{shortcommit} - -%global gh_owner idan -%global gh_project oauthlib - %global modname oauthlib Name: python-oauthlib -Version: 2.0.1 -Release: 11%{?dist} +Version: 2.1.0 +Release: 1%{?dist} Summary: An implementation of the OAuth request-signing logic Group: Development/Libraries License: BSD -URL: https://pypi.python.org/pypi/oauthlib -Source0: https://files.pythonhosted.org/packages/source/o/%{modname}/%{modname}-%{version}.tar.gz -#Source0: https://github.com/%%{gh_owner}/%%{gh_project}/archive/%%{commit}/%%{gh_project}-%%{commit}.tar.gz +URL: https://github.com/oauthlib/oauthlib +Source0: https://github.com/oauthlib/oauthlib/archive/v%{version}.tar.gz BuildArch: noarch -Patch0: jwcrypto.patch - - %description OAuthLib is a generic utility which implements the logic of OAuth without assuming a specific HTTP request object or web framework. Use it to graft @@ -65,18 +51,10 @@ BuildRequires: python2-nose BuildRequires: python2-mock BuildRequires: python2-blinker -%if %{with jwcrypto} -BuildRequires: python2-jwcrypto -%else BuildRequires: python2-jwt -%endif BuildRequires: python2-cryptography -%if %{with jwcrypto} -Requires: python2-jwcrypto -%else Requires: python2-jwt -%endif Requires: python2-cryptography >= 0.8.1 %description -n python2-oauthlib @@ -86,6 +64,7 @@ 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. + %endif # with python2 %if %{with python3} @@ -101,18 +80,10 @@ BuildRequires: python3-nose BuildRequires: python3-mock BuildRequires: python3-blinker -%if %{with jwcrypto} -BuildRequires: python3-jwcrypto -%else BuildRequires: python3-jwt -%endif BuildRequires: python3-cryptography -%if %{with jwcrypto} -Requires: python3-jwcrypto -%else Requires: python3-jwt -%endif Requires: python3-cryptography >= 0.8.1 %description -n python3-oauthlib @@ -122,15 +93,11 @@ 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. + %endif # with python3 %prep %setup -q -n %{modname}-%{version} -#%%setup -q -n %%{gh_project}-%%{commit} - -%if %{with jwcrypto} -%patch0 -p1 -%endif # python-unittest2 is now provided by "python" package and python-unittest is retired # adapt setup.py to reflect this fact downstream @@ -180,6 +147,9 @@ rm -rf %{modname}.egg-info %endif # with python3 %changelog +* Fri Aug 3 2018 - 2.1.0-1 +- upgrade to latest upstream 2.1.0 + * Sat Jul 14 2018 Fedora Release Engineering - 2.0.1-11 - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild @@ -208,7 +178,7 @@ rm -rf %{modname}.egg-info * Mon Apr 17 2017 John Dennis - 2.0.1-3 - fix dependency on python2-jwt, should be python-jwt -* Fri Apr 13 2017 Dennis Gilmore - 2.0.1-2 +* Thu Apr 13 2017 Dennis Gilmore - 2.0.1-2 - add spaces around the >= for Requires * Thu Mar 16 2017 John Dennis - 2.0.1-1 diff --git a/sources b/sources index 4c8ca55..6f75dd5 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (oauthlib-2.0.1.tar.gz) = 5d19839d1fec6939d10a26b5a0cf96024783fbf67b91299ad7ad640c5c75170918fcda9ed0e13333a5dde353b86201f033e410b8312f1d9eaaf9af0b289c348f +SHA512 (v2.1.0.tar.gz) = 7935933d4c5be1eacf8688d6f0243dac8a43de35d19179839f72ee9fe9b02d4897d62a310475e741f0ed9a1c7e407bd846d51eb9884d8dd48b4624783cef0284