[1.5.x] Required serializer to use bytes in loads/dumps

loads has no way to tell if it should provide text or bytes to the
serializer; bytes are more reasonnable for a serialized representation,
and are the only option for pickled data.

dumps can perform conversions on the value it receives from the
serializer; but for consistency it seems better to require bytes too.

The current code would cause an exception when loading pickled session
data. See next commit.

Also fixed a bug when checking for compressed data.

Backport of 58a086a from master.
This commit is contained in:
Aymeric Augustin 2012-10-28 16:42:34 +01:00
parent f105fbe52b
commit 780eaa4779
1 changed files with 10 additions and 6 deletions

View File

@ -97,10 +97,10 @@ class JSONSerializer(object):
signing.loads. signing.loads.
""" """
def dumps(self, obj): def dumps(self, obj):
return json.dumps(obj, separators=(',', ':')) return json.dumps(obj, separators=(',', ':')).encode('latin-1')
def loads(self, data): def loads(self, data):
return json.loads(data) return json.loads(data.decode('latin-1'))
def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False): def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False):
@ -116,8 +116,10 @@ def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,
only valid for a given namespace. Leaving this at the default only valid for a given namespace. Leaving this at the default
value or re-using a salt value across different parts of your value or re-using a salt value across different parts of your
application without good cause is a security risk. application without good cause is a security risk.
The serializer is expected to return a bytestring.
""" """
data = force_bytes(serializer().dumps(obj)) data = serializer().dumps(obj)
# Flag for if it's been compressed or not # Flag for if it's been compressed or not
is_compressed = False is_compressed = False
@ -136,20 +138,22 @@ def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,
def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None): def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None):
""" """
Reverse of dumps(), raises BadSignature if signature fails Reverse of dumps(), raises BadSignature if signature fails.
The serializer is expected to accept a bytestring.
""" """
# TimestampSigner.unsign always returns unicode but base64 and zlib # TimestampSigner.unsign always returns unicode but base64 and zlib
# compression operate on bytes. # compression operate on bytes.
base64d = force_bytes(TimestampSigner(key, salt=salt).unsign(s, max_age=max_age)) base64d = force_bytes(TimestampSigner(key, salt=salt).unsign(s, max_age=max_age))
decompress = False decompress = False
if base64d[0] == b'.': if base64d[:1] == b'.':
# It's compressed; uncompress it first # It's compressed; uncompress it first
base64d = base64d[1:] base64d = base64d[1:]
decompress = True decompress = True
data = b64_decode(base64d) data = b64_decode(base64d)
if decompress: if decompress:
data = zlib.decompress(data) data = zlib.decompress(data)
return serializer().loads(force_str(data)) return serializer().loads(data)
class Signer(object): class Signer(object):