Statistics
| Branch: | Tag: | Revision:

root / lib / serializer.py @ 560ef132

History | View | Annotate | Download (10.7 kB)

1 8d14b30d Iustin Pop
#
2 8d14b30d Iustin Pop
#
3 8d14b30d Iustin Pop
4 32542155 Jose A. Lopes
# Copyright (C) 2007, 2008, 2014 Google Inc.
5 8d14b30d Iustin Pop
#
6 8d14b30d Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 8d14b30d Iustin Pop
# it under the terms of the GNU General Public License as published by
8 8d14b30d Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 8d14b30d Iustin Pop
# (at your option) any later version.
10 8d14b30d Iustin Pop
#
11 8d14b30d Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 8d14b30d Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 8d14b30d Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 8d14b30d Iustin Pop
# General Public License for more details.
15 8d14b30d Iustin Pop
#
16 8d14b30d Iustin Pop
# You should have received a copy of the GNU General Public License
17 8d14b30d Iustin Pop
# along with this program; if not, write to the Free Software
18 8d14b30d Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 8d14b30d Iustin Pop
# 02110-1301, USA.
20 8d14b30d Iustin Pop
21 8d14b30d Iustin Pop
"""Serializer abstraction module
22 8d14b30d Iustin Pop

23 8d14b30d Iustin Pop
This module introduces a simple abstraction over the serialization
24 8d14b30d Iustin Pop
backend (currently json).
25 8d14b30d Iustin Pop

26 8d14b30d Iustin Pop
"""
27 b459a848 Andrea Spadaccini
# pylint: disable=C0103
28 fe267188 Iustin Pop
29 fe267188 Iustin Pop
# C0103: Invalid name, since pylint doesn't see that Dump points to a
30 fe267188 Iustin Pop
# function and not a constant
31 8d14b30d Iustin Pop
32 8d14b30d Iustin Pop
import re
33 f4a2f532 Guido Trotter
34 b76fd1bc Michael Hanselmann
# Python 2.6 and above contain a JSON module based on simplejson. Unfortunately
35 b76fd1bc Michael Hanselmann
# the standard library version is significantly slower than the external
36 b76fd1bc Michael Hanselmann
# module. While it should be better from at least Python 3.2 on (see Python
37 b76fd1bc Michael Hanselmann
# issue 7451), for now Ganeti needs to work well with older Python versions
38 b76fd1bc Michael Hanselmann
# too.
39 b76fd1bc Michael Hanselmann
import simplejson
40 b76fd1bc Michael Hanselmann
41 f4a2f532 Guido Trotter
from ganeti import errors
42 615aaaba Michael Hanselmann
from ganeti import utils
43 560ef132 Santi Raffa
from ganeti import constants
44 d357f531 Michael Hanselmann
45 d0c8c01d Iustin Pop
_RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE)
46 8d14b30d Iustin Pop
47 8d14b30d Iustin Pop
48 560ef132 Santi Raffa
def DumpJson(data, private_encoder=None):
49 8d14b30d Iustin Pop
  """Serialize a given object.
50 8d14b30d Iustin Pop

51 c41eea6e Iustin Pop
  @param data: the data to serialize
52 c41eea6e Iustin Pop
  @return: the string representation of data
53 560ef132 Santi Raffa
  @param private_encoder: specify L{serializer.EncodeWithPrivateFields} if you
54 560ef132 Santi Raffa
                          require the produced JSON to also contain private
55 560ef132 Santi Raffa
                          parameters. Otherwise, they will encode to null.
56 071448fb Michael Hanselmann

57 8d14b30d Iustin Pop
  """
58 560ef132 Santi Raffa
  if private_encoder is None:
59 560ef132 Santi Raffa
    # Do not leak private fields by default.
60 560ef132 Santi Raffa
    private_encoder = EncodeWithoutPrivateFields
61 560ef132 Santi Raffa
  encoded = simplejson.dumps(data, default=private_encoder)
62 071448fb Michael Hanselmann
63 a182a3ed Michael Hanselmann
  txt = _RE_EOLSP.sub("", encoded)
64 d0c8c01d Iustin Pop
  if not txt.endswith("\n"):
65 d0c8c01d Iustin Pop
    txt += "\n"
66 d357f531 Michael Hanselmann
67 8d14b30d Iustin Pop
  return txt
68 8d14b30d Iustin Pop
69 8d14b30d Iustin Pop
70 228538cf Michael Hanselmann
def LoadJson(txt):
71 8d14b30d Iustin Pop
  """Unserialize data from a string.
72 8d14b30d Iustin Pop

73 c41eea6e Iustin Pop
  @param txt: the json-encoded form
74 c41eea6e Iustin Pop
  @return: the original data
75 32542155 Jose A. Lopes
  @raise JSONDecodeError: if L{txt} is not a valid JSON document
76 c41eea6e Iustin Pop

77 8d14b30d Iustin Pop
  """
78 560ef132 Santi Raffa
  values = simplejson.loads(txt)
79 560ef132 Santi Raffa
80 560ef132 Santi Raffa
  # Hunt and seek for Private fields and wrap them.
81 560ef132 Santi Raffa
  WrapPrivateValues(values)
82 560ef132 Santi Raffa
83 560ef132 Santi Raffa
  return values
84 560ef132 Santi Raffa
85 228538cf Michael Hanselmann
86 560ef132 Santi Raffa
def WrapPrivateValues(json):
87 560ef132 Santi Raffa
  """Crawl a JSON decoded structure for private values and wrap them.
88 560ef132 Santi Raffa

89 560ef132 Santi Raffa
  @param json: the json-decoded value to protect.
90 560ef132 Santi Raffa

91 560ef132 Santi Raffa
  """
92 560ef132 Santi Raffa
  # This function used to be recursive. I use this list to avoid actual
93 560ef132 Santi Raffa
  # recursion, however, since this is a very high-traffic area.
94 560ef132 Santi Raffa
  todo = [json]
95 560ef132 Santi Raffa
96 560ef132 Santi Raffa
  while todo:
97 560ef132 Santi Raffa
    data = todo.pop()
98 560ef132 Santi Raffa
99 560ef132 Santi Raffa
    if isinstance(data, list): # Array
100 560ef132 Santi Raffa
      for item in data:
101 560ef132 Santi Raffa
        todo.append(item)
102 560ef132 Santi Raffa
    elif isinstance(data, dict): # Object
103 560ef132 Santi Raffa
104 560ef132 Santi Raffa
      # This is kind of a kludge, but the only place where we know what should
105 560ef132 Santi Raffa
      # be protected is in ganeti.opcodes, and not in a way that is helpful to
106 560ef132 Santi Raffa
      # us, especially in such a high traffic method; on the other hand, the
107 560ef132 Santi Raffa
      # Haskell `py_compat_fields` test should complain whenever this check
108 560ef132 Santi Raffa
      # does not protect fields properly.
109 560ef132 Santi Raffa
      for field in data:
110 560ef132 Santi Raffa
        value = data[field]
111 560ef132 Santi Raffa
        if field in constants.PRIVATE_PARAMETERS_BLACKLIST:
112 560ef132 Santi Raffa
          if not field.endswith("_cluster"):
113 560ef132 Santi Raffa
            data[field] = PrivateDict(value)
114 560ef132 Santi Raffa
          else:
115 560ef132 Santi Raffa
            for os in data[field]:
116 560ef132 Santi Raffa
              value[os] = PrivateDict(value[os])
117 560ef132 Santi Raffa
        else:
118 560ef132 Santi Raffa
          todo.append(value)
119 560ef132 Santi Raffa
    else: # Values
120 560ef132 Santi Raffa
      pass
121 228538cf Michael Hanselmann
122 560ef132 Santi Raffa
123 560ef132 Santi Raffa
def DumpSignedJson(data, key, salt=None, key_selector=None,
124 560ef132 Santi Raffa
                   private_encoder=None):
125 f4a2f532 Guido Trotter
  """Serialize a given object and authenticate it.
126 f4a2f532 Guido Trotter

127 f4a2f532 Guido Trotter
  @param data: the data to serialize
128 f4a2f532 Guido Trotter
  @param key: shared hmac key
129 72ca1dcb Balazs Lecz
  @param key_selector: name/id that identifies the key (in case there are
130 72ca1dcb Balazs Lecz
    multiple keys in use, e.g. in a multi-cluster environment)
131 560ef132 Santi Raffa
  @param private_encoder: see L{DumpJson}
132 f4a2f532 Guido Trotter
  @return: the string representation of data signed by the hmac key
133 f4a2f532 Guido Trotter

134 f4a2f532 Guido Trotter
  """
135 560ef132 Santi Raffa
  txt = DumpJson(data, private_encoder=private_encoder)
136 f4a2f532 Guido Trotter
  if salt is None:
137 d0c8c01d Iustin Pop
    salt = ""
138 f4a2f532 Guido Trotter
  signed_dict = {
139 d0c8c01d Iustin Pop
    "msg": txt,
140 d0c8c01d Iustin Pop
    "salt": salt,
141 3718bf6d Michael Hanselmann
    }
142 3718bf6d Michael Hanselmann
143 72ca1dcb Balazs Lecz
  if key_selector:
144 72ca1dcb Balazs Lecz
    signed_dict["key_selector"] = key_selector
145 72ca1dcb Balazs Lecz
  else:
146 3718bf6d Michael Hanselmann
    key_selector = ""
147 3718bf6d Michael Hanselmann
148 3718bf6d Michael Hanselmann
  signed_dict["hmac"] = utils.Sha1Hmac(key, txt, salt=salt + key_selector)
149 72ca1dcb Balazs Lecz
150 a182a3ed Michael Hanselmann
  return DumpJson(signed_dict)
151 f4a2f532 Guido Trotter
152 f4a2f532 Guido Trotter
153 4e9dac14 Guido Trotter
def LoadSignedJson(txt, key):
154 f4a2f532 Guido Trotter
  """Verify that a given message was signed with the given key, and load it.
155 f4a2f532 Guido Trotter

156 f4a2f532 Guido Trotter
  @param txt: json-encoded hmac-signed message
157 72ca1dcb Balazs Lecz
  @param key: the shared hmac key or a callable taking one argument (the key
158 72ca1dcb Balazs Lecz
    selector), which returns the hmac key belonging to the key selector.
159 72ca1dcb Balazs Lecz
    Typical usage is to pass a reference to the get method of a dict.
160 f4a2f532 Guido Trotter
  @rtype: tuple of original data, string
161 4e9dac14 Guido Trotter
  @return: original data, salt
162 f4a2f532 Guido Trotter
  @raises errors.SignatureError: if the message signature doesn't verify
163 f4a2f532 Guido Trotter

164 f4a2f532 Guido Trotter
  """
165 f4a2f532 Guido Trotter
  signed_dict = LoadJson(txt)
166 560ef132 Santi Raffa
167 560ef132 Santi Raffa
  WrapPrivateValues(signed_dict)
168 560ef132 Santi Raffa
169 f4a2f532 Guido Trotter
  if not isinstance(signed_dict, dict):
170 d0c8c01d Iustin Pop
    raise errors.SignatureError("Invalid external message")
171 f4a2f532 Guido Trotter
  try:
172 d0c8c01d Iustin Pop
    msg = signed_dict["msg"]
173 d0c8c01d Iustin Pop
    salt = signed_dict["salt"]
174 d0c8c01d Iustin Pop
    hmac_sign = signed_dict["hmac"]
175 f4a2f532 Guido Trotter
  except KeyError:
176 d0c8c01d Iustin Pop
    raise errors.SignatureError("Invalid external message")
177 f4a2f532 Guido Trotter
178 72ca1dcb Balazs Lecz
  if callable(key):
179 b459a848 Andrea Spadaccini
    # pylint: disable=E1103
180 72ca1dcb Balazs Lecz
    key_selector = signed_dict.get("key_selector", None)
181 72ca1dcb Balazs Lecz
    hmac_key = key(key_selector)
182 72ca1dcb Balazs Lecz
    if not hmac_key:
183 72ca1dcb Balazs Lecz
      raise errors.SignatureError("No key with key selector '%s' found" %
184 72ca1dcb Balazs Lecz
                                  key_selector)
185 72ca1dcb Balazs Lecz
  else:
186 72ca1dcb Balazs Lecz
    key_selector = ""
187 72ca1dcb Balazs Lecz
    hmac_key = key
188 72ca1dcb Balazs Lecz
189 3718bf6d Michael Hanselmann
  if not utils.VerifySha1Hmac(hmac_key, msg, hmac_sign,
190 3718bf6d Michael Hanselmann
                              salt=salt + key_selector):
191 d0c8c01d Iustin Pop
    raise errors.SignatureError("Invalid Signature")
192 f4a2f532 Guido Trotter
193 4e9dac14 Guido Trotter
  return LoadJson(msg), salt
194 f4a2f532 Guido Trotter
195 f4a2f532 Guido Trotter
196 5d630c22 Michael Hanselmann
def LoadAndVerifyJson(raw, verify_fn):
197 5d630c22 Michael Hanselmann
  """Parses and verifies JSON data.
198 5d630c22 Michael Hanselmann

199 5d630c22 Michael Hanselmann
  @type raw: string
200 5d630c22 Michael Hanselmann
  @param raw: Input data in JSON format
201 5d630c22 Michael Hanselmann
  @type verify_fn: callable
202 5d630c22 Michael Hanselmann
  @param verify_fn: Verification function, usually from L{ht}
203 5d630c22 Michael Hanselmann
  @return: De-serialized data
204 5d630c22 Michael Hanselmann

205 5d630c22 Michael Hanselmann
  """
206 5d630c22 Michael Hanselmann
  try:
207 5d630c22 Michael Hanselmann
    data = LoadJson(raw)
208 5d630c22 Michael Hanselmann
  except Exception, err:
209 5d630c22 Michael Hanselmann
    raise errors.ParseError("Can't parse input data: %s" % err)
210 5d630c22 Michael Hanselmann
211 5d630c22 Michael Hanselmann
  if not verify_fn(data):
212 5d630c22 Michael Hanselmann
    raise errors.ParseError("Data does not match expected format: %s" %
213 5d630c22 Michael Hanselmann
                            verify_fn)
214 5d630c22 Michael Hanselmann
215 5d630c22 Michael Hanselmann
  return data
216 5d630c22 Michael Hanselmann
217 5d630c22 Michael Hanselmann
218 228538cf Michael Hanselmann
Dump = DumpJson
219 228538cf Michael Hanselmann
Load = LoadJson
220 f4a2f532 Guido Trotter
DumpSigned = DumpSignedJson
221 f4a2f532 Guido Trotter
LoadSigned = LoadSignedJson
222 4884f187 Santi Raffa
223 4884f187 Santi Raffa
224 4884f187 Santi Raffa
class Private(object):
225 4884f187 Santi Raffa
  """Wrap a value so it is hard to leak it accidentally.
226 4884f187 Santi Raffa

227 4884f187 Santi Raffa
  >>> x = Private("foo")
228 4884f187 Santi Raffa
  >>> print "Value: %s" % x
229 4884f187 Santi Raffa
  Value: <redacted>
230 4884f187 Santi Raffa
  >>> print "Value: {0}".format(x)
231 4884f187 Santi Raffa
  Value: <redacted>
232 4884f187 Santi Raffa
  >>> x.upper() == "FOO"
233 4884f187 Santi Raffa
  True
234 4884f187 Santi Raffa

235 4884f187 Santi Raffa
  """
236 4884f187 Santi Raffa
  def __init__(self, item, descr="redacted"):
237 4884f187 Santi Raffa
    if isinstance(item, Private):
238 4884f187 Santi Raffa
      raise ValueError("Attempted to nest Private values.")
239 4884f187 Santi Raffa
    self._item = item
240 4884f187 Santi Raffa
    self._descr = descr
241 4884f187 Santi Raffa
242 4884f187 Santi Raffa
  def Get(self):
243 4884f187 Santi Raffa
    "Return the wrapped value."
244 4884f187 Santi Raffa
    return self._item
245 4884f187 Santi Raffa
246 4884f187 Santi Raffa
  def __str__(self):
247 4884f187 Santi Raffa
    return "<{._descr}>".format(self)
248 4884f187 Santi Raffa
249 4884f187 Santi Raffa
  def __repr__(self):
250 4884f187 Santi Raffa
    return "Private(?, descr='{._descr}')".format(self)
251 4884f187 Santi Raffa
252 4884f187 Santi Raffa
  # pylint: disable=W0212
253 4884f187 Santi Raffa
  # If it doesn't access _item directly, the call will go through __getattr__
254 4884f187 Santi Raffa
  # because this class defines __slots__ and "item" is not in it.
255 4884f187 Santi Raffa
  # OTOH, if we do add it there, we'd risk shadowing an "item" attribute.
256 4884f187 Santi Raffa
  def __eq__(self, other):
257 4884f187 Santi Raffa
    if isinstance(other, Private):
258 4884f187 Santi Raffa
      return self._item == other._item
259 4884f187 Santi Raffa
    else:
260 4884f187 Santi Raffa
      return self._item == other
261 4884f187 Santi Raffa
262 4884f187 Santi Raffa
  def __hash__(self):
263 4884f187 Santi Raffa
    return hash(self._item)
264 4884f187 Santi Raffa
265 4884f187 Santi Raffa
  def __format__(self, *_1, **_2):
266 4884f187 Santi Raffa
    return self.__str__()
267 4884f187 Santi Raffa
268 4884f187 Santi Raffa
  def __getattr__(self, attr):
269 4884f187 Santi Raffa
    return Private(getattr(self._item, attr),
270 4884f187 Santi Raffa
                   descr="%s.%s" % (self._descr, attr))
271 4884f187 Santi Raffa
272 4884f187 Santi Raffa
  def __call__(self, *args, **kwargs):
273 4884f187 Santi Raffa
    return Private(self._item(*args, **kwargs),
274 4884f187 Santi Raffa
                   descr="%s()" % self._descr)
275 4884f187 Santi Raffa
276 4884f187 Santi Raffa
  # pylint: disable=R0201
277 4884f187 Santi Raffa
  # While this could get away with being a function, it needs to be a method.
278 4884f187 Santi Raffa
  # Required by the copy.deepcopy function used by FillDict.
279 4884f187 Santi Raffa
  def __getnewargs__(self):
280 4884f187 Santi Raffa
    return tuple()
281 4884f187 Santi Raffa
282 4884f187 Santi Raffa
  def __nonzero__(self):
283 4884f187 Santi Raffa
    return bool(self._item)
284 4884f187 Santi Raffa
285 4884f187 Santi Raffa
  # Get in the way of Pickle by implementing __slots__ but not __getstate__
286 4884f187 Santi Raffa
  # ...and get a performance boost, too.
287 4884f187 Santi Raffa
  __slots__ = ["_item", "_descr"]
288 4884f187 Santi Raffa
289 4884f187 Santi Raffa
290 4884f187 Santi Raffa
class PrivateDict(dict):
291 4884f187 Santi Raffa
  """A dictionary that turns its values to private fields.
292 4884f187 Santi Raffa

293 4884f187 Santi Raffa
  >>> PrivateDict()
294 4884f187 Santi Raffa
  {}
295 4884f187 Santi Raffa
  >>> supersekkrit = PrivateDict({"password": "foobar"})
296 4884f187 Santi Raffa
  >>> print supersekkrit["password"]
297 4884f187 Santi Raffa
  <password>
298 4884f187 Santi Raffa
  >>> supersekkrit["password"].Get()
299 4884f187 Santi Raffa
  'foobar'
300 4884f187 Santi Raffa
  >>> supersekkrit.GetPrivate("password")
301 4884f187 Santi Raffa
  'foobar'
302 4884f187 Santi Raffa
  >>> supersekkrit["user"] = "eggspam"
303 4884f187 Santi Raffa
  >>> supersekkrit.Unprivate()
304 4884f187 Santi Raffa
  {'password': 'foobar', 'user': 'eggspam'}
305 4884f187 Santi Raffa

306 4884f187 Santi Raffa
  """
307 4884f187 Santi Raffa
  def __init__(self, data=None):
308 4884f187 Santi Raffa
    dict.__init__(self)
309 4884f187 Santi Raffa
    self.update(data)
310 4884f187 Santi Raffa
311 4884f187 Santi Raffa
  def __setitem__(self, item, value):
312 4884f187 Santi Raffa
    if not isinstance(value, Private):
313 4884f187 Santi Raffa
      if not isinstance(item, dict):
314 4884f187 Santi Raffa
        value = Private(value, descr=item)
315 4884f187 Santi Raffa
      else:
316 4884f187 Santi Raffa
        value = PrivateDict(value)
317 4884f187 Santi Raffa
    dict.__setitem__(self, item, value)
318 4884f187 Santi Raffa
319 4884f187 Santi Raffa
  # The actual conversion to Private containers is done by __setitem__
320 4884f187 Santi Raffa
321 4884f187 Santi Raffa
  # copied straight from cpython/Lib/UserDict.py
322 4884f187 Santi Raffa
  # Copyright (c) 2001-2014 Python Software Foundation; All Rights Reserved
323 4884f187 Santi Raffa
  def update(self, other=None, **kwargs):
324 4884f187 Santi Raffa
    # Make progressively weaker assumptions about "other"
325 4884f187 Santi Raffa
    if other is None:
326 4884f187 Santi Raffa
      pass
327 4884f187 Santi Raffa
    elif hasattr(other, 'iteritems'):  # iteritems saves memory and lookups
328 4884f187 Santi Raffa
      for k, v in other.iteritems():
329 4884f187 Santi Raffa
        self[k] = v
330 4884f187 Santi Raffa
    elif hasattr(other, 'keys'):
331 4884f187 Santi Raffa
      for k in other.keys():
332 4884f187 Santi Raffa
        self[k] = other[k]
333 4884f187 Santi Raffa
    else:
334 4884f187 Santi Raffa
      for k, v in other:
335 4884f187 Santi Raffa
        self[k] = v
336 4884f187 Santi Raffa
    if kwargs:
337 4884f187 Santi Raffa
      self.update(kwargs)
338 4884f187 Santi Raffa
339 4884f187 Santi Raffa
  def GetPrivate(self, *args):
340 4884f187 Santi Raffa
    """Like dict.get, but extracting the value in the process.
341 4884f187 Santi Raffa

342 4884f187 Santi Raffa
    Arguments are semantically equivalent to ``dict.get``
343 4884f187 Santi Raffa

344 4884f187 Santi Raffa
    >>> PrivateDict({"foo": "bar"}).GetPrivate("foo")
345 4884f187 Santi Raffa
    'bar'
346 4884f187 Santi Raffa
    >>> PrivateDict({"foo": "bar"}).GetPrivate("baz", "spam")
347 4884f187 Santi Raffa
    'spam'
348 4884f187 Santi Raffa

349 4884f187 Santi Raffa
    """
350 4884f187 Santi Raffa
    if len(args) == 1:
351 4884f187 Santi Raffa
      key, = args
352 4884f187 Santi Raffa
      return self[key].Get()
353 4884f187 Santi Raffa
    elif len(args) == 2:
354 4884f187 Santi Raffa
      key, default = args
355 4884f187 Santi Raffa
      if key not in self:
356 4884f187 Santi Raffa
        return default
357 4884f187 Santi Raffa
      else:
358 4884f187 Santi Raffa
        return self[key].Get()
359 4884f187 Santi Raffa
    else:
360 4884f187 Santi Raffa
      raise TypeError("GetPrivate() takes 2 arguments (%d given)" % len(args))
361 4884f187 Santi Raffa
362 4884f187 Santi Raffa
  def Unprivate(self):
363 4884f187 Santi Raffa
    """Turn this dict of Private() values to a dict of values.
364 4884f187 Santi Raffa

365 4884f187 Santi Raffa
    >>> PrivateDict({"foo": "bar"}).Unprivate()
366 4884f187 Santi Raffa
    {'foo': 'bar'}
367 4884f187 Santi Raffa

368 4884f187 Santi Raffa
    @rtype: dict
369 4884f187 Santi Raffa

370 4884f187 Santi Raffa
    """
371 4884f187 Santi Raffa
    returndict = {}
372 4884f187 Santi Raffa
    for key in self:
373 4884f187 Santi Raffa
      returndict[key] = self[key].Get()
374 4884f187 Santi Raffa
    return returndict
375 560ef132 Santi Raffa
376 560ef132 Santi Raffa
377 560ef132 Santi Raffa
def EncodeWithoutPrivateFields(obj):
378 560ef132 Santi Raffa
  if isinstance(obj, Private):
379 560ef132 Santi Raffa
    return None
380 560ef132 Santi Raffa
  raise TypeError(repr(obj) + " is not JSON serializable")
381 560ef132 Santi Raffa
382 560ef132 Santi Raffa
383 560ef132 Santi Raffa
def EncodeWithPrivateFields(obj):
384 560ef132 Santi Raffa
  if isinstance(obj, Private):
385 560ef132 Santi Raffa
    return obj.Get()
386 560ef132 Santi Raffa
  raise TypeError(repr(obj) + " is not JSON serializable")