summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api/session.rst5
-rw-r--r--pyramid/session.py51
-rw-r--r--pyramid/tests/test_session.py26
3 files changed, 56 insertions, 26 deletions
diff --git a/docs/api/session.rst b/docs/api/session.rst
index daed9fc33..12b727183 100644
--- a/docs/api/session.rst
+++ b/docs/api/session.rst
@@ -7,3 +7,8 @@
.. autofunction:: InsecureCookieSessionFactoryConfig
+ .. autofunction:: signed_serialize
+
+ .. autofunction:: signed_deserialize
+
+
diff --git a/pyramid/session.py b/pyramid/session.py
index 158468152..95d790a7b 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -110,9 +110,15 @@ def InsecureCookieSessionFactoryConfig(
now = time.time()
created = accessed = now
new = True
- cookieval = request.cookies.get(self._cookie_name)
- value = deserialize(cookieval, self._secret)
+ value = None
state = {}
+ cookieval = request.cookies.get(self._cookie_name)
+ if cookieval is not None:
+ try:
+ value = signed_deserialize(cookieval, self._secret)
+ except ValueError:
+ value = None
+
if value is not None:
accessed, created, state = value
new = False
@@ -162,7 +168,7 @@ def InsecureCookieSessionFactoryConfig(
exception = getattr(self.request, 'exception', None)
if exception is not None: # dont set a cookie during exceptions
return False
- cookieval = serialize(
+ cookieval = signed_serialize(
(self.accessed, self.created, dict(self)), self._secret
)
if len(cookieval) > 4064:
@@ -193,22 +199,43 @@ def InsecureCookieSessionFactoryConfig(
return InsecureCookieSessionFactory
-def serialize(data, secret):
+def signed_serialize(data, secret):
+ """ Serialize any pickleable structure (``data``) and sign it
+ using the ``secret`` (must be a string). Return the
+ serialization, which includes the signature as its first 40 bytes.
+ The ``signed_deserialize`` method will deserialize such a value.
+
+ This function is useful for creating signed cookies. For example:
+
+ .. code-block:: python
+
+ cookieval = signed_serialize({'a':1}, 'secret')
+ response.set_cookie('signed_cookie', cookieval)
+ """
pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
sig = hmac.new(secret, pickled, sha1).hexdigest()
return sig + base64.standard_b64encode(pickled)
-def deserialize(serialized, secret, hmac=hmac):
- # hmac parameterized only for unit tests
- if serialized is None:
- return None
+def signed_deserialize(serialized, secret, hmac=hmac):
+ """ Deserialize the value returned from ``signed_serialize``. If
+ the value cannot be deserialized for any reason, a
+ :exc:`ValueError` exception will be raised.
+
+ This function is useful for deserializing a signed cookie value
+ created by ``signed_serialize``. For example:
+ .. code-block:: python
+
+ cookieval = request.cookies['signed_cookie']
+ data = signed_deserialize(cookieval, 'secret')
+ """
+ # hmac parameterized only for unit tests
try:
input_sig, pickled = (serialized[:40],
base64.standard_b64decode(serialized[40:]))
- except (binascii.Error, TypeError):
+ except (binascii.Error, TypeError), e:
# Badly formed data can make base64 die
- return None
+ raise ValueError('Badly formed base64 data: %s' % e)
sig = hmac.new(secret, pickled, sha1).hexdigest()
@@ -216,7 +243,7 @@ def deserialize(serialized, secret, hmac=hmac):
# have no idea what it means)
if len(sig) != len(input_sig):
- return None
+ raise ValueError('Wrong signature length')
invalid_bits = 0
@@ -224,7 +251,7 @@ def deserialize(serialized, secret, hmac=hmac):
invalid_bits += a != b
if invalid_bits:
- return None
+ raise ValueError('Invalid bits in signature')
return pickle.loads(pickled)
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index 12f70bab9..07ca06c81 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -12,8 +12,8 @@ class TestInsecureCookieSession(unittest.TestCase):
self.assertEqual(dict(session), {})
def _serialize(self, accessed, state, secret='secret'):
- from pyramid.session import serialize
- return serialize((accessed, accessed, state), secret)
+ from pyramid.session import signed_serialize
+ return signed_serialize((accessed, accessed, state), secret)
def test_ctor_with_cookie_still_valid(self):
import time
@@ -172,22 +172,22 @@ def serialize(data, secret):
sig = hmac.new(secret, pickled, sha1).hexdigest()
return sig + base64.standard_b64encode(pickled)
-class Test_serialize(unittest.TestCase):
+class Test_signed_serialize(unittest.TestCase):
def _callFUT(self, data, secret):
- from pyramid.session import serialize
- return serialize(data, secret)
+ from pyramid.session import signed_serialize
+ return signed_serialize(data, secret)
def test_it(self):
expected = serialize('123', 'secret')
result = self._callFUT('123', 'secret')
self.assertEqual(result, expected)
-class Test_deserialize(unittest.TestCase):
+class Test_signed_deserialize(unittest.TestCase):
def _callFUT(self, serialized, secret, hmac=None):
if hmac is None:
import hmac
- from pyramid.session import deserialize
- return deserialize(serialized, secret, hmac=hmac)
+ from pyramid.session import signed_deserialize
+ return signed_deserialize(serialized, secret, hmac=hmac)
def test_it(self):
serialized = serialize('123', 'secret')
@@ -196,8 +196,7 @@ class Test_deserialize(unittest.TestCase):
def test_invalid_bits(self):
serialized = serialize('123', 'secret')
- result = self._callFUT(serialized, 'seekrit')
- self.assertEqual(result, None)
+ self.assertRaises(ValueError, self._callFUT, serialized, 'seekrit')
def test_invalid_len(self):
class hmac(object):
@@ -206,13 +205,12 @@ class Test_deserialize(unittest.TestCase):
def hexdigest(self):
return '1234'
serialized = serialize('123', 'secret123')
- result = self._callFUT(serialized, 'secret', hmac=hmac())
- self.assertEqual(result, None)
+ self.assertRaises(ValueError, self._callFUT, serialized, 'secret',
+ hmac=hmac())
def test_it_bad_encoding(self):
serialized = 'bad' + serialize('123', 'secret')
- result = self._callFUT(serialized, 'secret')
- self.assertEqual(result, None)
+ self.assertRaises(ValueError, self._callFUT, serialized, 'secret')
class DummySessionFactory(dict):