diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a02f9ee --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/oauthlib-2.0.1.tar.gz diff --git a/.python-oauthlib.metadata b/.python-oauthlib.metadata new file mode 100644 index 0000000..f28d4cb --- /dev/null +++ b/.python-oauthlib.metadata @@ -0,0 +1 @@ +8e080d3e6f57c32e4c59179b4c5f9eca78d6f430 SOURCES/oauthlib-2.0.1.tar.gz diff --git a/SOURCES/jwcrypto.patch b/SOURCES/jwcrypto.patch new file mode 100644 index 0000000..aef2de4 --- /dev/null +++ b/SOURCES/jwcrypto.patch @@ -0,0 +1,393 @@ +diff -r -u oauthlib-2.0.1/oauthlib/common.py oauthlib-2.0.1.patched/oauthlib/common.py +--- oauthlib-2.0.1/oauthlib/common.py 2016-11-23 05:30:34.000000000 -0500 ++++ oauthlib-2.0.1.patched/oauthlib/common.py 2017-03-16 17:50:22.814033506 -0400 +@@ -15,6 +15,16 @@ + import re + import sys + import time ++from calendar import timegm ++ ++from jwcrypto.jwk import JWK as _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 +@@ -29,6 +39,82 @@ + except ImportError: + import urllib.parse as urlparse + ++# Copy code from jwcrypto 0.3.2, we need JWK.from_pyca() ++from cryptography.hazmat.primitives.asymmetric import rsa ++class JWK(_JWK): ++ def __init__(self, **kwargs): ++ self._params = dict() ++ self._key = dict() ++ self._unknown = dict() ++ ++ if 'generate' in kwargs: ++ self.generate_key(**kwargs) ++ elif kwargs: ++ self.import_key(**kwargs) ++ ++ def _import_pyca_pri_rsa(self, key, **params): ++ pn = key.private_numbers() ++ params.update( ++ kty='RSA', ++ n=self._encode_int(pn.public_numbers.n), ++ e=self._encode_int(pn.public_numbers.e), ++ d=self._encode_int(pn.d), ++ p=self._encode_int(pn.p), ++ q=self._encode_int(pn.q), ++ dp=self._encode_int(pn.dmp1), ++ dq=self._encode_int(pn.dmq1), ++ qi=self._encode_int(pn.iqmp) ++ ) ++ self.import_key(**params) ++ ++ def _import_pyca_pub_rsa(self, key, **params): ++ pn = key.public_numbers() ++ params.update( ++ kty='RSA', ++ n=self._encode_int(pn.n), ++ e=self._encode_int(pn.e) ++ ) ++ self.import_key(**params) ++ ++ def _import_pyca_pri_ec(self, key, **params): ++ pn = key.private_numbers() ++ params.update( ++ kty='EC', ++ crv=JWKpycaCurveMap[key.curve.name], ++ x=self._encode_int(pn.public_numbers.x), ++ y=self._encode_int(pn.public_numbers.y), ++ d=self._encode_int(pn.private_value) ++ ) ++ self.import_key(**params) ++ ++ def _import_pyca_pub_ec(self, key, **params): ++ pn = key.public_numbers() ++ params.update( ++ kty='EC', ++ crv=JWKpycaCurveMap[key.curve.name], ++ x=self._encode_int(pn.x), ++ y=self._encode_int(pn.y), ++ ) ++ self.import_key(**params) ++ ++ def import_from_pyca(self, key): ++ if isinstance(key, rsa.RSAPrivateKey): ++ self._import_pyca_pri_rsa(key) ++ elif isinstance(key, rsa.RSAPublicKey): ++ self._import_pyca_pub_rsa(key) ++ elif isinstance(key, ec.EllipticCurvePrivateKey): ++ self._import_pyca_pri_ec(key) ++ elif isinstance(key, ec.EllipticCurvePublicKey): ++ self._import_pyca_pub_ec(key) ++ else: ++ raise InvalidJWKValue('Unknown key object %r' % key) ++ ++ @classmethod ++ def from_pyca(cls, key): ++ obj = cls() ++ obj.import_from_pyca(key) ++ return obj ++ + UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + '0123456789') +@@ -229,28 +315,69 @@ + 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 + +- now = datetime.datetime.utcnow() ++ 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 ++ ++ ++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 -r -u oauthlib-2.0.1/oauthlib/oauth1/rfc5849/signature.py oauthlib-2.0.1.patched/oauthlib/oauth1/rfc5849/signature.py +--- oauthlib-2.0.1/oauthlib/oauth1/rfc5849/signature.py 2016-11-23 05:30:34.000000000 -0500 ++++ oauthlib-2.0.1.patched/oauthlib/oauth1/rfc5849/signature.py 2017-03-16 13:54:56.881287445 -0400 +@@ -31,8 +31,15 @@ + 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 @@ + # .. _`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 @@ + 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 @@ + 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 @@ + 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 -r -u oauthlib-2.0.1/oauthlib/oauth2/rfc6749/clients/service_application.py oauthlib-2.0.1.patched/oauthlib/oauth2/rfc6749/clients/service_application.py +--- oauthlib-2.0.1/oauthlib/oauth2/rfc6749/clients/service_application.py 2016-11-23 05:30:34.000000000 -0500 ++++ oauthlib-2.0.1.patched/oauthlib/oauth2/rfc6749/clients/service_application.py 2017-03-16 13:54:56.881287445 -0400 +@@ -10,7 +10,7 @@ + + 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 @@ + + .. _`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 @@ + + 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 -r -u oauthlib-2.0.1/oauthlib.egg-info/requires.txt oauthlib-2.0.1.patched/oauthlib.egg-info/requires.txt +--- oauthlib-2.0.1/oauthlib.egg-info/requires.txt 2016-11-23 05:31:05.000000000 -0500 ++++ oauthlib-2.0.1.patched/oauthlib.egg-info/requires.txt 2017-03-16 13:55:02.296191560 -0400 +@@ -7,10 +7,10 @@ + + [signedtoken] + cryptography +-pyjwt>=1.0.0 ++jwcrypto + + [test] + nose + cryptography +-pyjwt>=1.0.0 ++jwcrypto + blinker +diff -r -u oauthlib-2.0.1/oauthlib.egg-info/SOURCES.txt oauthlib-2.0.1.patched/oauthlib.egg-info/SOURCES.txt +--- oauthlib-2.0.1/oauthlib.egg-info/SOURCES.txt 2016-11-23 05:31:05.000000000 -0500 ++++ oauthlib-2.0.1.patched/oauthlib.egg-info/SOURCES.txt 2017-03-16 13:55:02.320191135 -0400 +@@ -2,6 +2,7 @@ + LICENSE + MANIFEST.in + README.rst ++setup.cfg + setup.py + oauthlib/__init__.py + oauthlib/common.py +diff -r -u oauthlib-2.0.1/setup.py oauthlib-2.0.1.patched/setup.py +--- oauthlib-2.0.1/setup.py 2016-11-23 05:30:34.000000000 -0500 ++++ oauthlib-2.0.1.patched/setup.py 2017-03-16 13:54:56.883287410 -0400 +@@ -18,11 +18,11 @@ + return f.read() + + if sys.version_info[0] == 3: +- tests_require = ['nose', 'cryptography', 'pyjwt>=1.0.0', 'blinker'] ++ tests_require = ['nose', 'cryptography', 'jwcrypto', 'blinker'] + else: +- tests_require = ['nose', 'unittest2', 'cryptography', 'mock', 'pyjwt>=1.0.0', 'blinker'] ++ tests_require = ['nose', 'cryptography', 'mock', 'jwcrypto', 'blinker'] + rsa_require = ['cryptography'] +-signedtoken_require = ['cryptography', 'pyjwt>=1.0.0'] ++signedtoken_require = ['cryptography', 'jwcrypto'] + signals_require = ['blinker'] + + requires = [] +diff -r -u oauthlib-2.0.1/tests/oauth2/rfc6749/clients/test_service_application.py oauthlib-2.0.1.patched/tests/oauth2/rfc6749/clients/test_service_application.py +--- oauthlib-2.0.1/tests/oauth2/rfc6749/clients/test_service_application.py 2016-11-23 05:30:34.000000000 -0500 ++++ oauthlib-2.0.1.patched/tests/oauth2/rfc6749/clients/test_service_application.py 2017-03-16 13:54:56.882287427 -0400 +@@ -4,10 +4,9 @@ + 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 @@ + 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 -r -u oauthlib-2.0.1/tests/oauth2/rfc6749/test_server.py oauthlib-2.0.1.patched/tests/oauth2/rfc6749/test_server.py +--- oauthlib-2.0.1/tests/oauth2/rfc6749/test_server.py 2016-11-23 05:30:34.000000000 -0500 ++++ oauthlib-2.0.1.patched/tests/oauth2/rfc6749/test_server.py 2017-03-16 13:54:56.882287427 -0400 +@@ -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/SPECS/python-oauthlib.spec b/SPECS/python-oauthlib.spec new file mode 100644 index 0000000..0205027 --- /dev/null +++ b/SPECS/python-oauthlib.spec @@ -0,0 +1,229 @@ +%if 0%{?fedora} +%global with_python3 1 +%endif + +%if 0%{?rhel} && 0%{?rhel} <= 7 +%{!?__python2: %global __python2 /usr/bin/python2} +%{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} +%{!?python2_sitearch: %global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} +%{!?py2_build: %global py2_build %{__python2} setup.py build --executable="%{__python2} -s" %{?*}} +%{!?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: 8%{?dist} +Summary: An implementation of the OAuth request-signing logic + +Group: Development/Libraries +License: BSD +URL: http://pypi.python.org/pypi/oauthlib +Source0: http://pypi.python.org/packages/source/o/%{modname}/%{modname}-%{version}.tar.gz +#Source0: https://github.com/%{gh_owner}/%{gh_project}/archive/%{commit}/%{gh_project}-%{commit}.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 +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. + +%package -n python2-oauthlib +%if 0%{?python_provide:1} +%python_provide python2-oauthlib +%else +Provides: python-oauthlib = %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: python-oauthlib < %{?epoch:%{epoch}:}%{version}-%{release} +%endif + +Summary: An implementation of the OAuth request-signing logic +Group: Development/Libraries + +BuildRequires: python2-devel +BuildRequires: python2-setuptools + +BuildRequires: python2-nose +BuildRequires: python-mock +BuildRequires: python-blinker + +BuildRequires: python-jwcrypto +BuildRequires: python2-cryptography + +Requires: python-jwcrypto +Requires: python2-cryptography >= 0.8.1 + +%description -n python2-oauthlib +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 +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. + +%if 0%{?with_python3} +%package -n python3-oauthlib +%{?python_provide:%python_provide python3-oauthlib} +Summary: An implementation of the OAuth request-signing logic +Group: Development/Libraries + +BuildRequires: python3-devel +BuildRequires: python3-setuptools + +BuildRequires: python3-nose +BuildRequires: python3-blinker + +BuildRequires: python3-jwcrypto +BuildRequires: python3-cryptography + +Requires: python3-jwcrypto +Requires: python3-cryptography >= 0.8.1 + +%description -n python3-oauthlib +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 +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 + +%prep +%setup -q -n %{modname}-%{version} +#%%setup -q -n %{gh_project}-%{commit} +%patch0 -p1 + +# python-unittest2 is now provided by "python" package and python-unittest is retired +# adapt setup.py to reflect this fact downstream +sed -i "s/'unittest2', //" setup.py + +# Remove bundled egg-info in case it exists +rm -rf %{modname}.egg-info + +%build +%py2_build +%if 0%{?with_python3} +%py3_build +%endif + +%install +%py2_install +%if 0%{?with_python3} +%py3_install +%endif + +%check +%{__python2} setup.py test +%if 0%{?with_python3} +%{__python3} setup.py test +%endif + +%files -n python2-oauthlib +%doc README.rst +%license LICENSE +%{python2_sitelib}/%{modname}/ +%{python2_sitelib}/%{modname}-%{version}* + +%if 0%{?with_python3} +%files -n python3-oauthlib +%doc README.rst +%license LICENSE +%{python3_sitelib}/%{modname}/ +%{python3_sitelib}/%{modname}-%{version}-* +%endif + +%changelog +* Mon Apr 24 2017 John Dennis - 2.0.1-8 +- add missing Obsoletes for prior python-oauthlib package, + replaced by python2-oauthlib + Resolves: rhbz#1401784 + +* Thu Apr 6 2017 John Dennis - 2.0.1-7 +- fix usage of python-provide macro + Resolves: rhbz#1401784 + +* Thu Apr 6 2017 John Dennis - 2.0.1-6 +- add spaces around python-cryptography version operator + Resolves: rhbz#1401784 + +* Tue Apr 4 2017 John Dennis - 2.0.1-5 +- change Requires: python-cryptography to python2-cryptography + Resolves: rhbz#1401784 + +* Mon Apr 3 2017 John Dennis - 2.0.1-4 +- Add Provides: python-oauthlib=version + Resolves: rhbz#1401784 + +* Mon Apr 3 2017 John Dennis - 2.0.1-3 +- Add Provides: python-oauthlib + Resolves: rhbz#1401784 + +* Wed Mar 29 2017 John Dennis - 2.0.1-2 +- Add missing Requires + Resolves: rhbz#1401784 + +* Thu Mar 16 2017 John Dennis - 2.0.1-1 +- Upgrade to upstream 2.0.1 and port from jwt to jwcrypto + Resolves: rhbz#1401784 + +* Sat Feb 11 2017 Fedora Release Engineering - 1.0.3-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Tue Dec 13 2016 Stratakis Charalampos - 1.0.3-4 +- Rebuild for Python 3.6 + +* Tue Jul 19 2016 Fedora Release Engineering - 1.0.3-3 +- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages + +* Tue Jun 28 2016 Ralph Bean - 1.0.3-2 +- Modernize python macros. + +* Sun Apr 10 2016 Kevin Fenzi - 1.0.3-1 +- Update to 1.0.3 +- Add python2 provides (fixes bug #1313235 and #1314349) + +* Thu Feb 04 2016 Fedora Release Engineering - 0.7.2-5.20150520git514cad7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Nov 10 2015 Fedora Release Engineering - 0.7.2-4.20150520git514cad7 +- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5 + +* Thu Jun 18 2015 Fedora Release Engineering - 0.7.2-3.20150520git514cad7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Wed Feb 18 2015 Ralph Bean - 0.7.2-2.20150520git514cad7 +- new version, from a git checkout +- Replace our patch with a sed statement. + +* Sat Jun 07 2014 Fedora Release Engineering - 0.6.0-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Wed May 14 2014 Bohuslav Kabrda - 0.6.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Changes/Python_3.4 + +* Fri Apr 11 2014 Ralph Bean - 0.6.0-4 +- Use forward-compat python-crypto2.6 package for el6. + +* Tue Jan 21 2014 Ralph Bean - 0.6.0-3 +- Compat macros for el6. + +* Fri Nov 01 2013 Ralph Bean - 0.6.0-2 +- Modernized python2 rpmmacros. + +* Thu Oct 31 2013 Ralph Bean - 0.6.0-1 +- Initial package for Fedora