Revision 560ef132 lib/serializer.py

b/lib/serializer.py
40 40

  
41 41
from ganeti import errors
42 42
from ganeti import utils
43

  
43
from ganeti import constants
44 44

  
45 45
_RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE)
46 46

  
47 47

  
48
def DumpJson(data):
48
def DumpJson(data, private_encoder=None):
49 49
  """Serialize a given object.
50 50

  
51 51
  @param data: the data to serialize
52 52
  @return: the string representation of data
53
  @param private_encoder: specify L{serializer.EncodeWithPrivateFields} if you
54
                          require the produced JSON to also contain private
55
                          parameters. Otherwise, they will encode to null.
53 56

  
54 57
  """
55
  encoded = simplejson.dumps(data)
58
  if private_encoder is None:
59
    # Do not leak private fields by default.
60
    private_encoder = EncodeWithoutPrivateFields
61
  encoded = simplejson.dumps(data, default=private_encoder)
56 62

  
57 63
  txt = _RE_EOLSP.sub("", encoded)
58 64
  if not txt.endswith("\n"):
......
69 75
  @raise JSONDecodeError: if L{txt} is not a valid JSON document
70 76

  
71 77
  """
72
  return simplejson.loads(txt)
78
  values = simplejson.loads(txt)
79

  
80
  # Hunt and seek for Private fields and wrap them.
81
  WrapPrivateValues(values)
82

  
83
  return values
84

  
73 85

  
86
def WrapPrivateValues(json):
87
  """Crawl a JSON decoded structure for private values and wrap them.
88

  
89
  @param json: the json-decoded value to protect.
90

  
91
  """
92
  # This function used to be recursive. I use this list to avoid actual
93
  # recursion, however, since this is a very high-traffic area.
94
  todo = [json]
95

  
96
  while todo:
97
    data = todo.pop()
98

  
99
    if isinstance(data, list): # Array
100
      for item in data:
101
        todo.append(item)
102
    elif isinstance(data, dict): # Object
103

  
104
      # This is kind of a kludge, but the only place where we know what should
105
      # be protected is in ganeti.opcodes, and not in a way that is helpful to
106
      # us, especially in such a high traffic method; on the other hand, the
107
      # Haskell `py_compat_fields` test should complain whenever this check
108
      # does not protect fields properly.
109
      for field in data:
110
        value = data[field]
111
        if field in constants.PRIVATE_PARAMETERS_BLACKLIST:
112
          if not field.endswith("_cluster"):
113
            data[field] = PrivateDict(value)
114
          else:
115
            for os in data[field]:
116
              value[os] = PrivateDict(value[os])
117
        else:
118
          todo.append(value)
119
    else: # Values
120
      pass
74 121

  
75
def DumpSignedJson(data, key, salt=None, key_selector=None):
122

  
123
def DumpSignedJson(data, key, salt=None, key_selector=None,
124
                   private_encoder=None):
76 125
  """Serialize a given object and authenticate it.
77 126

  
78 127
  @param data: the data to serialize
79 128
  @param key: shared hmac key
80 129
  @param key_selector: name/id that identifies the key (in case there are
81 130
    multiple keys in use, e.g. in a multi-cluster environment)
131
  @param private_encoder: see L{DumpJson}
82 132
  @return: the string representation of data signed by the hmac key
83 133

  
84 134
  """
85
  txt = DumpJson(data)
135
  txt = DumpJson(data, private_encoder=private_encoder)
86 136
  if salt is None:
87 137
    salt = ""
88 138
  signed_dict = {
......
113 163

  
114 164
  """
115 165
  signed_dict = LoadJson(txt)
166

  
167
  WrapPrivateValues(signed_dict)
168

  
116 169
  if not isinstance(signed_dict, dict):
117 170
    raise errors.SignatureError("Invalid external message")
118 171
  try:
......
319 372
    for key in self:
320 373
      returndict[key] = self[key].Get()
321 374
    return returndict
375

  
376

  
377
def EncodeWithoutPrivateFields(obj):
378
  if isinstance(obj, Private):
379
    return None
380
  raise TypeError(repr(obj) + " is not JSON serializable")
381

  
382

  
383
def EncodeWithPrivateFields(obj):
384
  if isinstance(obj, Private):
385
    return obj.Get()
386
  raise TypeError(repr(obj) + " is not JSON serializable")

Also available in: Unified diff