[Python-modules-commits] [beaker] 01/03: Import beaker_1.8.1.orig.tar.gz
Piotr Ożarowski
piotr at moszumanska.debian.org
Sat Nov 5 19:07:25 UTC 2016
This is an automated email from the git hooks/post-receive script.
piotr pushed a commit to branch master
in repository beaker.
commit 3f7edc65277801d8d4f41c0031ef07db613bc1f9
Author: Piotr Ożarowski <piotr at debian.org>
Date: Sat Nov 5 20:02:26 2016 +0100
Import beaker_1.8.1.orig.tar.gz
---
Beaker.egg-info/PKG-INFO | 4 +-
PKG-INFO | 4 +-
beaker/__init__.py | 2 +-
beaker/cache.py | 4 +-
beaker/middleware.py | 5 +-
beaker/session.py | 228 +++++++++++++++++++++++++++++++----------------
beaker/util.py | 32 ++++++-
setup.py | 2 +-
8 files changed, 190 insertions(+), 91 deletions(-)
diff --git a/Beaker.egg-info/PKG-INFO b/Beaker.egg-info/PKG-INFO
index 680c2bf..a12725a 100644
--- a/Beaker.egg-info/PKG-INFO
+++ b/Beaker.egg-info/PKG-INFO
@@ -1,8 +1,8 @@
Metadata-Version: 1.1
Name: Beaker
-Version: 1.8.0
+Version: 1.8.1
Summary: A Session and Caching library with WSGI Middleware
-Home-page: http://beaker.rtfd.org/
+Home-page: https://beaker.readthedocs.io/
Author: Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina
Author-email: ben at groovie.org, pjenvey at groovie.org, amol at turbogears.org
License: BSD
diff --git a/PKG-INFO b/PKG-INFO
index 680c2bf..a12725a 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,8 +1,8 @@
Metadata-Version: 1.1
Name: Beaker
-Version: 1.8.0
+Version: 1.8.1
Summary: A Session and Caching library with WSGI Middleware
-Home-page: http://beaker.rtfd.org/
+Home-page: https://beaker.readthedocs.io/
Author: Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina
Author-email: ben at groovie.org, pjenvey at groovie.org, amol at turbogears.org
License: BSD
diff --git a/beaker/__init__.py b/beaker/__init__.py
index b280975..e8b6b09 100644
--- a/beaker/__init__.py
+++ b/beaker/__init__.py
@@ -1 +1 @@
-__version__ = '1.8.0'
+__version__ = '1.8.1'
diff --git a/beaker/cache.py b/beaker/cache.py
index cbb071d..dd8645f 100644
--- a/beaker/cache.py
+++ b/beaker/cache.py
@@ -99,9 +99,9 @@ class _backends(object):
if not isinstance(sys.exc_info()[1], DistributionNotFound):
import traceback
try:
- from io import StringIO
+ from StringIO import StringIO # Python2
except ImportError:
- from StringIO import StringIO
+ from io import StringIO # Python3
tb = StringIO()
traceback.print_exc(file=tb)
diff --git a/beaker/middleware.py b/beaker/middleware.py
index ecdd567..319f2b7 100644
--- a/beaker/middleware.py
+++ b/beaker/middleware.py
@@ -106,8 +106,9 @@ class SessionMiddleware(object):
# Load up the default params
self.options = dict(invalidate_corrupt=True, type=None,
- data_dir=None, key='beaker.session.id',
- timeout=None, secret=None, log_file=None)
+ data_dir=None, key='beaker.session.id',
+ timeout=None, save_accessed_time=True, secret=None,
+ log_file=None)
# Pull out any config args meant for beaker session. if there are any
for dct in [config, kwargs]:
diff --git a/beaker/session.py b/beaker/session.py
index cbd7694..86c9d70 100644
--- a/beaker/session.py
+++ b/beaker/session.py
@@ -1,4 +1,4 @@
-from ._compat import PY2, pickle, http_cookies, unicode_text, b64encode, b64decode
+from ._compat import PY2, pickle, http_cookies, unicode_text, b64encode, b64decode, string_type
import os
import time
@@ -9,7 +9,19 @@ from beaker.cache import clsmap
from beaker.exceptions import BeakerException, InvalidCryptoBackendError
from beaker.cookie import SimpleCookie
-__all__ = ['SignedCookie', 'Session']
+__all__ = ['SignedCookie', 'Session', 'InvalidSignature']
+
+
+class _InvalidSignatureType(object):
+ """Returned from SignedCookie when the value's signature was invalid."""
+ def __nonzero__(self):
+ return False
+
+ def __bool__(self):
+ return False
+
+
+InvalidSignature = _InvalidSignatureType()
try:
@@ -50,19 +62,22 @@ class SignedCookie(SimpleCookie):
def value_decode(self, val):
val = val.strip('"')
+ if not val:
+ return None, val
+
sig = HMAC.new(self.secret, val[40:].encode('utf-8'), SHA1).hexdigest()
# Avoid timing attacks
invalid_bits = 0
input_sig = val[:40]
if len(sig) != len(input_sig):
- return None, val
+ return InvalidSignature, val
for a, b in zip(sig, input_sig):
invalid_bits += a != b
if invalid_bits:
- return None, val
+ return InvalidSignature, val
else:
return val[40:], val
@@ -88,13 +103,19 @@ class Session(dict):
:param key: The name the cookie should be set to.
:param timeout: How long session data is considered valid. This is used
regardless of the cookie being present or not to determine
- whether session data is still valid.
- :type timeout: int
+ whether session data is still valid. Can be set to None to
+ disable session time out.
+ :type timeout: int or None
+ :param save_accessed_time: Whether beaker should save the session's access
+ time (True) or only modification time (False).
+ Defaults to True.
: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.
+ to serialize data. Can also be an object with
+ ``loads` and ``dumps`` methods. 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.
@@ -108,8 +129,9 @@ class Session(dict):
"""
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='/', data_serializer='pickle', secret=None,
+ key='beaker.session.id', timeout=None, save_accessed_time=True,
+ cookie_expires=True, cookie_domain=None, cookie_path='/',
+ data_serializer='pickle', secret=None,
secure=False, namespace_class=None, httponly=False,
encrypt_key=None, validate_key=None, encrypt_nonce_bits=DEFAULT_NONCE_BITS,
**namespace_args):
@@ -129,10 +151,14 @@ class Session(dict):
self.data_dir = data_dir
self.key = key
+ if timeout and not save_accessed_time:
+ raise BeakerException("timeout requires save_accessed_time")
self.timeout = timeout
+ self.save_atime = save_accessed_time
self.use_cookies = use_cookies
self.cookie_expires = cookie_expires
- self.data_serializer = data_serializer
+
+ self._set_serializer(data_serializer)
# Default cookie domain/path
self._domain = cookie_domain
@@ -152,14 +178,24 @@ class Session(dict):
cookieheader = request.get('cookie', '')
if secret:
try:
- self.cookie = SignedCookie(secret, input=cookieheader)
+ self.cookie = SignedCookie(
+ secret,
+ input=cookieheader,
+ )
except http_cookies.CookieError:
- self.cookie = SignedCookie(secret, input=None)
+ self.cookie = SignedCookie(
+ secret,
+ input=None,
+ )
else:
self.cookie = SimpleCookie(input=cookieheader)
if not self.id and self.key in self.cookie:
- self.id = self.cookie[self.key].value
+ cookie_data = self.cookie[self.key].value
+ # Should we check invalidate_corrupt here?
+ if cookie_data is InvalidSignature:
+ cookie_data = None
+ self.id = cookie_data
self.is_new = self.id is None
if self.is_new:
@@ -169,7 +205,7 @@ class Session(dict):
try:
self.load()
except Exception as e:
- if invalidate_corrupt:
+ if self.invalidate_corrupt:
util.warn(
"Invalidating corrupt session %s; "
"error was: %s. Set invalidate_corrupt=False "
@@ -178,6 +214,17 @@ class Session(dict):
else:
raise
+ def _set_serializer(self, data_serializer):
+ self.data_serializer = data_serializer
+ if self.data_serializer == 'json':
+ self.serializer = util.JsonSerializer()
+ elif self.data_serializer == 'pickle':
+ self.serializer = util.PickleSerializer()
+ elif isinstance(self.data_serializer, string_type):
+ raise BeakerException('Invalid value for data_serializer: %s' % data_serializer)
+ else:
+ self.serializer = data_serializer
+
def has_key(self, name):
return name in self
@@ -194,24 +241,25 @@ class Session(dict):
def _set_cookie_expires(self, expires):
if expires is None:
- if self.cookie_expires is not True:
- if self.cookie_expires is False:
- expires = datetime.fromtimestamp(0x7FFFFFFF)
- elif isinstance(self.cookie_expires, timedelta):
- expires = datetime.utcnow() + self.cookie_expires
- elif isinstance(self.cookie_expires, datetime):
- expires = self.cookie_expires
- else:
- raise ValueError("Invalid argument for cookie_expires: %s"
- % repr(self.cookie_expires))
- else:
- expires = None
- if expires is not None:
- if not self.cookie or self.key not in self.cookie:
- self.cookie[self.key] = self.id
- self.cookie[self.key]['expires'] = \
- expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT")
- return expires
+ expires = self.cookie_expires
+ if expires is False:
+ expires_date = datetime.fromtimestamp(0x7FFFFFFF)
+ elif isinstance(expires, timedelta):
+ expires_date = datetime.utcnow() + expires
+ elif isinstance(expires, datetime):
+ expires_date = expires
+ elif expires is not True:
+ raise ValueError("Invalid argument for cookie_expires: %s"
+ % repr(self.cookie_expires))
+ self.cookie_expires = expires
+ if not self.cookie or self.key not in self.cookie:
+ self.cookie[self.key] = self.id
+ if expires is True:
+ self.cookie[self.key]['expires'] = ''
+ return True
+ self.cookie[self.key]['expires'] = \
+ expires_date.strftime("%a, %d-%b-%Y %H:%M:%S GMT")
+ return expires_date
def _update_cookie_out(self, set_cookie=True):
self.request['cookie_out'] = self.cookie[self.key].output(header='')
@@ -269,41 +317,26 @@ class Session(dict):
nonce = b64encode(os.urandom(nonce_len))[:nonce_b64len]
encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
self.validate_key + nonce, 1)
- data = util.serialize(session_data, self.data_serializer)
+ data = self.serializer.dumps(session_data)
return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key))
else:
- data = util.serialize(session_data, self.data_serializer)
+ data = self.serializer.dumps(session_data)
return b64encode(data)
def _decrypt_data(self, session_data):
"""Bas64, decipher, then un-serialize the data for the session
dict"""
if self.encrypt_key:
- try:
- __, 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[nonce_b64len:])
- data = crypto.aesDecrypt(payload, encrypt_key)
- except:
- # As much as I hate a bare except, we get some insane errors
- # here that get tossed when crypto fails, so we raise the
- # 'right' exception
- if self.invalidate_corrupt:
- return None
- else:
- raise
+ __, 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[nonce_b64len:])
+ data = crypto.aesDecrypt(payload, encrypt_key)
else:
data = b64decode(session_data)
- try:
- return util.deserialize(data, self.data_serializer)
- except:
- if self.invalidate_corrupt:
- return None
- else:
- raise
+ return self.serializer.loads(data)
def _delete_cookie(self):
self.request['set_cookie'] = True
@@ -402,7 +435,7 @@ class Session(dict):
"""
# Look to see if its a new session that was only accessed
# Don't save it under that case
- if accessed_only and self.is_new:
+ if accessed_only and (self.is_new or not self.save_atime):
return None
# this session might not have a namespace yet or the session id
@@ -487,24 +520,34 @@ class CookieSession(Session):
regardless of the cookie being present or not to determine
whether session data is still valid.
:type timeout: int
+ :param save_accessed_time: Whether beaker should save the session's access
+ time (True) or only modification time (False).
+ Defaults to True.
: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.
+ to serialize data. Can also be an object with
+ ``loads` and ``dumps`` methods. 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 invalidate_corrupt: How to handle corrupt data when loading. When
+ set to True, then corrupt data will be silently
+ invalidated and a new session created,
+ otherwise invalid data will cause an exception.
+ :type invalidate_corrupt: bool
"""
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,
+ save_accessed_time=True, cookie_expires=True, cookie_domain=None,
+ cookie_path='/', encrypt_key=None, validate_key=None, secure=False,
httponly=False, data_serializer='pickle',
- encrypt_nonce_bits=DEFAULT_NONCE_BITS, **kwargs):
+ encrypt_nonce_bits=DEFAULT_NONCE_BITS, invalidate_corrupt=False,
+ **kwargs):
if not crypto.has_aes and encrypt_key:
raise InvalidCryptoBackendError("No AES library is installed, can't generate "
@@ -513,6 +556,7 @@ class CookieSession(Session):
self.request = request
self.key = key
self.timeout = timeout
+ self.save_atime = save_accessed_time
self.cookie_expires = cookie_expires
self.encrypt_key = encrypt_key
self.validate_key = validate_key
@@ -522,7 +566,8 @@ class CookieSession(Session):
self.httponly = httponly
self._domain = cookie_domain
self._path = cookie_path
- self.data_serializer = data_serializer
+ self.invalidate_corrupt = invalidate_corrupt
+ self._set_serializer(data_serializer)
try:
cookieheader = request['cookie']
@@ -532,11 +577,19 @@ class CookieSession(Session):
if validate_key is None:
raise BeakerException("No validate_key specified for Cookie only "
"Session.")
+ if timeout and not save_accessed_time:
+ raise BeakerException("timeout requires save_accessed_time")
try:
- self.cookie = SignedCookie(validate_key, input=cookieheader)
+ self.cookie = SignedCookie(
+ validate_key,
+ input=cookieheader,
+ )
except http_cookies.CookieError:
- self.cookie = SignedCookie(validate_key, input=None)
+ self.cookie = SignedCookie(
+ validate_key,
+ input=None,
+ )
self['_id'] = _session_id()
self.is_new = True
@@ -546,10 +599,19 @@ class CookieSession(Session):
self.is_new = False
try:
cookie_data = self.cookie[self.key].value
+ if cookie_data is InvalidSignature:
+ raise BeakerException("Invalid signature")
self.update(self._decrypt_data(cookie_data))
self._path = self.get('_path', '/')
- except:
- pass
+ except Exception as e:
+ if self.invalidate_corrupt:
+ util.warn(
+ "Invalidating corrupt session %s; "
+ "error was: %s. Set invalidate_corrupt=False "
+ "to propagate this exception." % (self.id, e))
+ self.invalidate()
+ else:
+ raise
if self.timeout is not None:
now = time.time()
@@ -587,7 +649,7 @@ class CookieSession(Session):
def save(self, accessed_only=False):
"""Saves the data for this session to persistent storage"""
- if accessed_only and self.is_new:
+ if accessed_only and (self.is_new or not self.save_atime):
return
if accessed_only:
self.clear()
@@ -667,10 +729,16 @@ class SessionObject(object):
environ = self.__dict__['_environ']
self.__dict__['_headers'] = req = {'cookie_out': None}
req['cookie'] = environ.get('HTTP_COOKIE')
- if params.get('type') == 'cookie':
- self.__dict__['_sess'] = CookieSession(req, **params)
+ session_cls = params.get('session_class', None)
+ if session_cls is None:
+ if params.get('type') == 'cookie':
+ session_cls = CookieSession
+ else:
+ session_cls = Session
else:
- self.__dict__['_sess'] = Session(req, **params)
+ assert issubclass(session_cls, Session),\
+ "Not a Session: " + session_cls
+ self.__dict__['_sess'] = session_cls(req, **params)
return self.__dict__['_sess']
def __getattr__(self, attr):
@@ -722,21 +790,27 @@ class SessionObject(object):
def persist(self):
"""Persist the session to the storage
- If its set to autosave, then the entire session will be saved
- regardless of if save() has been called. Otherwise, just the
- accessed time will be updated if save() was not called, or
- the session will be saved if save() was called.
+ Always saves the whole session if save() or delete() have been called.
+ If they haven't:
+ - If autosave is set to true, saves the the entire session regardless.
+ - If save_accessed_time is set to true or unset, only saves the updated
+ access time.
+ - If save_accessed_time is set to false, doesn't save anything.
"""
if self.__dict__['_params'].get('auto'):
self._session().save()
- else:
- if self.__dict__.get('_dirty'):
+ elif self.__dict__['_params'].get('save_accessed_time', True):
+ if self.dirty():
self._session().save()
else:
self._session().save(accessed_only=True)
+ else: # save_accessed_time is false
+ if self.dirty():
+ self._session().save()
def dirty(self):
+ """Returns True if save() or delete() have been called"""
return self.__dict__.get('_dirty', False)
def accessed(self):
diff --git a/beaker/util.py b/beaker/util.py
index 35f0441..4e26594 100644
--- a/beaker/util.py
+++ b/beaker/util.py
@@ -304,6 +304,8 @@ def coerce_session_params(params):
('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."),
+ ('save_accessed_time', (bool, NoneType),
+ "Session save_accessed_time must be a boolean (defaults to true)."),
('auto', (bool, NoneType), "Session is created if accessed."),
('webtest_varname', (str, NoneType), "Session varname must be a string."),
('data_serializer', (str,), "data_serializer must be a string.")
@@ -313,6 +315,10 @@ def coerce_session_params(params):
if cookie_expires and isinstance(cookie_expires, int) and \
not isinstance(cookie_expires, bool):
opts['cookie_expires'] = timedelta(seconds=cookie_expires)
+
+ if opts.get('timeout') is not None and not opts.get('save_accessed_time', True):
+ raise Exception("save_accessed_time must be true to use timeout")
+
return opts
@@ -442,15 +448,33 @@ def func_namespace(func):
return '%s|%s' % (inspect.getsourcefile(func), func.__name__)
+class PickleSerializer(object):
+ def loads(self, data_string):
+ return pickle.loads(data_string)
+
+ def dumps(self, data):
+ return pickle.dumps(data, 2)
+
+
+class JsonSerializer(object):
+ def loads(self, data_string):
+ return json.loads(zlib.decompress(data_string).decode('utf-8'))
+
+ def dumps(self, data):
+ return zlib.compress(json.dumps(data).encode('utf-8'))
+
+
def serialize(data, method):
if method == 'json':
- return zlib.compress(json.dumps(data).encode('utf-8'))
+ serializer = JsonSerializer()
else:
- return pickle.dumps(data, 2)
+ serializer = PickleSerializer()
+ return serializer.dumps(data)
def deserialize(data_string, method):
if method == 'json':
- return json.loads(zlib.decompress(data_string).decode('utf-8'))
+ serializer = JsonSerializer()
else:
- return pickle.loads(data_string)
+ serializer = PickleSerializer()
+ return serializer.loads(data_string)
diff --git a/setup.py b/setup.py
index af01ea7..f5d7080 100644
--- a/setup.py
+++ b/setup.py
@@ -61,7 +61,7 @@ setup(name='Beaker',
keywords='wsgi myghty session web cache middleware',
author='Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina',
author_email='ben at groovie.org, pjenvey at groovie.org, amol at turbogears.org',
- url='http://beaker.rtfd.org/',
+ url='https://beaker.readthedocs.io/',
license='BSD',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests', 'tests.*']),
zip_safe=False,
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/beaker.git
More information about the Python-modules-commits
mailing list