[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