From 0f663f7df080ddb5fe23a72e448fec8d14f38b6d7f738b2fd96be3dd13fe0563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Thu, 23 Apr 2015 06:30:49 +0000 Subject: [PATCH] Accepting request 298709 from home:Nijel:branches:devel:languages:python - Update to 0.7.2: * (Quick fix) Unpushed locally modified files got included in the PyPI 0.7.1 release. Doing a new clean release to address this. Please upgrade quickly and report any issues you are running into. * (Quick fix) Add oauthlib.common.log object back in for libraries using it. * (Change) OAuth2 clients will not raise a Warning on scope change if the environment variable ``OAUTHLIB_RELAX_TOKEN_SCOPE`` is set. The token will now be available as an attribute on the error, ``error.token``. Token changes will now also be announced using blinker. * (Fix/Feature) Automatic fixes of non-compliant OAuth2 provider responses (e.g. Facebook). * (Fix) Logging is now tiered (per file) as opposed to logging all under ``oauthlib``. * (Fix) Error messages should now include a description in their message. * (Fix/Feature) Optional support for jsonp callbacks after token revocation. * (Feature) Client side preparation of OAuth 2 token revocation requests. * (Feature) New OAuth2 client API methods for preparing full requests. * (Feature) OAuth1 SignatureOnlyEndpoint that only verifies signatures and client IDs. * (Fix/Feature) Refresh token grant now allow optional refresh tokens. * (Fix) add missing state param to OAuth2 errors. * (Fix) add_params_to_uri now properly parse fragment. * (Fix/Feature) All OAuth1 errors can now be imported from oauthlib.oauth1. * (Fix/Security) OAuth2 logs will now strip client provided password, if present. * Allow unescaped @ in urlencoded parameters. - New dependency on python-blinker - Add pycrypto.patch to be compatible with latest PyJWT OBS-URL: https://build.opensuse.org/request/show/298709 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-oauthlib?expand=0&rev=17 --- oauthlib-0.6.3.tar.gz | 3 - oauthlib-0.7.2.tar.gz | 3 + pycrypto.patch | 449 ++++++++++++++++++++++++++++++++++++++++ python-oauthlib.changes | 28 +++ python-oauthlib.spec | 13 +- 5 files changed, 490 insertions(+), 6 deletions(-) delete mode 100644 oauthlib-0.6.3.tar.gz create mode 100644 oauthlib-0.7.2.tar.gz create mode 100644 pycrypto.patch diff --git a/oauthlib-0.6.3.tar.gz b/oauthlib-0.6.3.tar.gz deleted file mode 100644 index c45fef4..0000000 --- a/oauthlib-0.6.3.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0a11ab2311f7a29d0079fe529bde9503fec9a2bdd52db834cf8a284a8c1c52f9 -size 100950 diff --git a/oauthlib-0.7.2.tar.gz b/oauthlib-0.7.2.tar.gz new file mode 100644 index 0000000..0545781 --- /dev/null +++ b/oauthlib-0.7.2.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a051f04ee8ec3305055ab34d87b36c9a449375e07c7d6a05bcafa48329cac7c3 +size 106079 diff --git a/pycrypto.patch b/pycrypto.patch new file mode 100644 index 0000000..3b225ac --- /dev/null +++ b/pycrypto.patch @@ -0,0 +1,449 @@ +From fc2d1218e015adcf65ba01965884430c47895cd0 Mon Sep 17 00:00:00 2001 +From: Joseph Tate +Date: Tue, 14 Apr 2015 12:30:44 -0400 +Subject: [PATCH] Support newer PyJWT (1.0.0). remove PyCrypto completely for + cryptography and PyJWT helpers. Reformat some test certificates to be easier + to maintain. Update documentation to match use of cryptography instead of + PyCrypto + +--- + CHANGELOG.rst | 6 ++ + docs/faq.rst | 13 ++-- + docs/feature_matrix.rst | 4 +- + docs/oauth1/client.rst | 31 ++++----- + oauthlib/common.py | 16 +---- + oauthlib/oauth1/rfc5849/signature.py | 41 +++++++----- + .../oauth2/rfc6749/clients/service_application.py | 3 - + oauthlib/oauth2/rfc6749/utils.py | 2 +- + requirements.txt | 6 +- + setup.py | 8 +-- + tests/oauth1/rfc5849/test_signatures.py | 44 ++++++------- + .../rfc6749/clients/test_service_application.py | 51 ++++++++------- + tests/oauth2/rfc6749/test_server.py | 73 +++++++++++++--------- + tests/oauth2/rfc6749/test_utils.py | 3 + + 14 files changed, 153 insertions(+), 148 deletions(-) + +diff --git a/oauthlib/common.py b/oauthlib/common.py +index 9cd7a61..0179b8e 100644 +--- a/oauthlib/common.py ++++ b/oauthlib/common.py +@@ -229,11 +229,8 @@ def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET): + + + def generate_signed_token(private_pem, request): +- import Crypto.PublicKey.RSA as RSA + import jwt + +- private_key = RSA.importKey(private_pem) +- + now = datetime.datetime.utcnow() + + claims = { +@@ -243,23 +240,16 @@ def generate_signed_token(private_pem, request): + + claims.update(request.claims) + +- token = jwt.encode(claims, private_key, 'RS256') ++ token = jwt.encode(claims, private_pem, 'RS256') + token = to_unicode(token, "UTF-8") + + return token + + +-def verify_signed_token(private_pem, token): +- import Crypto.PublicKey.RSA as RSA ++def verify_signed_token(public_pem, token): + import jwt + +- public_key = RSA.importKey(private_pem).publickey() +- +- try: +- # return jwt.verify_jwt(token.encode(), public_key) +- return jwt.decode(token, public_key) +- except: +- raise Exception ++ return jwt.decode(token, public_pem, algorithms=['RS256']) + + + 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 f2d7ee4..f57d80a 100644 +--- a/oauthlib/oauth1/rfc5849/signature.py ++++ b/oauthlib/oauth1/rfc5849/signature.py +@@ -464,6 +464,15 @@ 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): + """**RSA-SHA1** +@@ -481,16 +490,13 @@ def sign_rsa_sha1(base_string, rsa_private_key): + .. _`RFC3447, Section 8.2`: http://tools.ietf.org/html/rfc3447#section-8.2 + + """ +- # TODO: finish RSA documentation +- from Crypto.PublicKey import RSA +- from Crypto.Signature import PKCS1_v1_5 +- from Crypto.Hash import SHA +- key = RSA.importKey(rsa_private_key) + if isinstance(base_string, unicode_type): + base_string = base_string.encode('utf-8') +- h = SHA.new(base_string) +- p = PKCS1_v1_5.new(key) +- return binascii.b2a_base64(p.sign(h))[:-1].decode('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') + + + def sign_rsa_sha1_with_client(base_string, client): +@@ -560,13 +566,17 @@ 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 PyCrypto library. ++ Note this method requires the jwt and cryptography libraries. + + .. _`section 3.4.3`: http://tools.ietf.org/html/rfc5849#section-3.4.3 + +@@ -578,17 +588,14 @@ def verify_rsa_sha1(request, rsa_public_key): + + .. _`RFC2616 section 5.2`: http://tools.ietf.org/html/rfc2616#section-5.2 + """ +- from Crypto.PublicKey import RSA +- from Crypto.Signature import PKCS1_v1_5 +- from Crypto.Hash import SHA +- key = RSA.importKey(rsa_public_key) + norm_params = normalize_parameters(request.params) + uri = normalize_base_string_uri(request.uri) +- message = construct_base_string(request.http_method, uri, norm_params) +- h = SHA.new(message.encode('utf-8')) +- p = PKCS1_v1_5.new(key) ++ message = construct_base_string(request.http_method, uri, norm_params).encode('utf-8') + sig = binascii.a2b_base64(request.signature.encode('utf-8')) +- return p.verify(h, sig) ++ ++ alg = _jwt_rs1_signing_algorithm() ++ key = _prepare_key_plus(alg, rsa_public_key) ++ return alg.verify(message, key, sig) + + + 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 f60ac4f..36da98b 100644 +--- a/oauthlib/oauth2/rfc6749/clients/service_application.py ++++ b/oauthlib/oauth2/rfc6749/clients/service_application.py +@@ -140,14 +140,11 @@ def prepare_request_body(self, + .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1 + """ + import jwt +- import Crypto.PublicKey.RSA as RSA + + key = private_key or self.private_key + if not key: + raise ValueError('An encryption key must be supplied to make JWT' + ' token requests.') +- key = RSA.importKey(key) +- + claim = { + 'iss': issuer or self.issuer, + 'aud': audience or self.issuer, +diff --git a/oauthlib/oauth2/rfc6749/utils.py b/oauthlib/oauth2/rfc6749/utils.py +index e8ffba4..6a8e24b 100644 +--- a/oauthlib/oauth2/rfc6749/utils.py ++++ b/oauthlib/oauth2/rfc6749/utils.py +@@ -24,7 +24,7 @@ def list_to_scope(scope): + """Convert a list of scopes to a space separated string.""" + if isinstance(scope, unicode_type) or scope is None: + return scope +- elif isinstance(scope, list): ++ elif isinstance(scope, (tuple, list)): + return " ".join([unicode_type(s) for s in scope]) + elif isinstance(scope, set): + return list_to_scope(list(scope)) +diff --git a/setup.py b/setup.py +index da76450..e2870b2 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', 'pycrypto', 'pyjwt', 'blinker'] ++ tests_require = ['nose', 'cryptography', 'pyjwt>=1.0.0', 'blinker'] + else: +- tests_require = ['nose', 'unittest2', 'pycrypto', 'mock', 'pyjwt', 'blinker'] +-rsa_require = ['pycrypto'] +-signedtoken_require = ['pycrypto', 'pyjwt'] ++ tests_require = ['nose', 'unittest2', 'cryptography', 'mock', 'pyjwt>=1.0.0', 'blinker'] ++rsa_require = ['cryptography'] ++signedtoken_require = ['cryptography', 'pyjwt>=1.0.0'] + signals_require = ['blinker'] + + requires = [] +diff --git a/tests/oauth1/rfc5849/test_signatures.py b/tests/oauth1/rfc5849/test_signatures.py +index aca6142..51e01ef 100644 +--- a/tests/oauth1/rfc5849/test_signatures.py ++++ b/tests/oauth1/rfc5849/test_signatures.py +@@ -269,31 +269,25 @@ def test_sign_hmac_sha1_with_client(self): + b"th_nonce%253D%25227d8f3e4a%2522%252Coauth_signature" + b"%253D%2522bYT5CMsGcbgUdFHObYMEfcx6bsw%25253D%2522") + +- @property +- def rsa_private_key(self): +- # Generated using: $ openssl genrsa -out .pem 1024 +- # PyCrypto / python-rsa requires the key to be concatenated with +- # linebreaks. +- return ( +- 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-----" +- ) +- ++ # Generated using: $ openssl genrsa -out .pem 1024 ++ # PEM encoding requires the key to be concatenated with ++ # linebreaks. ++ rsa_private_key = b"""-----BEGIN RSA PRIVATE KEY----- ++MIICXgIBAAKBgQDk1/bxyS8Q8jiheHeYYp/4rEKJopeQRRKKpZI4s5i+UPwVpupG ++AlwXWfzXwSMaKPAoKJNdu7tqKRniqst5uoHXw98gj0x7zamu0Ck1LtQ4c7pFMVah ++5IYGhBi2E9ycNS329W27nJPWNCbESTu7snVlG8V8mfvGGg3xNjTMO7IdrwIDAQAB ++AoGBAOQ2KuH8S5+OrsL4K+wfjoCi6MfxCUyqVU9GxocdM1m30WyWRFMEz2nKJ8fR ++p3vTD4w8yplTOhcoXdQZl0kRoaDzrcYkm2VvJtQRrX7dKFT8dR8D/Tr7dNQLOXfC ++DY6xveQczE7qt7Vk7lp4FqmxBsaaEuokt78pOOjywZoInjZhAkEA9wz3zoZNT0/i ++rf6qv2qTIeieUB035N3dyw6f1BGSWYaXSuerDCD/J1qZbAPKKhyHZbVawFt3UMhe ++542UftBaxQJBAO0iJy1I8GQjGnS7B3yvyH3CcLYGy296+XO/2xKp/d/ty1OIeovx ++C60pLNwuFNF3z9d2GVQAdoQ89hUkOtjZLeMCQQD0JO6oPHUeUjYT+T7ImAv7UKVT ++Suy30sKjLzqoGw1kR+wv7C5PeDRvscs4wa4CW9s6mjSrMDkDrmCLuJDtmf55AkEA ++kmaMg2PNrjUR51F0zOEFycaaqXbGcFwe1/xx9zLmHzMDXd4bsnwt9kk+fe0hQzVS ++JzatanQit3+feev1PN3QewJAWv4RZeavEUhKv+kLe95Yd0su7lTLVduVgh4v5yLT ++Ga6FHdjGPcfajt+nrpB1n8UQBEH9ZxniokR/IPvdMlxqXA== ++-----END RSA PRIVATE KEY----- ++""" + @property + def control_signature_rsa_sha1(self): + # Base string saved in "". Signature obtained using: +diff --git a/tests/oauth2/rfc6749/clients/test_service_application.py b/tests/oauth2/rfc6749/clients/test_service_application.py +index a14750e..de57291 100644 +--- a/tests/oauth2/rfc6749/clients/test_service_application.py ++++ b/tests/oauth2/rfc6749/clients/test_service_application.py +@@ -5,7 +5,6 @@ + from time import time + + import jwt +-from Crypto.PublicKey import RSA + from mock import patch + + from oauthlib.common import Request +@@ -18,25 +17,32 @@ class ServiceApplicationClientTest(TestCase): + + gt = ServiceApplicationClient.grant_type + +- 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-----" +- ) ++ private_key = """ ++-----BEGIN RSA PRIVATE KEY----- ++MIICXgIBAAKBgQDk1/bxyS8Q8jiheHeYYp/4rEKJopeQRRKKpZI4s5i+UPwVpupG ++AlwXWfzXwSMaKPAoKJNdu7tqKRniqst5uoHXw98gj0x7zamu0Ck1LtQ4c7pFMVah ++5IYGhBi2E9ycNS329W27nJPWNCbESTu7snVlG8V8mfvGGg3xNjTMO7IdrwIDAQAB ++AoGBAOQ2KuH8S5+OrsL4K+wfjoCi6MfxCUyqVU9GxocdM1m30WyWRFMEz2nKJ8fR ++p3vTD4w8yplTOhcoXdQZl0kRoaDzrcYkm2VvJtQRrX7dKFT8dR8D/Tr7dNQLOXfC ++DY6xveQczE7qt7Vk7lp4FqmxBsaaEuokt78pOOjywZoInjZhAkEA9wz3zoZNT0/i ++rf6qv2qTIeieUB035N3dyw6f1BGSWYaXSuerDCD/J1qZbAPKKhyHZbVawFt3UMhe ++542UftBaxQJBAO0iJy1I8GQjGnS7B3yvyH3CcLYGy296+XO/2xKp/d/ty1OIeovx ++C60pLNwuFNF3z9d2GVQAdoQ89hUkOtjZLeMCQQD0JO6oPHUeUjYT+T7ImAv7UKVT ++Suy30sKjLzqoGw1kR+wv7C5PeDRvscs4wa4CW9s6mjSrMDkDrmCLuJDtmf55AkEA ++kmaMg2PNrjUR51F0zOEFycaaqXbGcFwe1/xx9zLmHzMDXd4bsnwt9kk+fe0hQzVS ++JzatanQit3+feev1PN3QewJAWv4RZeavEUhKv+kLe95Yd0su7lTLVduVgh4v5yLT ++Ga6FHdjGPcfajt+nrpB1n8UQBEH9ZxniokR/IPvdMlxqXA== ++-----END RSA PRIVATE KEY----- ++""" ++ ++ public_key = """ ++-----BEGIN PUBLIC KEY----- ++MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDk1/bxyS8Q8jiheHeYYp/4rEKJ ++opeQRRKKpZI4s5i+UPwVpupGAlwXWfzXwSMaKPAoKJNdu7tqKRniqst5uoHXw98g ++j0x7zamu0Ck1LtQ4c7pFMVah5IYGhBi2E9ycNS329W27nJPWNCbESTu7snVlG8V8 ++mfvGGg3xNjTMO7IdrwIDAQAB ++-----END PUBLIC KEY----- ++""" + + subject = 'resource-owner@provider.com' + +@@ -86,11 +92,10 @@ def test_request_body(self, t): + self.assertEqual(r.isnot, 'empty') + self.assertEqual(r.grant_type, ServiceApplicationClient.grant_type) + +- key = RSA.importKey(self.private_key).publickey() +- claim = jwt.decode(r.assertion, key) ++ claim = jwt.decode(r.assertion, self.public_key, audience=self.audience, algorithms=['RS256']) + + self.assertEqual(claim['iss'], self.issuer) +- self.assertEqual(claim['aud'], self.audience) ++ # audience verification is handled during decode now + 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 24c9fb9..fde785e 100644 +--- a/tests/oauth2/rfc6749/test_server.py ++++ b/tests/oauth2/rfc6749/test_server.py +@@ -1,7 +1,6 @@ + # -*- coding: utf-8 -*- + from __future__ import absolute_import, unicode_literals + from ...unittest import TestCase +-import Crypto.PublicKey.RSA as RSA + import json + import jwt + import mock +@@ -177,35 +176,47 @@ def set_user(request): + self.mock_validator.authenticate_client.side_effect = set_user + self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock()) + +- self.private_pem = ( +- "-----BEGIN RSA PRIVATE KEY-----\n" +- "MIIEpAIBAAKCAQEA6TtDhWGwzEOWZP6m/zHoZnAPLABfetvoMPmxPGjFjtDuMRPv\n" +- "EvI1sbixZBjBtdnc5rTtHUUQ25Am3JzwPRGo5laMGbj1pPyCPxlVi9LK82HQNX0B\n" +- "YK7tZtVfDHElQA7F4v3j9d3rad4O9/n+lyGIQ0tT7yQcBm2A8FEaP0bZYCLMjwMN\n" +- "WfaVLE8eXHyv+MfpNNLI9wttLxygKYM48I3NwsFuJgOa/KuodXaAmf8pJnx8t1Wn\n" +- "nxvaYXFiUn/TxmhM/qhemPa6+0nqq+aWV5eT7xn4K/ghLgNs09v6Yge0pmPl9Oz+\n" +- "+bjJ+aKRnAmwCOY8/5U5EilAiUOeBoO9+8OXtwIDAQABAoIBAGFTTbXXMkPK4HN8\n" +- "oItVdDlrAanG7hECuz3UtFUVE3upS/xG6TjqweVLwRqYCh2ssDXFwjy4mXRGDzF4\n" +- "e/e/6s9Txlrlh/w1MtTJ6ZzTdcViR9RKOczysjZ7S5KRlI3KnGFAuWPcG2SuOWjZ\n" +- "dZfzcj1Crd/ZHajBAVFHRsCo/ATVNKbTRprFfb27xKpQ2BwH/GG781sLE3ZVNIhs\n" +- "aRRaED4622kI1E/WXws2qQMqbFKzo0m1tPbLb3Z89WgZJ/tRQwuDype1Vfm7k6oX\n" +- "xfbp3948qSe/yWKRlMoPkleji/WxPkSIalzWSAi9ziN/0Uzhe65FURgrfHL3XR1A\n" +- "B8UR+aECgYEA7NPQZV4cAikk02Hv65JgISofqV49P8MbLXk8sdnI1n7Mj10TgzU3\n" +- "lyQGDEX4hqvT0bTXe4KAOxQZx9wumu05ejfzhdtSsEm6ptGHyCdmYDQeV0C/pxDX\n" +- "JNCK8XgMku2370XG0AnyBCT7NGlgtDcNCQufcesF2gEuoKiXg6Zjo7sCgYEA/Bzs\n" +- "9fWGZZnSsMSBSW2OYbFuhF3Fne0HcxXQHipl0Rujc/9g0nccwqKGizn4fGOE7a8F\n" +- "usQgJoeGcinL7E9OEP/uQ9VX1C9RNVjIxP1O5/Guw1zjxQQYetOvbPhN2QhD1Ye7\n" +- "0TRKrW1BapcjwLpFQlVg1ZeTPOi5lv24W/wX9jUCgYEAkrMSX/hPuTbrTNVZ3L6r\n" +- "NV/2hN+PaTPeXei/pBuXwOaCqDurnpcUfFcgN/IP5LwDVd+Dq0pHTFFDNv45EFbq\n" +- "R77o5n3ZVsIVEMiyJ1XgoK8oLDw7e61+15smtjT69Piz+09pu+ytMcwGn4y3Dmsb\n" +- "dALzHYnL8iLRU0ubrz0ec4kCgYAJiVKRTzNBPptQom49h85d9ac3jJCAE8o3WTjh\n" +- "Gzt0uHXrWlqgO280EY/DTnMOyXjqwLcXxHlu26uDP/99tdY/IF8z46sJ1KxetzgI\n" +- "84f7kBHLRAU9m5UNeFpnZdEUB5MBTbwWAsNcYgiabpMkpCcghjg+fBhOsoLqqjhC\n" +- "CnwhjQKBgQDkv0QTdyBU84TE8J0XY3eLQwXbrvG2yD5A2ntN3PyxGEneX5WTJGMZ\n" +- "xJxwaFYQiDS3b9E7b8Q5dg8qa5Y1+epdhx3cuQAWPm+AoHKshDfbRve4txBDQAqh\n" +- "c6MxSWgsa+2Ld5SWSNbGtpPcmEM3Fl5ttMCNCKtNc0UE16oHwaPAIw==\n" +- "-----END RSA PRIVATE KEY-----" +- ) ++ self.private_pem = """ ++-----BEGIN RSA PRIVATE KEY----- ++MIIEpAIBAAKCAQEA6TtDhWGwzEOWZP6m/zHoZnAPLABfetvoMPmxPGjFjtDuMRPv ++EvI1sbixZBjBtdnc5rTtHUUQ25Am3JzwPRGo5laMGbj1pPyCPxlVi9LK82HQNX0B ++YK7tZtVfDHElQA7F4v3j9d3rad4O9/n+lyGIQ0tT7yQcBm2A8FEaP0bZYCLMjwMN ++WfaVLE8eXHyv+MfpNNLI9wttLxygKYM48I3NwsFuJgOa/KuodXaAmf8pJnx8t1Wn ++nxvaYXFiUn/TxmhM/qhemPa6+0nqq+aWV5eT7xn4K/ghLgNs09v6Yge0pmPl9Oz+ +++bjJ+aKRnAmwCOY8/5U5EilAiUOeBoO9+8OXtwIDAQABAoIBAGFTTbXXMkPK4HN8 ++oItVdDlrAanG7hECuz3UtFUVE3upS/xG6TjqweVLwRqYCh2ssDXFwjy4mXRGDzF4 ++e/e/6s9Txlrlh/w1MtTJ6ZzTdcViR9RKOczysjZ7S5KRlI3KnGFAuWPcG2SuOWjZ ++dZfzcj1Crd/ZHajBAVFHRsCo/ATVNKbTRprFfb27xKpQ2BwH/GG781sLE3ZVNIhs ++aRRaED4622kI1E/WXws2qQMqbFKzo0m1tPbLb3Z89WgZJ/tRQwuDype1Vfm7k6oX ++xfbp3948qSe/yWKRlMoPkleji/WxPkSIalzWSAi9ziN/0Uzhe65FURgrfHL3XR1A ++B8UR+aECgYEA7NPQZV4cAikk02Hv65JgISofqV49P8MbLXk8sdnI1n7Mj10TgzU3 ++lyQGDEX4hqvT0bTXe4KAOxQZx9wumu05ejfzhdtSsEm6ptGHyCdmYDQeV0C/pxDX ++JNCK8XgMku2370XG0AnyBCT7NGlgtDcNCQufcesF2gEuoKiXg6Zjo7sCgYEA/Bzs ++9fWGZZnSsMSBSW2OYbFuhF3Fne0HcxXQHipl0Rujc/9g0nccwqKGizn4fGOE7a8F ++usQgJoeGcinL7E9OEP/uQ9VX1C9RNVjIxP1O5/Guw1zjxQQYetOvbPhN2QhD1Ye7 ++0TRKrW1BapcjwLpFQlVg1ZeTPOi5lv24W/wX9jUCgYEAkrMSX/hPuTbrTNVZ3L6r ++NV/2hN+PaTPeXei/pBuXwOaCqDurnpcUfFcgN/IP5LwDVd+Dq0pHTFFDNv45EFbq ++R77o5n3ZVsIVEMiyJ1XgoK8oLDw7e61+15smtjT69Piz+09pu+ytMcwGn4y3Dmsb ++dALzHYnL8iLRU0ubrz0ec4kCgYAJiVKRTzNBPptQom49h85d9ac3jJCAE8o3WTjh ++Gzt0uHXrWlqgO280EY/DTnMOyXjqwLcXxHlu26uDP/99tdY/IF8z46sJ1KxetzgI ++84f7kBHLRAU9m5UNeFpnZdEUB5MBTbwWAsNcYgiabpMkpCcghjg+fBhOsoLqqjhC ++CnwhjQKBgQDkv0QTdyBU84TE8J0XY3eLQwXbrvG2yD5A2ntN3PyxGEneX5WTJGMZ ++xJxwaFYQiDS3b9E7b8Q5dg8qa5Y1+epdhx3cuQAWPm+AoHKshDfbRve4txBDQAqh ++c6MxSWgsa+2Ld5SWSNbGtpPcmEM3Fl5ttMCNCKtNc0UE16oHwaPAIw== ++-----END RSA PRIVATE KEY----- ++ """ ++ ++ self.public_pem = """ ++-----BEGIN PUBLIC KEY----- ++MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6TtDhWGwzEOWZP6m/zHo ++ZnAPLABfetvoMPmxPGjFjtDuMRPvEvI1sbixZBjBtdnc5rTtHUUQ25Am3JzwPRGo ++5laMGbj1pPyCPxlVi9LK82HQNX0BYK7tZtVfDHElQA7F4v3j9d3rad4O9/n+lyGI ++Q0tT7yQcBm2A8FEaP0bZYCLMjwMNWfaVLE8eXHyv+MfpNNLI9wttLxygKYM48I3N ++wsFuJgOa/KuodXaAmf8pJnx8t1WnnxvaYXFiUn/TxmhM/qhemPa6+0nqq+aWV5eT ++7xn4K/ghLgNs09v6Yge0pmPl9Oz++bjJ+aKRnAmwCOY8/5U5EilAiUOeBoO9+8OX ++twIDAQAB ++-----END PUBLIC KEY----- ++ """ + + signed_token = tokens.signed_token_generator(self.private_pem, + user_id=123) +@@ -254,7 +265,7 @@ def test_scopes_and_user_id_stored_in_access_token(self): + + access_token = json.loads(body)['access_token'] + +- claims = common.verify_signed_token(self.private_pem, access_token) ++ claims = common.verify_signed_token(self.public_pem, access_token) + + self.assertEqual(claims['scope'], 'all of them') + self.assertEqual(claims['user_id'], 123) +diff --git a/tests/oauth2/rfc6749/test_utils.py b/tests/oauth2/rfc6749/test_utils.py +index d292eeb..858cf1f 100644 +--- a/tests/oauth2/rfc6749/test_utils.py ++++ b/tests/oauth2/rfc6749/test_utils.py +@@ -73,6 +73,9 @@ def test_list_to_scope(self): + string_list = ['foo', 'bar', 'baz'] + self.assertEqual(list_to_scope(string_list), expected) + ++ string_tuple = ('foo', 'bar', 'baz') ++ self.assertEqual(list_to_scope(string_tuple), expected) ++ + obj_list = [ScopeObject('foo'), ScopeObject('bar'), ScopeObject('baz')] + self.assertEqual(list_to_scope(obj_list), expected) + diff --git a/python-oauthlib.changes b/python-oauthlib.changes index 49f524c..fee293e 100644 --- a/python-oauthlib.changes +++ b/python-oauthlib.changes @@ -1,3 +1,31 @@ +------------------------------------------------------------------- +Wed Apr 22 09:38:29 UTC 2015 - mcihar@suse.cz + +- Update to 0.7.2: + * (Quick fix) Unpushed locally modified files got included in the PyPI 0.7.1 + release. Doing a new clean release to address this. Please upgrade quickly + and report any issues you are running into. + * (Quick fix) Add oauthlib.common.log object back in for libraries using it. + * (Change) OAuth2 clients will not raise a Warning on scope change if + the environment variable ``OAUTHLIB_RELAX_TOKEN_SCOPE`` is set. The token + will now be available as an attribute on the error, ``error.token``. + Token changes will now also be announced using blinker. + * (Fix/Feature) Automatic fixes of non-compliant OAuth2 provider responses (e.g. Facebook). + * (Fix) Logging is now tiered (per file) as opposed to logging all under ``oauthlib``. + * (Fix) Error messages should now include a description in their message. + * (Fix/Feature) Optional support for jsonp callbacks after token revocation. + * (Feature) Client side preparation of OAuth 2 token revocation requests. + * (Feature) New OAuth2 client API methods for preparing full requests. + * (Feature) OAuth1 SignatureOnlyEndpoint that only verifies signatures and client IDs. + * (Fix/Feature) Refresh token grant now allow optional refresh tokens. + * (Fix) add missing state param to OAuth2 errors. + * (Fix) add_params_to_uri now properly parse fragment. + * (Fix/Feature) All OAuth1 errors can now be imported from oauthlib.oauth1. + * (Fix/Security) OAuth2 logs will now strip client provided password, if present. + * Allow unescaped @ in urlencoded parameters. +- New dependency on python-blinker +- Add pycrypto.patch to be compatible with latest PyJWT + ------------------------------------------------------------------- Wed Jul 23 13:29:30 UTC 2014 - mcihar@suse.cz diff --git a/python-oauthlib.spec b/python-oauthlib.spec index 6bfd021..efaa1cd 100644 --- a/python-oauthlib.spec +++ b/python-oauthlib.spec @@ -1,7 +1,7 @@ # # spec file for package python-oauthlib # -# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,24 +17,30 @@ Name: python-oauthlib -Version: 0.6.3 +Version: 0.7.2 Release: 0 Url: https://github.com/idangazit/oauthlib Summary: A Generic Implementation of the OAuth Request-Signing Logic License: BSD-3-Clause Group: Development/Languages/Python Source: http://pypi.python.org/packages/source/o/oauthlib/oauthlib-%{version}.tar.gz +# PATCH-FIX-UPSTREAM -- https://github.com/josephtate/oauthlib/commit/fc2d1218e015adcf65ba01965884430c47895cd0 +Patch0: pycrypto.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: python-devel BuildRequires: python-setuptools # Test build requirements: BuildRequires: python-PyJWT +BuildRequires: python-blinker +BuildRequires: python-cryptography BuildRequires: python-mock BuildRequires: python-nose +BuildRequires: python-pyasn1 BuildRequires: python-pycrypto BuildRequires: python-unittest2 Requires: python-PyJWT -Requires: python-pycrypto +Requires: python-blinker +Requires: python-cryptography %if 0%{?suse_version} && 0%{?suse_version} <= 1110 %{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} %else @@ -59,6 +65,7 @@ veneer on top of OAuthLib and get OAuth support for very little effort. %prep %setup -q -n oauthlib-%{version} +%patch0 -p1 %build python setup.py build