Hello community, here is the log from the commit of package python3-Beaker for openSUSE:Factory checked in at 2016-02-01 19:56:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-Beaker (Old) and /work/SRC/openSUSE:Factory/.python3-Beaker.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-Beaker" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-Beaker/python3-Beaker.changes 2015-04-27 13:05:17.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python3-Beaker.new/python3-Beaker.changes 2016-02-01 19:57:06.000000000 +0100 @@ -1,0 +2,18 @@ +Sat Jan 30 17:57:04 UTC 2016 - a...@gmx.de + +- specfile: + * update copyright year + +- update to version 1.8.0: + * Encrypted sessions can now specify nonce length for salt + generation through encrypt_nonce_bits parameter. set it to 48 for + backward compatibility with sessions generated before 1.8.0 + * kwargs support in @cache_region decorator + * annotations support in @cache_region decorator + * data_serializer parameter in Session can now specify json to avoid + pickle security issues + * Invalid cookies are now skipped in cookie based sessions + * Memcached based on PyLibMC now share same connection pool for same + url + +------------------------------------------------------------------- Old: ---- Beaker-1.7.0.tar.gz New: ---- Beaker-1.8.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-Beaker.spec ++++++ --- /var/tmp/diff_new_pack.U5wcME/_old 2016-02-01 19:57:07.000000000 +0100 +++ /var/tmp/diff_new_pack.U5wcME/_new 2016-02-01 19:57:07.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package python3-Beaker # -# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2016 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,7 +17,7 @@ Name: python3-Beaker -Version: 1.7.0 +Version: 1.8.0 Release: 0 Url: http://beaker.rtfd.org/ Summary: A Session and Caching library with WSGI Middleware ++++++ Beaker-1.7.0.tar.gz -> Beaker-1.8.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/Beaker.egg-info/PKG-INFO new/Beaker-1.8.0/Beaker.egg-info/PKG-INFO --- old/Beaker-1.7.0/Beaker.egg-info/PKG-INFO 2015-04-22 17:32:50.000000000 +0200 +++ new/Beaker-1.8.0/Beaker.egg-info/PKG-INFO 2016-01-27 12:40:12.000000000 +0100 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: Beaker -Version: 1.7.0 +Version: 1.8.0 Summary: A Session and Caching library with WSGI Middleware Home-page: http://beaker.rtfd.org/ -Author: Ben Bangert, Mike Bayer, Philip Jenvey -Author-email: b...@groovie.org, pjen...@groovie.org +Author: Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina +Author-email: b...@groovie.org, pjen...@groovie.org, a...@turbogears.org License: BSD Description: ========================= Cache and Session Library @@ -81,6 +81,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: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/Beaker.egg-info/SOURCES.txt new/Beaker-1.8.0/Beaker.egg-info/SOURCES.txt --- old/Beaker-1.7.0/Beaker.egg-info/SOURCES.txt 2015-04-22 17:32:50.000000000 +0200 +++ new/Beaker-1.8.0/Beaker.egg-info/SOURCES.txt 2016-01-27 12:40:12.000000000 +0100 @@ -6,6 +6,7 @@ Beaker.egg-info/dependency_links.txt Beaker.egg-info/entry_points.txt Beaker.egg-info/not-zip-safe +Beaker.egg-info/pbr.json Beaker.egg-info/requires.txt Beaker.egg-info/top_level.txt beaker/__init__.py @@ -13,6 +14,7 @@ beaker/cache.py beaker/container.py beaker/converters.py +beaker/cookie.py beaker/exceptions.py beaker/middleware.py beaker/session.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/Beaker.egg-info/pbr.json new/Beaker-1.8.0/Beaker.egg-info/pbr.json --- old/Beaker-1.7.0/Beaker.egg-info/pbr.json 1970-01-01 01:00:00.000000000 +0100 +++ new/Beaker-1.8.0/Beaker.egg-info/pbr.json 2015-08-15 21:32:53.000000000 +0200 @@ -0,0 +1 @@ +{"is_release": false, "git_version": "5c407db"} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/Beaker.egg-info/requires.txt new/Beaker-1.8.0/Beaker.egg-info/requires.txt --- old/Beaker-1.7.0/Beaker.egg-info/requires.txt 2015-04-22 17:32:50.000000000 +0200 +++ new/Beaker-1.8.0/Beaker.egg-info/requires.txt 2016-01-27 12:40:12.000000000 +0100 @@ -1,11 +1,11 @@ - +funcsigs [testsuite] nose webtest Mock -coverage pycrypto +coverage SQLALchemy [crypto] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/PKG-INFO new/Beaker-1.8.0/PKG-INFO --- old/Beaker-1.7.0/PKG-INFO 2015-04-22 17:32:50.000000000 +0200 +++ new/Beaker-1.8.0/PKG-INFO 2016-01-27 12:40:12.000000000 +0100 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: Beaker -Version: 1.7.0 +Version: 1.8.0 Summary: A Session and Caching library with WSGI Middleware Home-page: http://beaker.rtfd.org/ -Author: Ben Bangert, Mike Bayer, Philip Jenvey -Author-email: b...@groovie.org, pjen...@groovie.org +Author: Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina +Author-email: b...@groovie.org, pjen...@groovie.org, a...@turbogears.org License: BSD Description: ========================= Cache and Session Library @@ -81,6 +81,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: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/__init__.py new/Beaker-1.8.0/beaker/__init__.py --- old/Beaker-1.7.0/beaker/__init__.py 2015-02-25 16:49:49.000000000 +0100 +++ new/Beaker-1.8.0/beaker/__init__.py 2016-01-26 00:31:01.000000000 +0100 @@ -1 +1 @@ -__version__ = '1.7.0' +__version__ = '1.8.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/_compat.py new/Beaker-1.8.0/beaker/_compat.py --- old/Beaker-1.7.0/beaker/_compat.py 2015-04-20 21:56:09.000000000 +0200 +++ new/Beaker-1.8.0/beaker/_compat.py 2016-01-25 15:29:15.000000000 +0100 @@ -82,6 +82,7 @@ def dictkeyslist(d): return d.keys() + def im_func(f): if not PY2: # pragma: no cover return getattr(f, '__func__', None) @@ -154,3 +155,14 @@ exec_("""def reraise(tp, value, tb=None): raise tp, value, tb """) + + +try: + from inspect import signature as func_signature +except ImportError: + from funcsigs import signature as func_signature + + +def bindfuncargs(arginfo, args, kwargs): + boundargs = arginfo.bind(*args, **kwargs) + return boundargs.args, boundargs.kwargs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/cache.py new/Beaker-1.8.0/beaker/cache.py --- old/Beaker-1.7.0/beaker/cache.py 2015-02-25 16:37:38.000000000 +0100 +++ new/Beaker-1.8.0/beaker/cache.py 2015-11-13 22:21:53.000000000 +0100 @@ -7,8 +7,9 @@ """ import warnings -from beaker._compat import u_, unicode_text +from itertools import chain +from beaker._compat import u_, unicode_text, func_signature, bindfuncargs import beaker.container as container import beaker.util as util from beaker.crypto.util import sha1 @@ -537,7 +538,7 @@ _cache_decorator_invalidate(cache, key_length, args) -def _cache_decorate(deco_args, manager, kwargs, region): +def _cache_decorate(deco_args, manager, options, region): """Return a caching function decorator.""" cache = [None] @@ -545,9 +546,10 @@ def decorate(func): namespace = util.func_namespace(func) skip_self = util.has_self_arg(func) + signature = func_signature(func) @wraps(func) - def cached(*args): + def cached(*args, **kwargs): if not cache[0]: if region is not None: if region not in cache_regions: @@ -555,24 +557,32 @@ 'Cache region not configured: %s' % region) reg = cache_regions[region] if not reg.get('enabled', True): - return func(*args) + return func(*args, **kwargs) cache[0] = Cache._get_cache(namespace, reg) elif manager: - cache[0] = manager.get_cache(namespace, **kwargs) + cache[0] = manager.get_cache(namespace, **options) else: raise Exception("'manager + kwargs' or 'region' " "argument is required") + cache_key_kwargs = [] + if kwargs: + # kwargs provided, merge them in positional args + # to avoid having different cache keys. + args, kwargs = bindfuncargs(signature, args, kwargs) + cache_key_kwargs = [u_(':').join((u_(key), u_(value))) for key, value in kwargs.items()] + cache_key_args = args if skip_self: cache_key_args = args[1:] - cache_key = u_(" ").join(map(u_, deco_args + cache_key_args)) + + cache_key = u_(" ").join(map(u_, chain(deco_args, cache_key_args, cache_key_kwargs))) if region: cachereg = cache_regions[region] key_length = cachereg.get('key_length', util.DEFAULT_CACHE_KEY_LENGTH) else: - key_length = kwargs.pop('key_length', util.DEFAULT_CACHE_KEY_LENGTH) + key_length = options.pop('key_length', util.DEFAULT_CACHE_KEY_LENGTH) # TODO: This is probably a bug as length is checked before converting to UTF8 # which will cause cache_key to grow in size. @@ -580,7 +590,7 @@ cache_key = sha1(cache_key.encode('utf-8')).hexdigest() def go(): - return func(*args) + return func(*args, **kwargs) return cache[0].get_value(cache_key, createfunc=go) cached._arg_namespace = namespace diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/cookie.py new/Beaker-1.8.0/beaker/cookie.py --- old/Beaker-1.7.0/beaker/cookie.py 1970-01-01 01:00:00.000000000 +0100 +++ new/Beaker-1.8.0/beaker/cookie.py 2015-11-22 14:54:40.000000000 +0100 @@ -0,0 +1,69 @@ +import sys +from ._compat import http_cookies + +# Some versions of Python 2.7 and later won't need this encoding bug fix: +_cookie_encodes_correctly = http_cookies.SimpleCookie().value_encode(';') == (';', '"\\073"') + +# Cookie pickling bug is fixed in Python 2.7.9 and Python 3.4.3+ +# http://bugs.python.org/issue22775 +cookie_pickles_properly = ( + (sys.version_info[:2] == (2, 7) and sys.version_info >= (2, 7, 9)) or + sys.version_info >= (3, 4, 3) +) + + +# Adapted from Django.http.cookies and always enabled the bad_cookies +# behaviour to cope with any invalid cookie key while keeping around +# the session. +class SimpleCookie(http_cookies.SimpleCookie): + if not cookie_pickles_properly: + def __setitem__(self, key, value): + # Apply the fix from http://bugs.python.org/issue22775 where + # it's not fixed in Python itself + if isinstance(value, http_cookies.Morsel): + # allow assignment of constructed Morsels (e.g. for pickling) + dict.__setitem__(self, key, value) + else: + super(SimpleCookie, self).__setitem__(key, value) + + if not _cookie_encodes_correctly: + def value_encode(self, val): + # Some browsers do not support quoted-string from RFC 2109, + # including some versions of Safari and Internet Explorer. + # These browsers split on ';', and some versions of Safari + # are known to split on ', '. Therefore, we encode ';' and ',' + + # SimpleCookie already does the hard work of encoding and decoding. + # It uses octal sequences like '\\012' for newline etc. + # and non-ASCII chars. We just make use of this mechanism, to + # avoid introducing two encoding schemes which would be confusing + # and especially awkward for javascript. + + # NB, contrary to Python docs, value_encode returns a tuple containing + # (real val, encoded_val) + val, encoded = super(SimpleCookie, self).value_encode(val) + + encoded = encoded.replace(";", "\\073").replace(",", "\\054") + # If encoded now contains any quoted chars, we need double quotes + # around the whole string. + if "\\" in encoded and not encoded.startswith('"'): + encoded = '"' + encoded + '"' + + return val, encoded + + def load(self, rawdata): + self.bad_cookies = set() + super(SimpleCookie, self).load(rawdata) + for key in self.bad_cookies: + del self[key] + + # override private __set() method: + # (needed for using our Morsel, and for laxness with CookieError + def _BaseCookie__set(self, key, real_value, coded_value): + try: + super(SimpleCookie, self)._BaseCookie__set(key, real_value, coded_value) + except http_cookies.CookieError: + if not hasattr(self, 'bad_cookies'): + self.bad_cookies = set() + self.bad_cookies.add(key) + dict.__setitem__(self, key, http_cookies.Morsel()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/crypto/__init__.py new/Beaker-1.8.0/beaker/crypto/__init__.py --- old/Beaker-1.7.0/beaker/crypto/__init__.py 2015-02-25 16:37:38.000000000 +0100 +++ new/Beaker-1.8.0/beaker/crypto/__init__.py 2016-01-25 23:36:28.000000000 +0100 @@ -7,6 +7,7 @@ from beaker import util keyLength = None +DEFAULT_NONCE_BITS = 128 if JYTHON: try: @@ -42,3 +43,12 @@ # os.urandom() returns truly random data, this will have no effect on the # overall security. return pbkdf2(master_key, salt, iterations=iterations, dklen=keyLength) + + +def get_nonce_size(number_of_bits): + if number_of_bits % 8: + raise ValueError('Nonce complexity currently supports multiples of 8') + + bytes = number_of_bits // 8 + b64bytes = ((4 * bytes // 3) + 3) & ~3 + return bytes, b64bytes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/crypto/pbkdf2.py new/Beaker-1.8.0/beaker/crypto/pbkdf2.py --- old/Beaker-1.7.0/beaker/crypto/pbkdf2.py 2015-02-25 16:37:38.000000000 +0100 +++ new/Beaker-1.8.0/beaker/crypto/pbkdf2.py 2016-01-23 11:54:31.000000000 +0100 @@ -1,3 +1,8 @@ +""" +PBKDF2 Implementation adapted from django.utils.crypto. + +This is used to generate the encryption key for enciphered sessions. +""" from beaker._compat import bytes_, xrange_ import hmac @@ -22,10 +27,14 @@ if hasattr(hashlib, "pbkdf2_hmac"): def pbkdf2(password, salt, iterations, dklen=0, digest=None): """ - Implements PBKDF2 with the same API as Django's existing - implementation, using the stdlib. + Implements PBKDF2 using the stdlib. This is used in Python 2.7.8+ and 3.4+. + + HMAC+SHA256 is used as the default pseudo random function. - This is used in Python 2.7.8+ and 3.4+. + As of 2014, 100,000 iterations was the recommended default which took + 100ms on a 2.7Ghz Intel i7 with an optimized implementation. This is + probably the bare minimum for security given 1000 iterations was + recommended in 2001. """ if digest is None: digest = hashlib.sha1 @@ -46,10 +55,7 @@ 100ms on a 2.7Ghz Intel i7 with an optimized implementation. This is probably the bare minimum for security given 1000 iterations was recommended in 2001. This code is very well optimized for CPython and - is about five times slower than OpenSSL's implementation. Look in - django.contrib.auth.hashers for the present default, it is lower than - the recommended 100,000 because of the performance difference between - this and an optimized implementation. + is about five times slower than OpenSSL's implementation. """ assert iterations > 0 if not digest: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/ext/memcached.py new/Beaker-1.8.0/beaker/ext/memcached.py --- old/Beaker-1.7.0/beaker/ext/memcached.py 2015-02-25 16:37:38.000000000 +0100 +++ new/Beaker-1.8.0/beaker/ext/memcached.py 2016-01-23 11:49:56.000000000 +0100 @@ -159,6 +159,8 @@ class PyLibMCNamespaceManager(MemcachedNamespaceManager): """Provide thread-local support for pylibmc.""" + pools = SyncDict() + def __init__(self, *arg, **kw): super(PyLibMCNamespaceManager, self).__init__(*arg, **kw) @@ -176,7 +178,9 @@ servers=url.split(';'), behaviors=behaviors, binary=(protocol == 'binary'), username=username, password=password) - self.pool = pylibmc.ThreadMappedPool(self.mc) + self.pool = PyLibMCNamespaceManager.pools.get( + (memcache_module, url), + pylibmc.ThreadMappedPool, self.mc) def __getitem__(self, key): with self.pool.reserve() as mc: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/session.py new/Beaker-1.8.0/beaker/session.py --- old/Beaker-1.7.0/beaker/session.py 2015-02-25 16:37:38.000000000 +0100 +++ new/Beaker-1.8.0/beaker/session.py 2016-01-25 23:36:28.000000000 +0100 @@ -3,11 +3,11 @@ import os import time from datetime import datetime, timedelta -from beaker.crypto import hmac as HMAC, hmac_sha1 as SHA1, sha1 +from beaker.crypto import hmac as HMAC, hmac_sha1 as SHA1, sha1, get_nonce_size, DEFAULT_NONCE_BITS from beaker import crypto, util from beaker.cache import clsmap from beaker.exceptions import BeakerException, InvalidCryptoBackendError - +from beaker.cookie import SimpleCookie __all__ = ['SignedCookie', 'Session'] @@ -42,7 +42,7 @@ return raw_id.replace('+', '-').replace('/', '_').rstrip('=') -class SignedCookie(http_cookies.BaseCookie): +class SignedCookie(SimpleCookie): """Extends python cookie to give digital signature support""" def __init__(self, secret, input=None): self.secret = secret.encode('UTF-8') @@ -93,20 +93,26 @@ :param cookie_expires: Expiration date for cookie :param cookie_domain: Domain to use for the cookie. :param cookie_path: Path to use for the cookie. + :param data_serializer: If ``"json"`` or ``"pickle"`` should be used + to serialize data. By default ``pickle`` is used. :param secure: Whether or not the cookie should only be sent over SSL. :param httponly: Whether or not the cookie should only be accessible by the browser not by JavaScript. :param encrypt_key: The key to use for the local session encryption, if not provided the session will not be encrypted. :param validate_key: The key used to sign the local encrypted session - + :param encrypt_nonce_bits: Number of bits used to generate nonce for encryption key salt. + For security reason this is 128bits be default. If you want + to keep backward compatibility with sessions generated before 1.8.0 + set this to 48. """ def __init__(self, request, id=None, invalidate_corrupt=False, use_cookies=True, type=None, data_dir=None, key='beaker.session.id', timeout=None, cookie_expires=True, - cookie_domain=None, cookie_path='/', secret=None, + cookie_domain=None, cookie_path='/', data_serializer='pickle', secret=None, secure=False, namespace_class=None, httponly=False, - encrypt_key=None, validate_key=None, **namespace_args): + encrypt_key=None, validate_key=None, encrypt_nonce_bits=DEFAULT_NONCE_BITS, + **namespace_args): if not type: if data_dir: self.type = 'file' @@ -126,6 +132,7 @@ self.timeout = timeout self.use_cookies = use_cookies self.cookie_expires = cookie_expires + self.data_serializer = data_serializer # Default cookie domain/path self._domain = cookie_domain @@ -136,6 +143,7 @@ self.httponly = httponly self.encrypt_key = encrypt_key self.validate_key = validate_key + self.encrypt_nonce_size = get_nonce_size(encrypt_nonce_bits) self.id = id self.accessed_dict = {} self.invalidate_corrupt = invalidate_corrupt @@ -148,7 +156,7 @@ except http_cookies.CookieError: self.cookie = SignedCookie(secret, input=None) else: - self.cookie = http_cookies.SimpleCookie(input=cookieheader) + self.cookie = SimpleCookie(input=cookieheader) if not self.id and self.key in self.cookie: self.id = self.cookie[self.key].value @@ -226,7 +234,7 @@ self.last_accessed = None if self.use_cookies: self._set_cookie_values() - sc = set_new == False + sc = set_new is False self._update_cookie_out(set_cookie=sc) @property @@ -257,13 +265,14 @@ """Serialize, encipher, and base64 the session dict""" session_data = session_data or self.copy() if self.encrypt_key: - nonce = b64encode(os.urandom(6))[:8] + nonce_len, nonce_b64len = self.encrypt_nonce_size + nonce = b64encode(os.urandom(nonce_len))[:nonce_b64len] encrypt_key = crypto.generateCryptoKeys(self.encrypt_key, self.validate_key + nonce, 1) - data = pickle.dumps(session_data, 2) + data = util.serialize(session_data, self.data_serializer) return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key)) else: - data = pickle.dumps(session_data, 2) + data = util.serialize(session_data, self.data_serializer) return b64encode(data) def _decrypt_data(self, session_data): @@ -271,10 +280,11 @@ dict""" if self.encrypt_key: try: - nonce = session_data[:8] + __, nonce_b64len = self.encrypt_nonce_size + nonce = session_data[:nonce_b64len] encrypt_key = crypto.generateCryptoKeys(self.encrypt_key, self.validate_key + nonce, 1) - payload = b64decode(session_data[8:]) + payload = b64decode(session_data[nonce_b64len:]) data = crypto.aesDecrypt(payload, encrypt_key) except: # As much as I hate a bare except, we get some insane errors @@ -284,16 +294,16 @@ return None else: raise - try: - return pickle.loads(data) - except: - if self.invalidate_corrupt: - return None - else: - raise else: data = b64decode(session_data) - return pickle.loads(data) + + try: + return util.deserialize(data, self.data_serializer) + except: + if self.invalidate_corrupt: + return None + else: + raise def _delete_cookie(self): self.request['set_cookie'] = True @@ -480,6 +490,8 @@ :param cookie_expires: Expiration date for cookie :param cookie_domain: Domain to use for the cookie. :param cookie_path: Path to use for the cookie. + :param data_serializer: If ``"json"`` or ``"pickle"`` should be used + to serialize data. By default ``pickle`` is used. :param secure: Whether or not the cookie should only be sent over SSL. :param httponly: Whether or not the cookie should only be accessible by the browser not by JavaScript. @@ -491,11 +503,12 @@ def __init__(self, request, key='beaker.session.id', timeout=None, cookie_expires=True, cookie_domain=None, cookie_path='/', encrypt_key=None, validate_key=None, secure=False, - httponly=False, **kwargs): + httponly=False, data_serializer='pickle', + encrypt_nonce_bits=DEFAULT_NONCE_BITS, **kwargs): if not crypto.has_aes and encrypt_key: raise InvalidCryptoBackendError("No AES library is installed, can't generate " - "encrypted cookie-only Session.") + "encrypted cookie-only Session.") self.request = request self.key = key @@ -503,11 +516,13 @@ self.cookie_expires = cookie_expires self.encrypt_key = encrypt_key self.validate_key = validate_key + self.encrypt_nonce_size = get_nonce_size(encrypt_nonce_bits) self.request['set_cookie'] = False self.secure = secure self.httponly = httponly self._domain = cookie_domain self._path = cookie_path + self.data_serializer = data_serializer try: cookieheader = request['cookie'] @@ -655,8 +670,7 @@ if params.get('type') == 'cookie': self.__dict__['_sess'] = CookieSession(req, **params) else: - self.__dict__['_sess'] = Session(req, use_cookies=True, - **params) + self.__dict__['_sess'] = Session(req, **params) return self.__dict__['_sess'] def __getattr__(self, attr): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/beaker/util.py new/Beaker-1.8.0/beaker/util.py --- old/Beaker-1.7.0/beaker/util.py 2015-02-25 16:37:38.000000000 +0100 +++ new/Beaker-1.8.0/beaker/util.py 2016-01-26 00:40:54.000000000 +0100 @@ -1,5 +1,6 @@ """Beaker utilities""" -from ._compat import PY2, string_type, unicode_text, NoneType, dictkeyslist, im_class, im_func +from ._compat import PY2, string_type, unicode_text, NoneType, dictkeyslist, im_class, im_func, pickle, func_signature, \ + default_im_func try: import threading as _threading @@ -15,7 +16,8 @@ import warnings import sys import inspect - +import json +import zlib from beaker.converters import asbool from beaker import exceptions @@ -24,7 +26,8 @@ DEFAULT_CACHE_KEY_LENGTH = 250 __all__ = ["ThreadLocal", "WeakValuedRegistry", "SyncDict", "encoded_path", - "verify_directory"] + "verify_directory", + "serialize", "deserialize"] def function_named(fn, name): @@ -87,8 +90,8 @@ def has_self_arg(func): """Return True if the given function has a 'self' argument.""" - args = inspect.getargspec(func) - if args and args[0] and args[0][0] in ('self', 'cls'): + args = list(func_signature(func).parameters) + if args and args[0] in ('self', 'cls'): return True else: return False @@ -285,31 +288,25 @@ def coerce_session_params(params): rules = [ - ('data_dir', (str, NoneType), "data_dir must be a string " - "referring to a directory."), - ('lock_dir', (str, NoneType), "lock_dir must be a string referring to a " - "directory."), + ('data_dir', (str, NoneType), "data_dir must be a string referring to a directory."), + ('lock_dir', (str, NoneType), "lock_dir must be a string referring to a directory."), ('type', (str, NoneType), "Session type must be a string."), - ('cookie_expires', (bool, datetime, timedelta, int), "Cookie expires was " - "not a boolean, datetime, int, or timedelta instance."), - ('cookie_domain', (str, NoneType), "Cookie domain must be a " - "string."), - ('cookie_path', (str, NoneType), "Cookie path must be a " - "string."), + ('cookie_expires', (bool, datetime, timedelta, int), + "Cookie expires was not a boolean, datetime, int, or timedelta instance."), + ('cookie_domain', (str, NoneType), "Cookie domain must be a string."), + ('cookie_path', (str, NoneType), "Cookie path must be a string."), ('id', (str,), "Session id must be a string."), ('key', (str,), "Session key must be a string."), ('secret', (str, NoneType), "Session secret must be a string."), - ('validate_key', (str, NoneType), "Session encrypt_key must be " - "a string."), - ('encrypt_key', (str, NoneType), "Session validate_key must be " - "a string."), + ('validate_key', (str, NoneType), "Session encrypt_key must be a string."), + ('encrypt_key', (str, NoneType), "Session validate_key must be a string."), + ('encrypt_nonce_bits', (int, NoneType), "Session encrypt_nonce_bits must be a number"), ('secure', (bool, NoneType), "Session secure must be a boolean."), ('httponly', (bool, NoneType), "Session httponly must be a boolean."), - ('timeout', (int, NoneType), "Session timeout must be an " - "integer."), + ('timeout', (int, NoneType), "Session timeout must be an integer."), ('auto', (bool, NoneType), "Session is created if accessed."), - ('webtest_varname', (str, NoneType), "Session varname must be " - "a string."), + ('webtest_varname', (str, NoneType), "Session varname must be a string."), + ('data_serializer', (str,), "data_serializer must be a string.") ] opts = verify_rules(params, rules) cookie_expires = opts.get('cookie_expires') @@ -321,19 +318,16 @@ def coerce_cache_params(params): rules = [ - ('data_dir', (str, NoneType), "data_dir must be a string " - "referring to a directory."), - ('lock_dir', (str, NoneType), "lock_dir must be a string referring to a " - "directory."), + ('data_dir', (str, NoneType), "data_dir must be a string referring to a directory."), + ('lock_dir', (str, NoneType), "lock_dir must be a string referring to a directory."), ('type', (str,), "Cache type must be a string."), - ('enabled', (bool, NoneType), "enabled must be true/false " - "if present."), - ('expire', (int, NoneType), "expire must be an integer representing " - "how many seconds the cache is valid for"), - ('regions', (list, tuple, NoneType), "Regions must be a " - "comma seperated list of valid regions"), - ('key_length', (int, NoneType), "key_length must be an integer " - "which indicates the longest a key can be before hashing"), + ('enabled', (bool, NoneType), "enabled must be true/false if present."), + ('expire', (int, NoneType), + "expire must be an integer representing how many seconds the cache is valid for"), + ('regions', (list, tuple, NoneType), + "Regions must be a comma seperated list of valid regions"), + ('key_length', (int, NoneType), + "key_length must be an integer which indicates the longest a key can be before hashing"), ] return verify_rules(params, rules) @@ -344,37 +338,31 @@ ('no_block', (bool, int), 'no_block must be a boolean or an integer'), ('receive_timeout', (int,), 'receive_timeout must be an integer'), ('send_timeout', (int,), 'send_timeout must be an integer'), - ('ketama_hash', (str,), 'ketama_hash must be a string designating ' - 'a valid hashing strategy option'), + ('ketama_hash', (str,), + 'ketama_hash must be a string designating a valid hashing strategy option'), ('_poll_timeout', (int,), '_poll_timeout must be an integer'), ('auto_eject', (bool, int), 'auto_eject must be an integer'), ('retry_timeout', (int,), 'retry_timeout must be an integer'), ('_sort_hosts', (bool, int), '_sort_hosts must be an integer'), ('_io_msg_watermark', (int,), '_io_msg_watermark must be an integer'), ('ketama', (bool, int), 'ketama must be a boolean or an integer'), - ('ketama_weighted', (bool, int), 'ketama_weighted must be a boolean or ' - 'an integer'), - ('_io_key_prefetch', (int, bool), '_io_key_prefetch must be a boolean ' - 'or an integer'), - ('_hash_with_prefix_key', (bool, int), '_hash_with_prefix_key must be ' - 'a boolean or an integer'), - ('tcp_nodelay', (bool, int), 'tcp_nodelay must be a boolean or an ' - 'integer'), + ('ketama_weighted', (bool, int), 'ketama_weighted must be a boolean or an integer'), + ('_io_key_prefetch', (int, bool), '_io_key_prefetch must be a boolean or an integer'), + ('_hash_with_prefix_key', (bool, int), + '_hash_with_prefix_key must be a boolean or an integer'), + ('tcp_nodelay', (bool, int), 'tcp_nodelay must be a boolean or an integer'), ('failure_limit', (int,), 'failure_limit must be an integer'), - ('buffer_requests', (bool, int), 'buffer_requests must be a boolean ' - 'or an integer'), + ('buffer_requests', (bool, int), 'buffer_requests must be a boolean or an integer'), ('_socket_send_size', (int,), '_socket_send_size must be an integer'), ('num_replicas', (int,), 'num_replicas must be an integer'), ('remove_failed', (int,), 'remove_failed must be an integer'), ('_noreply', (bool, int), '_noreply must be a boolean or an integer'), - ('_io_bytes_watermark', (int,), '_io_bytes_watermark must be an ' - 'integer'), + ('_io_bytes_watermark', (int,), '_io_bytes_watermark must be an integer'), ('_socket_recv_size', (int,), '_socket_recv_size must be an integer'), - ('distribution', (str,), 'distribution must be a string designating ' - 'a valid distribution option'), + ('distribution', (str,), + 'distribution must be a string designating a valid distribution option'), ('connect_timeout', (int,), 'connect_timeout must be an integer'), - ('hash', (str,), 'hash must be a string designating a valid hashing ' - 'option'), + ('hash', (str,), 'hash must be a string designating a valid hashing option'), ('verify_keys', (bool, int), 'verify_keys must be a boolean or an integer'), ('dead_timeout', (int,), 'dead_timeout must be an integer') ] @@ -452,3 +440,17 @@ return '%s.%s' % (kls.__module__, kls.__name__) else: return '%s|%s' % (inspect.getsourcefile(func), func.__name__) + + +def serialize(data, method): + if method == 'json': + return zlib.compress(json.dumps(data).encode('utf-8')) + else: + return pickle.dumps(data, 2) + + +def deserialize(data_string, method): + if method == 'json': + return json.loads(zlib.decompress(data_string).decode('utf-8')) + else: + return pickle.loads(data_string) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/setup.cfg new/Beaker-1.8.0/setup.cfg --- old/Beaker-1.7.0/setup.cfg 2015-04-22 17:32:50.000000000 +0200 +++ new/Beaker-1.8.0/setup.cfg 2016-01-27 12:40:12.000000000 +0100 @@ -5,6 +5,7 @@ with-doctest = True cover-package = beaker cover-inclusive = True +ignore-files = annotated_functions.py [egg_info] tag_build = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Beaker-1.7.0/setup.py new/Beaker-1.8.0/setup.py --- old/Beaker-1.7.0/setup.py 2015-02-25 16:37:38.000000000 +0100 +++ new/Beaker-1.8.0/setup.py 2016-01-26 00:08:52.000000000 +0100 @@ -1,9 +1,11 @@ import os import sys import re +import inspect from setuptools import setup, find_packages +py_version = sys.version_info[:2] here = os.path.abspath(os.path.dirname(__file__)) v = open(os.path.join(here, 'beaker', '__init__.py')) VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(v.read()).group(1) @@ -14,14 +16,26 @@ except IOError: README = '' -tests_require = ['nose', 'webtest', 'Mock', 'coverage', 'pycrypto'] + +INSTALL_REQUIRES = [] +if not hasattr(inspect, 'signature'): + # On Python 2.6, 2.7 and 3.2 we need funcsigs dependency + INSTALL_REQUIRES.append('funcsigs') + + +TESTS_REQUIRE = ['nose', 'webtest', 'Mock', 'pycrypto'] + +if py_version == (3, 2): + TESTS_REQUIRE.append('coverage < 4.0') +else: + TESTS_REQUIRE.append('coverage') if not sys.platform.startswith('java') and not sys.platform == 'cli': - tests_require.extend(['SQLALchemy']) + TESTS_REQUIRE.extend(['SQLALchemy']) try: import sqlite3 except ImportError: - tests_require.append('pysqlite') + TESTS_REQUIRE.append('pysqlite') setup(name='Beaker', version=VERSION, @@ -39,25 +53,26 @@ 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Internet :: WWW/HTTP :: WSGI', 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', ], keywords='wsgi myghty session web cache middleware', - author='Ben Bangert, Mike Bayer, Philip Jenvey', - author_email='b...@groovie.org, pjen...@groovie.org', + author='Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina', + author_email='b...@groovie.org, pjen...@groovie.org, a...@turbogears.org', url='http://beaker.rtfd.org/', license='BSD', packages=find_packages(exclude=['ez_setup', 'examples', 'tests', 'tests.*']), zip_safe=False, - install_requires=[], + install_requires=INSTALL_REQUIRES, extras_require={ 'crypto': ['pycryptopp>=0.5.12'], 'pycrypto': ['pycrypto'], - 'testsuite': [tests_require] + 'testsuite': [TESTS_REQUIRE] }, test_suite='nose.collector', - tests_require=tests_require, + tests_require=TESTS_REQUIRE, entry_points=""" [paste.filter_factory] beaker_session = beaker.middleware:session_filter_factory