Statistics
| Branch: | Tag: | Revision:

root / lib / serializer.py @ 7eda951b

History | View | Annotate | Download (4.8 kB)

1 8d14b30d Iustin Pop
#
2 8d14b30d Iustin Pop
#
3 8d14b30d Iustin Pop
4 8d14b30d Iustin Pop
# Copyright (C) 2007, 2008 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 071448fb Michael Hanselmann
44 d357f531 Michael Hanselmann
45 8d14b30d Iustin Pop
_JSON_INDENT = 2
46 8d14b30d Iustin Pop
47 d0c8c01d Iustin Pop
_RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE)
48 8d14b30d Iustin Pop
49 8d14b30d Iustin Pop
50 cdeda3b6 Michael Hanselmann
def _GetJsonDumpers(_encoder_class=simplejson.JSONEncoder):
51 d357f531 Michael Hanselmann
  """Returns two JSON functions to serialize data.
52 d357f531 Michael Hanselmann

53 d357f531 Michael Hanselmann
  @rtype: (callable, callable)
54 d357f531 Michael Hanselmann
  @return: The function to generate a compact form of JSON and another one to
55 d357f531 Michael Hanselmann
           generate a more readable, indented form of JSON (if supported)
56 d357f531 Michael Hanselmann

57 d357f531 Michael Hanselmann
  """
58 02141fb1 Michael Hanselmann
  plain_encoder = _encoder_class(sort_keys=True)
59 d357f531 Michael Hanselmann
60 d357f531 Michael Hanselmann
  # Check whether the simplejson module supports indentation
61 d357f531 Michael Hanselmann
  try:
62 02141fb1 Michael Hanselmann
    indent_encoder = _encoder_class(indent=_JSON_INDENT, sort_keys=True)
63 d357f531 Michael Hanselmann
  except TypeError:
64 d357f531 Michael Hanselmann
    # Indentation not supported
65 02141fb1 Michael Hanselmann
    indent_encoder = plain_encoder
66 d357f531 Michael Hanselmann
67 02141fb1 Michael Hanselmann
  return (plain_encoder.encode, indent_encoder.encode)
68 d357f531 Michael Hanselmann
69 d357f531 Michael Hanselmann
70 d357f531 Michael Hanselmann
(_DumpJson, _DumpJsonIndent) = _GetJsonDumpers()
71 d357f531 Michael Hanselmann
72 d357f531 Michael Hanselmann
73 071448fb Michael Hanselmann
def DumpJson(data, indent=True):
74 8d14b30d Iustin Pop
  """Serialize a given object.
75 8d14b30d Iustin Pop

76 c41eea6e Iustin Pop
  @param data: the data to serialize
77 c41eea6e Iustin Pop
  @param indent: whether to indent output (depends on simplejson version)
78 c41eea6e Iustin Pop

79 c41eea6e Iustin Pop
  @return: the string representation of data
80 071448fb Michael Hanselmann

81 8d14b30d Iustin Pop
  """
82 d357f531 Michael Hanselmann
  if indent:
83 d357f531 Michael Hanselmann
    fn = _DumpJsonIndent
84 8d14b30d Iustin Pop
  else:
85 d357f531 Michael Hanselmann
    fn = _DumpJson
86 071448fb Michael Hanselmann
87 d357f531 Michael Hanselmann
  txt = _RE_EOLSP.sub("", fn(data))
88 d0c8c01d Iustin Pop
  if not txt.endswith("\n"):
89 d0c8c01d Iustin Pop
    txt += "\n"
90 d357f531 Michael Hanselmann
91 8d14b30d Iustin Pop
  return txt
92 8d14b30d Iustin Pop
93 8d14b30d Iustin Pop
94 228538cf Michael Hanselmann
def LoadJson(txt):
95 8d14b30d Iustin Pop
  """Unserialize data from a string.
96 8d14b30d Iustin Pop

97 c41eea6e Iustin Pop
  @param txt: the json-encoded form
98 c41eea6e Iustin Pop

99 c41eea6e Iustin Pop
  @return: the original data
100 c41eea6e Iustin Pop

101 8d14b30d Iustin Pop
  """
102 cdeda3b6 Michael Hanselmann
  return simplejson.loads(txt)
103 228538cf Michael Hanselmann
104 228538cf Michael Hanselmann
105 72ca1dcb Balazs Lecz
def DumpSignedJson(data, key, salt=None, key_selector=None):
106 f4a2f532 Guido Trotter
  """Serialize a given object and authenticate it.
107 f4a2f532 Guido Trotter

108 f4a2f532 Guido Trotter
  @param data: the data to serialize
109 f4a2f532 Guido Trotter
  @param key: shared hmac key
110 72ca1dcb Balazs Lecz
  @param key_selector: name/id that identifies the key (in case there are
111 72ca1dcb Balazs Lecz
    multiple keys in use, e.g. in a multi-cluster environment)
112 f4a2f532 Guido Trotter
  @return: the string representation of data signed by the hmac key
113 f4a2f532 Guido Trotter

114 f4a2f532 Guido Trotter
  """
115 f4a2f532 Guido Trotter
  txt = DumpJson(data, indent=False)
116 f4a2f532 Guido Trotter
  if salt is None:
117 d0c8c01d Iustin Pop
    salt = ""
118 f4a2f532 Guido Trotter
  signed_dict = {
119 d0c8c01d Iustin Pop
    "msg": txt,
120 d0c8c01d Iustin Pop
    "salt": salt,
121 3718bf6d Michael Hanselmann
    }
122 3718bf6d Michael Hanselmann
123 72ca1dcb Balazs Lecz
  if key_selector:
124 72ca1dcb Balazs Lecz
    signed_dict["key_selector"] = key_selector
125 72ca1dcb Balazs Lecz
  else:
126 3718bf6d Michael Hanselmann
    key_selector = ""
127 3718bf6d Michael Hanselmann
128 3718bf6d Michael Hanselmann
  signed_dict["hmac"] = utils.Sha1Hmac(key, txt, salt=salt + key_selector)
129 72ca1dcb Balazs Lecz
130 40765aa0 Guido Trotter
  return DumpJson(signed_dict, indent=False)
131 f4a2f532 Guido Trotter
132 f4a2f532 Guido Trotter
133 4e9dac14 Guido Trotter
def LoadSignedJson(txt, key):
134 f4a2f532 Guido Trotter
  """Verify that a given message was signed with the given key, and load it.
135 f4a2f532 Guido Trotter

136 f4a2f532 Guido Trotter
  @param txt: json-encoded hmac-signed message
137 72ca1dcb Balazs Lecz
  @param key: the shared hmac key or a callable taking one argument (the key
138 72ca1dcb Balazs Lecz
    selector), which returns the hmac key belonging to the key selector.
139 72ca1dcb Balazs Lecz
    Typical usage is to pass a reference to the get method of a dict.
140 f4a2f532 Guido Trotter
  @rtype: tuple of original data, string
141 4e9dac14 Guido Trotter
  @return: original data, salt
142 f4a2f532 Guido Trotter
  @raises errors.SignatureError: if the message signature doesn't verify
143 f4a2f532 Guido Trotter

144 f4a2f532 Guido Trotter
  """
145 f4a2f532 Guido Trotter
  signed_dict = LoadJson(txt)
146 f4a2f532 Guido Trotter
  if not isinstance(signed_dict, dict):
147 d0c8c01d Iustin Pop
    raise errors.SignatureError("Invalid external message")
148 f4a2f532 Guido Trotter
  try:
149 d0c8c01d Iustin Pop
    msg = signed_dict["msg"]
150 d0c8c01d Iustin Pop
    salt = signed_dict["salt"]
151 d0c8c01d Iustin Pop
    hmac_sign = signed_dict["hmac"]
152 f4a2f532 Guido Trotter
  except KeyError:
153 d0c8c01d Iustin Pop
    raise errors.SignatureError("Invalid external message")
154 f4a2f532 Guido Trotter
155 72ca1dcb Balazs Lecz
  if callable(key):
156 b459a848 Andrea Spadaccini
    # pylint: disable=E1103
157 72ca1dcb Balazs Lecz
    key_selector = signed_dict.get("key_selector", None)
158 72ca1dcb Balazs Lecz
    hmac_key = key(key_selector)
159 72ca1dcb Balazs Lecz
    if not hmac_key:
160 72ca1dcb Balazs Lecz
      raise errors.SignatureError("No key with key selector '%s' found" %
161 72ca1dcb Balazs Lecz
                                  key_selector)
162 72ca1dcb Balazs Lecz
  else:
163 72ca1dcb Balazs Lecz
    key_selector = ""
164 72ca1dcb Balazs Lecz
    hmac_key = key
165 72ca1dcb Balazs Lecz
166 3718bf6d Michael Hanselmann
  if not utils.VerifySha1Hmac(hmac_key, msg, hmac_sign,
167 3718bf6d Michael Hanselmann
                              salt=salt + key_selector):
168 d0c8c01d Iustin Pop
    raise errors.SignatureError("Invalid Signature")
169 f4a2f532 Guido Trotter
170 4e9dac14 Guido Trotter
  return LoadJson(msg), salt
171 f4a2f532 Guido Trotter
172 f4a2f532 Guido Trotter
173 228538cf Michael Hanselmann
Dump = DumpJson
174 228538cf Michael Hanselmann
Load = LoadJson
175 f4a2f532 Guido Trotter
DumpSigned = DumpSignedJson
176 f4a2f532 Guido Trotter
LoadSigned = LoadSignedJson