[Python-modules-commits] [beaker] 01/05: Import beaker_1.9.0.orig.tar.gz
Piotr Ożarowski
piotr at moszumanska.debian.org
Mon Jul 3 21:53:59 UTC 2017
This is an automated email from the git hooks/post-receive script.
piotr pushed a commit to branch master
in repository beaker.
commit 016ce688c4d668329c14c3ee896ee97bb8929b15
Author: Piotr Ożarowski <piotr at debian.org>
Date: Mon Jul 3 21:28:48 2017 +0200
Import beaker_1.9.0.orig.tar.gz
---
Beaker.egg-info/PKG-INFO | 2 +-
Beaker.egg-info/SOURCES.txt | 4 +
Beaker.egg-info/requires.txt | 27 +++++--
PKG-INFO | 2 +-
beaker/__init__.py | 2 +-
beaker/_compat.py | 1 +
beaker/cache.py | 20 +++--
beaker/container.py | 3 +-
beaker/crypto/__init__.py | 77 ++++++++++++------
beaker/crypto/jcecrypto.py | 9 +++
beaker/crypto/noencryption.py | 12 +++
beaker/crypto/pyca_cryptography.py | 52 ++++++++++++
beaker/crypto/pycrypto.py | 2 +-
beaker/ext/mongodb.py | 161 +++++++++++++++++++++++++++++++++++++
beaker/ext/redisnm.py | 129 +++++++++++++++++++++++++++++
beaker/session.py | 33 +++++---
beaker/synchronization.py | 4 +-
beaker/util.py | 16 +++-
setup.py | 15 +++-
19 files changed, 509 insertions(+), 62 deletions(-)
diff --git a/Beaker.egg-info/PKG-INFO b/Beaker.egg-info/PKG-INFO
index a12725a..53f7379 100644
--- a/Beaker.egg-info/PKG-INFO
+++ b/Beaker.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: Beaker
-Version: 1.8.1
+Version: 1.9.0
Summary: A Session and Caching library with WSGI Middleware
Home-page: https://beaker.readthedocs.io/
Author: Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina
diff --git a/Beaker.egg-info/SOURCES.txt b/Beaker.egg-info/SOURCES.txt
index 09706d3..06eea23 100644
--- a/Beaker.egg-info/SOURCES.txt
+++ b/Beaker.egg-info/SOURCES.txt
@@ -22,12 +22,16 @@ beaker/synchronization.py
beaker/util.py
beaker/crypto/__init__.py
beaker/crypto/jcecrypto.py
+beaker/crypto/noencryption.py
beaker/crypto/nsscrypto.py
beaker/crypto/pbkdf2.py
+beaker/crypto/pyca_cryptography.py
beaker/crypto/pycrypto.py
beaker/crypto/util.py
beaker/ext/__init__.py
beaker/ext/database.py
beaker/ext/google.py
beaker/ext/memcached.py
+beaker/ext/mongodb.py
+beaker/ext/redisnm.py
beaker/ext/sqla.py
\ No newline at end of file
diff --git a/Beaker.egg-info/requires.txt b/Beaker.egg-info/requires.txt
index d36d58f..941a6bf 100644
--- a/Beaker.egg-info/requires.txt
+++ b/Beaker.egg-info/requires.txt
@@ -1,15 +1,26 @@
funcsigs
+[crypto]
+pycryptopp>=0.5.12
+
+[cryptography]
+cryptography
+
+[pycrypto]
+pycrypto
+
+[pycryptodome]
+pycryptodome
+
[testsuite]
nose
-webtest
Mock
-pycrypto
+pycryptodome
+cryptography
+webtest
coverage
SQLALchemy
-
-[crypto]
-pycryptopp>=0.5.12
-
-[pycrypto]
-pycrypto
\ No newline at end of file
+pymongo
+redis
+pylibmc
+python-memcached
diff --git a/PKG-INFO b/PKG-INFO
index a12725a..53f7379 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: Beaker
-Version: 1.8.1
+Version: 1.9.0
Summary: A Session and Caching library with WSGI Middleware
Home-page: https://beaker.readthedocs.io/
Author: Ben Bangert, Mike Bayer, Philip Jenvey, Alessandro Molina
diff --git a/beaker/__init__.py b/beaker/__init__.py
index e8b6b09..e5102d3 100644
--- a/beaker/__init__.py
+++ b/beaker/__init__.py
@@ -1 +1 @@
-__version__ = '1.8.1'
+__version__ = '1.9.0'
diff --git a/beaker/_compat.py b/beaker/_compat.py
index 1f85d5f..d7bac17 100644
--- a/beaker/_compat.py
+++ b/beaker/_compat.py
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
import sys
# True if we are running on Python 2.
diff --git a/beaker/cache.py b/beaker/cache.py
index dd8645f..73893b9 100644
--- a/beaker/cache.py
+++ b/beaker/cache.py
@@ -20,6 +20,8 @@ import beaker.ext.memcached as memcached
import beaker.ext.database as database
import beaker.ext.sqla as sqla
import beaker.ext.google as google
+import beaker.ext.mongodb as mongodb
+import beaker.ext.redisnm as redisnm
from functools import wraps
# Initialize the cache region dict
@@ -116,14 +118,16 @@ class _backends(object):
# Initialize the basic available backends
clsmap = _backends({
- 'memory': container.MemoryNamespaceManager,
- 'dbm': container.DBMNamespaceManager,
- 'file': container.FileNamespaceManager,
- 'ext:memcached': memcached.MemcachedNamespaceManager,
- 'ext:database': database.DatabaseNamespaceManager,
- 'ext:sqla': sqla.SqlaNamespaceManager,
- 'ext:google': google.GoogleNamespaceManager,
- })
+ 'memory': container.MemoryNamespaceManager,
+ 'dbm': container.DBMNamespaceManager,
+ 'file': container.FileNamespaceManager,
+ 'ext:memcached': memcached.MemcachedNamespaceManager,
+ 'ext:database': database.DatabaseNamespaceManager,
+ 'ext:sqla': sqla.SqlaNamespaceManager,
+ 'ext:google': google.GoogleNamespaceManager,
+ 'ext:mongodb': mongodb.MongoNamespaceManager,
+ 'ext:redis': redisnm.RedisNamespaceManager
+})
def cache_region(region, *args):
diff --git a/beaker/container.py b/beaker/container.py
index 5971892..f0a7d19 100644
--- a/beaker/container.py
+++ b/beaker/container.py
@@ -409,7 +409,8 @@ class Value(object):
if storedtime is None:
storedtime = time.time()
debug("set_value stored time %r expire time %r", storedtime, self.expire_argument)
- self.namespace.set_value(self.key, (storedtime, self.expire_argument, value))
+ self.namespace.set_value(self.key, (storedtime, self.expire_argument, value),
+ expiretime=self.expire_argument)
finally:
self.namespace.release_write_lock()
diff --git a/beaker/crypto/__init__.py b/beaker/crypto/__init__.py
index 9d1679f..84bc258 100644
--- a/beaker/crypto/__init__.py
+++ b/beaker/crypto/__init__.py
@@ -1,48 +1,77 @@
from .._compat import JYTHON
-from warnings import warn
from beaker.crypto.pbkdf2 import pbkdf2
from beaker.crypto.util import hmac, sha1, hmac_sha1, md5
from beaker import util
+from beaker.exceptions import InvalidCryptoBackendError
keyLength = None
DEFAULT_NONCE_BITS = 128
-if JYTHON:
- try:
- from beaker.crypto.jcecrypto import getKeyLength, aesEncrypt
- keyLength = getKeyLength()
- except ImportError:
- pass
-else:
- try:
- from beaker.crypto.nsscrypto import getKeyLength, aesEncrypt, aesDecrypt
- keyLength = getKeyLength()
- except ImportError:
+CRYPTO_MODULES = {}
+
+
+def load_default_module():
+ """ Load the default crypto module
+ """
+ if JYTHON:
try:
- from beaker.crypto.pycrypto import getKeyLength, aesEncrypt, aesDecrypt
- keyLength = getKeyLength()
+ from beaker.crypto import jcecrypto
+ return jcecrypto
except ImportError:
pass
+ else:
+ try:
+ from beaker.crypto import nsscrypto
+ return nsscrypto
+ except ImportError:
+ try:
+ from beaker.crypto import pycrypto
+ return pycrypto
+ except ImportError:
+ pass
+ from beaker.crypto import noencryption
+ return noencryption
+
+
+def register_crypto_module(name, mod):
+ """
+ Register the given module under the name given.
+ """
+ CRYPTO_MODULES[name] = mod
+
+
+def get_crypto_module(name):
+ """
+ Get the active crypto module for this name
+ """
+ if name not in CRYPTO_MODULES:
+ if name == 'default':
+ register_crypto_module('default', load_default_module())
+ elif name == 'nss':
+ from beaker.crypto import nsscrypto
+ register_crypto_module(name, nsscrypto)
+ elif name == 'pycrypto':
+ from beaker.crypto import pycrypto
+ register_crypto_module(name, pycrypto)
+ elif name == 'cryptography':
+ from beaker.crypto import pyca_cryptography
+ register_crypto_module(name, pyca_cryptography)
+ else:
+ raise InvalidCryptoBackendError(
+ "No crypto backend with name '%s' is registered." % name)
-if not keyLength:
- has_aes = False
-else:
- has_aes = True
+ return CRYPTO_MODULES[name]
-if has_aes and keyLength < 32:
- warn('Crypto implementation only supports key lengths up to %d bits. '
- 'Generated session cookies may be incompatible with other '
- 'environments' % (keyLength * 8))
-def generateCryptoKeys(master_key, salt, iterations):
+def generateCryptoKeys(master_key, salt, iterations, keylen):
# NB: We XOR parts of the keystream into the randomly-generated parts, just
# in case os.urandom() isn't as random as it should be. Note that if
# os.urandom() returns truly random data, this will have no effect on the
# overall security.
- return pbkdf2(master_key, salt, iterations=iterations, dklen=keyLength)
+ return pbkdf2(master_key, salt, iterations=iterations, dklen=keylen)
def get_nonce_size(number_of_bits):
diff --git a/beaker/crypto/jcecrypto.py b/beaker/crypto/jcecrypto.py
index ce313d6..dc070c7 100644
--- a/beaker/crypto/jcecrypto.py
+++ b/beaker/crypto/jcecrypto.py
@@ -8,6 +8,8 @@ generated by pycryptopp, which has no such restrictions. To fix this,
download the "Unlimited Strength Jurisdiction Policy Files" from Sun,
which will allow encryption using 256 bit AES keys.
"""
+from warnings import warn
+
from javax.crypto import Cipher
from javax.crypto.spec import SecretKeySpec, IvParameterSpec
@@ -26,7 +28,14 @@ def aesEncrypt(data, key):
# magic.
aesDecrypt = aesEncrypt
+has_aes = True
def getKeyLength():
maxlen = Cipher.getMaxAllowedKeyLength('AES/CTR/NoPadding')
return min(maxlen, 256) / 8
+
+
+if getKeyLength() < 32:
+ warn('Crypto implementation only supports key lengths up to %d bits. '
+ 'Generated session cookies may be incompatible with other '
+ 'environments' % (getKeyLength() * 8))
diff --git a/beaker/crypto/noencryption.py b/beaker/crypto/noencryption.py
new file mode 100644
index 0000000..a4af84f
--- /dev/null
+++ b/beaker/crypto/noencryption.py
@@ -0,0 +1,12 @@
+"""Encryption module that does nothing"""
+
+def aesEncrypt(data, key):
+ return data
+
+def aesDecrypt(data, key):
+ return data
+
+has_aes = False
+
+def getKeyLength():
+ return 32
diff --git a/beaker/crypto/pyca_cryptography.py b/beaker/crypto/pyca_cryptography.py
new file mode 100644
index 0000000..ae273b7
--- /dev/null
+++ b/beaker/crypto/pyca_cryptography.py
@@ -0,0 +1,52 @@
+"""Encryption module that uses pyca/cryptography"""
+
+import os
+import json
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import (
+ Cipher, algorithms, modes
+)
+
+
+def aesEncrypt(data, key):
+ # Generate a random 96-bit IV.
+ iv = os.urandom(12)
+
+ # Construct an AES-GCM Cipher object with the given key and a
+ # randomly generated IV.
+ encryptor = Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv),
+ backend=default_backend()
+ ).encryptor()
+
+ # Encrypt the plaintext and get the associated ciphertext.
+ # GCM does not require padding.
+ ciphertext = encryptor.update(data) + encryptor.finalize()
+
+ return iv + encryptor.tag + ciphertext
+
+
+def aesDecrypt(data, key):
+ iv = data[:12]
+ tag = data[12:28]
+ ciphertext = data[28:]
+
+ # Construct a Cipher object, with the key, iv, and additionally the
+ # GCM tag used for authenticating the message.
+ decryptor = Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv, tag),
+ backend=default_backend()
+ ).decryptor()
+
+ # Decryption gets us the authenticated plaintext.
+ # If the tag does not match an InvalidTag exception will be raised.
+ return decryptor.update(ciphertext) + decryptor.finalize()
+
+
+has_aes = True
+
+def getKeyLength():
+ return 32
diff --git a/beaker/crypto/pycrypto.py b/beaker/crypto/pycrypto.py
index 6657bff..55b319c 100644
--- a/beaker/crypto/pycrypto.py
+++ b/beaker/crypto/pycrypto.py
@@ -28,7 +28,7 @@ except ImportError:
counter=Counter.new(128, initial_value=0))
return cipher.decrypt(data)
-
+has_aes = True
def getKeyLength():
return 32
diff --git a/beaker/ext/mongodb.py b/beaker/ext/mongodb.py
new file mode 100644
index 0000000..da50b1d
--- /dev/null
+++ b/beaker/ext/mongodb.py
@@ -0,0 +1,161 @@
+import datetime
+import os
+import threading
+import time
+import pickle
+
+try:
+ import pymongo
+ import pymongo.errors
+ import bson
+except ImportError:
+ pymongo = None
+ bson = None
+
+from beaker.container import NamespaceManager
+from beaker.synchronization import SynchronizerImpl
+from beaker.util import SyncDict, machine_identifier
+from beaker.crypto.util import sha1
+from beaker._compat import string_type, PY2
+
+
+class MongoNamespaceManager(NamespaceManager):
+ """Provides the :class:`.NamespaceManager` API over MongoDB."""
+ MAX_KEY_LENGTH = 1024
+
+ clients = SyncDict()
+
+ def __init__(self, namespace, url, **kw):
+ super(MongoNamespaceManager, self).__init__(namespace)
+ self.lock_dir = None # MongoDB uses mongo itself for locking.
+
+ if pymongo is None:
+ raise RuntimeError('pymongo3 is not available')
+
+ if isinstance(url, string_type):
+ self.client = MongoNamespaceManager.clients.get(url, pymongo.MongoClient, url)
+ else:
+ self.client = url
+ self.db = self.client.get_default_database()
+
+ def _format_key(self, key):
+ if not isinstance(key, str):
+ key = key.decode('ascii')
+ if len(key) > (self.MAX_KEY_LENGTH - len(self.namespace) - 1):
+ if not PY2:
+ key = key.encode('utf-8')
+ key = sha1(key).hexdigest()
+ return '%s:%s' % (self.namespace, key)
+
+ def get_creation_lock(self, key):
+ return MongoSynchronizer(self._format_key(key), self.client)
+
+ def __getitem__(self, key):
+ self._clear_expired()
+ entry = self.db.backer_cache.find_one({'_id': self._format_key(key)})
+ if entry is None:
+ raise KeyError(key)
+ return pickle.loads(entry['value'])
+
+ def __contains__(self, key):
+ self._clear_expired()
+ entry = self.db.backer_cache.find_one({'_id': self._format_key(key)})
+ return entry is not None
+
+ def has_key(self, key):
+ return key in self
+
+ def set_value(self, key, value, expiretime=None):
+ self._clear_expired()
+
+ expiration = None
+ if expiretime is not None:
+ expiration = time.time() + expiretime
+
+ value = pickle.dumps(value)
+ self.db.backer_cache.update_one({'_id': self._format_key(key)},
+ {'$set': {'value': bson.Binary(value),
+ 'expiration': expiration}},
+ upsert=True)
+
+ def __setitem__(self, key, value):
+ self.set_value(key, value)
+
+ def __delitem__(self, key):
+ self._clear_expired()
+ self.db.backer_cache.delete_many({'_id': self._format_key(key)})
+
+ def do_remove(self):
+ self.db.backer_cache.delete_many({'_id': {'$regex': '^%s' % self.namespace}})
+
+ def keys(self):
+ return [e['key'].split(':', 1)[-1] for e in self.db.backer_cache.find_all(
+ {'_id': {'$regex': '^%s' % self.namespace}}
+ )]
+
+ def _clear_expired(self):
+ now = time.time()
+ self.db.backer_cache.delete_many({'_id': {'$regex': '^%s' % self.namespace},
+ 'expiration': {'$ne': None, '$lte': now}})
+
+
+class MongoSynchronizer(SynchronizerImpl):
+ # If a cache entry generation function can take a lot,
+ # but 15 minutes is more than a reasonable time.
+ LOCK_EXPIRATION = 900
+ MACHINE_ID = machine_identifier()
+
+ def __init__(self, identifier, url):
+ super(MongoSynchronizer, self).__init__()
+ self.identifier = identifier
+ if isinstance(url, string_type):
+ self.client = MongoNamespaceManager.clients.get(url, pymongo.MongoClient, url)
+ else:
+ self.client = url
+ self.db = self.client.get_default_database()
+
+ def _clear_expired_locks(self):
+ now = datetime.datetime.utcnow()
+ expired = now - datetime.timedelta(seconds=self.LOCK_EXPIRATION)
+ self.db.beaker_locks.delete_many({'_id': self.identifier, 'timestamp': {'$lte': expired}})
+ return now
+
+ def _get_owner_id(self):
+ return '%s-%s-%s' % (self.MACHINE_ID, os.getpid(), threading.current_thread().ident)
+
+ def do_release_read_lock(self):
+ self.db.beaker_locks.update_one({'_id': self.identifier, 'readers': self._get_owner_id()},
+ {'$pull': {'readers': self._get_owner_id()}})
+
+ def do_acquire_read_lock(self, wait):
+ now = self._clear_expired_locks()
+ while True:
+ try:
+ self.db.beaker_locks.update_one({'_id': self.identifier, 'owner': None},
+ {'$set': {'timestamp': now},
+ '$push': {'readers': self._get_owner_id()}},
+ upsert=True)
+ return True
+ except pymongo.errors.DuplicateKeyError:
+ if not wait:
+ return False
+ time.sleep(0.2)
+
+ def do_release_write_lock(self):
+ self.db.beaker_locks.delete_one({'_id': self.identifier, 'owner': self._get_owner_id()})
+
+ def do_acquire_write_lock(self, wait):
+ now = self._clear_expired_locks()
+ while True:
+ try:
+ self.db.beaker_locks.update_one({'_id': self.identifier, 'owner': None,
+ 'readers': []},
+ {'$set': {'owner': self._get_owner_id(),
+ 'timestamp': now}},
+ upsert=True)
+ return True
+ except pymongo.errors.DuplicateKeyError:
+ if not wait:
+ return False
+ time.sleep(0.2)
+
diff --git a/beaker/ext/redisnm.py b/beaker/ext/redisnm.py
new file mode 100644
index 0000000..8b1515e
--- /dev/null
+++ b/beaker/ext/redisnm.py
@@ -0,0 +1,129 @@
+import os
+import threading
+import time
+import pickle
+
+try:
+ import redis
+except ImportError:
+ redis = None
+
+from beaker.container import NamespaceManager
+from beaker.synchronization import SynchronizerImpl
+from beaker.util import SyncDict, machine_identifier
+from beaker.crypto.util import sha1
+from beaker._compat import string_type, PY2
+
+
+class RedisNamespaceManager(NamespaceManager):
+ """Provides the :class:`.NamespaceManager` API over Redis."""
+ MAX_KEY_LENGTH = 1024
+
+ clients = SyncDict()
+
+ def __init__(self, namespace, url, **kw):
+ super(RedisNamespaceManager, self).__init__(namespace)
+ self.lock_dir = None # Redis uses redis itself for locking.
+
+ if redis is None:
+ raise RuntimeError('redis is not available')
+
+ if isinstance(url, string_type):
+ self.client = RedisNamespaceManager.clients.get(url, redis.StrictRedis.from_url, url)
+ else:
+ self.client = url
+
+ def _format_key(self, key):
+ if not isinstance(key, str):
+ key = key.decode('ascii')
+ if len(key) > (self.MAX_KEY_LENGTH - len(self.namespace) - len('beaker_cache:') - 1):
+ if not PY2:
+ key = key.encode('utf-8')
+ key = sha1(key).hexdigest()
+ return 'beaker_cache:%s:%s' % (self.namespace, key)
+
+ def get_creation_lock(self, key):
+ return RedisSynchronizer(self._format_key(key), self.client)
+
+ def __getitem__(self, key):
+ entry = self.client.get(self._format_key(key))
+ if entry is None:
+ raise KeyError(key)
+ return pickle.loads(entry)
+
+ def __contains__(self, key):
+ return self.client.exists(self._format_key(key))
+
+ def has_key(self, key):
+ return key in self
+
+ def set_value(self, key, value, expiretime=None):
+ value = pickle.dumps(value)
+ if expiretime is not None:
+ self.client.setex(self._format_key(key), int(expiretime), value)
+ else:
+ self.client.set(self._format_key(key), value)
+
+ def __setitem__(self, key, value):
+ self.set_value(key, value)
+
+ def __delitem__(self, key):
+ self.client.delete(self._format_key(key))
+
+ def do_remove(self):
+ for k in self.keys():
+ self.client.delete(k)
+
+ def keys(self):
+ return self.client.keys('beaker_cache:%s:*' % self.namespace)
+
+
+class RedisSynchronizer(SynchronizerImpl):
+ """Synchronizer based on redis.
+
+ This Synchronizer only supports 1 reader or 1 writer at time, not concurrent readers.
+ """
+ # If a cache entry generation function can take a lot,
+ # but 15 minutes is more than a reasonable time.
+ LOCK_EXPIRATION = 900
+ MACHINE_ID = machine_identifier()
+
+ def __init__(self, identifier, url):
+ super(RedisSynchronizer, self).__init__()
+ self.identifier = 'beaker_lock:%s' % identifier
+ if isinstance(url, string_type):
+ self.client = RedisNamespaceManager.clients.get(url, redis.StrictRedis.from_url, url)
+ else:
+ self.client = url
+
+ def _get_owner_id(self):
+ return (
+ '%s-%s-%s' % (self.MACHINE_ID, os.getpid(), threading.current_thread().ident)
+ ).encode('ascii')
+
+ def do_release_read_lock(self):
+ self.do_release_write_lock()
+
+ def do_acquire_read_lock(self, wait):
+ self.do_acquire_write_lock(wait)
+
+ def do_release_write_lock(self):
+ identifier = self.identifier
+ owner_id = self._get_owner_id()
+ def execute_release(pipe):
+ lock_value = pipe.get(identifier)
+ if lock_value == owner_id:
+ pipe.delete(identifier)
+ self.client.transaction(execute_release, identifier)
+
+ def do_acquire_write_lock(self, wait):
+ owner_id = self._get_owner_id()
+ while True:
+ if self.client.setnx(self.identifier, owner_id):
+ self.client.pexpire(self.identifier, self.LOCK_EXPIRATION * 1000)
+ return True
+
+ if not wait:
+ return False
+ time.sleep(0.2)
+
diff --git a/beaker/session.py b/beaker/session.py
index 86c9d70..d4454bd 100644
--- a/beaker/session.py
+++ b/beaker/session.py
@@ -3,7 +3,7 @@ from ._compat import PY2, pickle, http_cookies, unicode_text, b64encode, b64deco
import os
import time
from datetime import datetime, timedelta
-from beaker.crypto import hmac as HMAC, hmac_sha1 as SHA1, sha1, get_nonce_size, DEFAULT_NONCE_BITS
+from beaker.crypto import hmac as HMAC, hmac_sha1 as SHA1, sha1, get_nonce_size, DEFAULT_NONCE_BITS, get_crypto_module
from beaker import crypto, util
from beaker.cache import clsmap
from beaker.exceptions import BeakerException, InvalidCryptoBackendError
@@ -126,6 +126,7 @@ class Session(dict):
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.
+ :param crypto_type: encryption module to use
"""
def __init__(self, request, id=None, invalidate_corrupt=False,
use_cookies=True, type=None, data_dir=None,
@@ -134,6 +135,7 @@ class Session(dict):
data_serializer='pickle', secret=None,
secure=False, namespace_class=None, httponly=False,
encrypt_key=None, validate_key=None, encrypt_nonce_bits=DEFAULT_NONCE_BITS,
+ crypto_type='default',
**namespace_args):
if not type:
if data_dir:
@@ -170,6 +172,7 @@ class Session(dict):
self.encrypt_key = encrypt_key
self.validate_key = validate_key
self.encrypt_nonce_size = get_nonce_size(encrypt_nonce_bits)
+ self.crypto_module = get_crypto_module(crypto_type)
self.id = id
self.accessed_dict = {}
self.invalidate_corrupt = invalidate_corrupt
@@ -262,6 +265,7 @@ class Session(dict):
return expires_date
def _update_cookie_out(self, set_cookie=True):
+ self._set_cookie_values()
self.request['cookie_out'] = self.cookie[self.key].output(header='')
self.request['set_cookie'] = set_cookie
@@ -281,7 +285,6 @@ class Session(dict):
self.is_new = True
self.last_accessed = None
if self.use_cookies:
- self._set_cookie_values()
sc = set_new is False
self._update_cookie_out(set_cookie=sc)
@@ -290,8 +293,7 @@ class Session(dict):
return self['_creation_time']
def _set_domain(self, domain):
- self['_domain'] = domain
- self.cookie[self.key]['domain'] = domain
+ self['_domain'] = self._domain = domain
self._update_cookie_out()
def _get_domain(self):
@@ -301,7 +303,6 @@ class Session(dict):
def _set_path(self, path):
self['_path'] = self._path = path
- self.cookie[self.key]['path'] = path
self._update_cookie_out()
def _get_path(self):
@@ -316,23 +317,27 @@ class Session(dict):
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)
+ self.validate_key + nonce,
+ 1,
+ self.crypto_module.getKeyLength())
data = self.serializer.dumps(session_data)
- return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key))
+ return nonce + b64encode(self.crypto_module.aesEncrypt(data, encrypt_key))
else:
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
+ """Base64, decipher, then un-serialize the data for the session
dict"""
if self.encrypt_key:
__, nonce_b64len = self.encrypt_nonce_size
nonce = session_data[:nonce_b64len]
encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
- self.validate_key + nonce, 1)
+ self.validate_key + nonce,
+ 1,
+ self.crypto_module.getKeyLength())
payload = b64decode(session_data[nonce_b64len:])
- data = crypto.aesDecrypt(payload, encrypt_key)
+ data = self.crypto_module.aesDecrypt(payload, encrypt_key)
else:
data = b64decode(session_data)
@@ -478,7 +483,7 @@ class Session(dict):
creates a new session id, retains all session data
Its a good security practice to regnerate the id after a client
- elevates priviliges.
+ elevates privileges.
"""
self._create_id(set_new=False)
@@ -541,15 +546,19 @@ class CookieSession(Session):
invalidated and a new session created,
otherwise invalid data will cause an exception.
:type invalidate_corrupt: bool
+ :param crypto_type: The crypto module to use.
"""
def __init__(self, request, key='beaker.session.id', timeout=None,
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, invalidate_corrupt=False,
+ crypto_type='default',
**kwargs):
- if not crypto.has_aes and encrypt_key:
+ self.crypto_module = get_crypto_module(crypto_type)
+
+ if not self.crypto_module.has_aes and encrypt_key:
raise InvalidCryptoBackendError("No AES library is installed, can't generate "
"encrypted cookie-only Session.")
diff --git a/beaker/synchronization.py b/beaker/synchronization.py
index f236b8c..7ea8222 100644
--- a/beaker/synchronization.py
+++ b/beaker/synchronization.py
@@ -197,13 +197,13 @@ class SynchronizerImpl(object):
def do_release_read_lock(self):
raise NotImplementedError()
- def do_acquire_read_lock(self):
+ def do_acquire_read_lock(self, wait):
raise NotImplementedError()
def do_release_write_lock(self):
raise NotImplementedError()
- def do_acquire_write_lock(self):
+ def do_acquire_write_lock(self, wait):
raise NotImplementedError()
diff --git a/beaker/util.py b/beaker/util.py
index 4e26594..9dcbf0b 100644
--- a/beaker/util.py
+++ b/beaker/util.py
@@ -1,4 +1,9 @@
"""Beaker utilities"""
+import hashlib
+import socket
+
+import binascii
+
from ._compat import PY2, string_type, unicode_text, NoneType, dictkeyslist, im_class, im_func, pickle, func_signature, \
default_im_func
@@ -331,7 +336,7 @@ def coerce_cache_params(params):
('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"),
+ "Regions must be a comma separated list of valid regions"),
('key_length', (int, NoneType),
"key_length must be an integer which indicates the longest a key can be before hashing"),
]
@@ -478,3 +483,12 @@ def deserialize(data_string, method):
else:
serializer = PickleSerializer()
return serializer.loads(data_string)
+
+
+def machine_identifier():
+ machine_hash = hashlib.md5()
+ if not PY2:
+ machine_hash.update(socket.gethostname().encode())
+ else:
+ machine_hash.update(socket.gethostname())
+ return binascii.hexlify(machine_hash.digest()[0:3]).decode('ascii')
diff --git a/setup.py b/setup.py
index f5d7080..7782ce5 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,12 @@ if not hasattr(inspect, 'signature'):
INSTALL_REQUIRES.append('funcsigs')
-TESTS_REQUIRE = ['nose', 'webtest', 'Mock', 'pycrypto']
+TESTS_REQUIRE = ['nose', 'Mock', 'pycryptodome', 'cryptography']
+
+if py_version == (2, 6):
+ TESTS_REQUIRE.append('WebTest<2.0.24')
+else:
+ TESTS_REQUIRE.append('webtest')
if py_version == (3, 2):
TESTS_REQUIRE.append('coverage < 4.0')
@@ -31,12 +36,16 @@ else:
TESTS_REQUIRE.append('coverage')
if not sys.platform.startswith('java') and not sys.platform == 'cli':
- TESTS_REQUIRE.extend(['SQLALchemy'])
+ TESTS_REQUIRE.extend(['SQLALchemy', 'pymongo', 'redis'])
try:
import sqlite3
except ImportError:
TESTS_REQUIRE.append('pysqlite')
+ if py_version[0] == 2:
+ TESTS_REQUIRE.extend(['pylibmc', 'python-memcached'])
+
+
setup(name='Beaker',
version=VERSION,
description="A Session and Caching library with WSGI Middleware",
@@ -69,6 +78,8 @@ setup(name='Beaker',
extras_require={
'crypto': ['pycryptopp>=0.5.12'],
'pycrypto': ['pycrypto'],
+ 'pycryptodome': ['pycryptodome'],
+ 'cryptography': ['cryptography'],
'testsuite': [TESTS_REQUIRE]
},
test_suite='nose.collector',
--
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