[Note: same patch as earlier but rebased]

Traverse the objects passed to JSON for encoding and decoding.
When binary data is seen during encode replace the binary
data with a dict {'__base64__' : base64_encoding_of_binary_value}.

On decode if a dict is seen whose single key is '__base64__' replace
that dict with the base64 decoded value of the key's value.

--
John Dennis <jden...@redhat.com>

Looking to carve out IT costs?
www.redhat.com/carveoutcosts/
>From d3627d87ff2b26b380a4a4d0a349ad2d432c1ca3 Mon Sep 17 00:00:00 2001
From: John Dennis <jden...@redhat.com>
Date: Mon, 1 Mar 2010 18:55:39 -0500
Subject: [PATCH] Fix JSON binary encode and decode errors
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.6.6.1"

This is a multi-part message in MIME format.
--------------1.6.6.1
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit


Traverse the objects passed to JSON for encoding and decoding.
When binary data is seen during encode replace the binary
data with a dict {'__base64__' : base64_encoding_of_binary_value}.

On decode if a dict is seen whose single key is '__base64__' replace
that dict with the base64 decoded value of the key's value.
---
 ipaserver/rpcserver.py |  101 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 99 insertions(+), 2 deletions(-)


--------------1.6.6.1
Content-Type: text/x-patch; name="0003-Fix-JSON-binary-encode-and-decode-errors.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0003-Fix-JSON-binary-encode-and-decode-errors.patch"

diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index ad402cd..967ee33 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -33,7 +33,7 @@ from ipalib.rpc import xml_dumps, xml_loads
 from ipalib.util import make_repr
 from ipalib.compat import json
 from wsgiref.util import shift_path_info
-
+import base64
 
 _not_found_template = """<html>
 <head>
@@ -60,7 +60,6 @@ def not_found(environ, start_response):
     )
     return [output]
 
-
 def read_input(environ):
     """
     Read the request body from environ['wsgi.input'].
@@ -300,6 +299,102 @@ class xmlserver(WSGIExecutioner):
         return xml_dumps(response, methodresponse=True)
 
 
+def json_encode_binary(val):
+    '''
+   JSON cannot encode binary values. We encode binary values in Python str
+   objects and text in Python unicode objects. In order to allow a binary
+   object to be passed through JSON we base64 encode it thus converting it to
+   text which JSON can transport. To assure we recognize the value is a base64
+   encoded representation of the original binary value and not confuse it with
+   other text we convert the binary value to a dict in this form:
+
+   {'__base64__' : base64_encoding_of_binary_value}
+
+   This modification of the original input value cannot be done "in place" as
+   one might first assume (e.g. replacing any binary items in a container
+   (e.g. list, tuple, dict) with the base64 dict because the container might be
+   an immutable object (i.e. a tuple). Therefore this function returns a copy
+   of any container objects it encounters with tuples replaced by lists. This
+   is O.K. because the JSON encoding will map both lists and tuples to JSON
+   arrays.
+   '''
+
+    if isinstance(val, dict):
+        new_dict = {}
+        for k,v in val.items():
+            if isinstance(v, str):
+                new_dict[k] = {'__base64__' : base64.b64encode(v)}
+            else:
+                new_dict[k] = json_encode_binary(v)
+        del val
+        return new_dict
+    elif isinstance(val, (list, tuple)):
+        new_list = []
+        n = len(val)
+        i = 0
+        while i < n:
+            v = val[i]
+            if isinstance(v, str):
+                new_list.append({'__base64__' : base64.b64encode(v)})
+            else:
+                new_list.append(json_encode_binary(v))
+            i += 1
+        del val
+        return new_list
+    elif isinstance(val, str):
+        return {'__base64__' : base64.b64encode(val)}
+    else:
+        return val
+
+def json_decode_binary(val):
+    '''
+    JSON cannot transport binary data. In order to transport binary data we
+    convert binary data to a form like this:
+
+   {'__base64__' : base64_encoding_of_binary_value}
+
+   see json_encode_binary()
+
+    After JSON had decoded the JSON stream back into a Python object we must
+    recursively scan the object looking for any dicts which might represent
+    binary values and replace the dict containing the base64 encoding of the
+    binary value with the decoded binary value. Unlike the encoding problem
+    where the input might consist of immutable object, all JSON decoded
+    container are mutable so the conversion could be done in place. However we
+    don't modify objects in place because of side effects which may be
+    dangerous. Thus we elect to spend a few more cycles and avoid the
+    possibility of unintended side effects in favor of robustness.
+    '''
+
+    if isinstance(val, dict):
+        if val.has_key('__base64__'):
+            return base64.b64decode(val['__base64__'])
+        else:
+            new_dict = {}
+            for k,v in val.items():
+                if isinstance(v, dict) and v.has_key('__base64__'):
+                        new_dict[k] = base64.b64decode(v['__base64__'])
+                else:
+                    new_dict[k] = json_decode_binary(v)
+            del val
+            return new_dict
+    elif isinstance(val, list):
+        new_list = []
+        n = len(val)
+        i = 0
+        while i < n:
+            v = val[i]
+            if isinstance(v, dict) and v.has_key('__base64__'):
+                binary_val = base64.b64decode(v['__base64__'])
+                new_list.append(binary_val)
+            else:
+                new_list.append(json_decode_binary(v))
+            i += 1
+        del val
+        return new_list
+    else:
+        return val
+
 class jsonserver(WSGIExecutioner):
     """
     JSON RPC server.
@@ -326,6 +421,7 @@ class jsonserver(WSGIExecutioner):
             error=error,
             id=_id,
         )
+        response = json_encode_binary(response)
         return json.dumps(response, sort_keys=True, indent=4)
 
     def unmarshal(self, data):
@@ -339,6 +435,7 @@ class jsonserver(WSGIExecutioner):
             raise JSONError(error='Request is missing "method"')
         if 'params' not in d:
             raise JSONError(error='Request is missing "params"')
+        d = json_decode_binary(d)
         method = d['method']
         params = d['params']
         _id = d.get('id')

--------------1.6.6.1--


_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to