1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Serializer abstraction module
22
23 This module introduces a simple abstraction over the serialization
24 backend (currently json).
25
26 """
27
28
29
30
31
32 import simplejson
33 import re
34
35 from ganeti import errors
36 from ganeti import utils
37
38
39 _JSON_INDENT = 2
40
41 _RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE)
42
43
45 """Returns two JSON functions to serialize data.
46
47 @rtype: (callable, callable)
48 @return: The function to generate a compact form of JSON and another one to
49 generate a more readable, indented form of JSON (if supported)
50
51 """
52 plain_encoder = _encoder_class(sort_keys=True)
53
54
55 try:
56 indent_encoder = _encoder_class(indent=_JSON_INDENT, sort_keys=True)
57 except TypeError:
58
59 indent_encoder = plain_encoder
60
61 return (plain_encoder.encode, indent_encoder.encode)
62
63
64 (_DumpJson, _DumpJsonIndent) = _GetJsonDumpers()
65
66
68 """Serialize a given object.
69
70 @param data: the data to serialize
71 @param indent: whether to indent output (depends on simplejson version)
72
73 @return: the string representation of data
74
75 """
76 if indent:
77 fn = _DumpJsonIndent
78 else:
79 fn = _DumpJson
80
81 txt = _RE_EOLSP.sub("", fn(data))
82 if not txt.endswith("\n"):
83 txt += "\n"
84
85 return txt
86
87
89 """Unserialize data from a string.
90
91 @param txt: the json-encoded form
92
93 @return: the original data
94
95 """
96 return simplejson.loads(txt)
97
98
100 """Serialize a given object and authenticate it.
101
102 @param data: the data to serialize
103 @param key: shared hmac key
104 @param key_selector: name/id that identifies the key (in case there are
105 multiple keys in use, e.g. in a multi-cluster environment)
106 @return: the string representation of data signed by the hmac key
107
108 """
109 txt = DumpJson(data, indent=False)
110 if salt is None:
111 salt = ""
112 signed_dict = {
113 "msg": txt,
114 "salt": salt,
115 }
116
117 if key_selector:
118 signed_dict["key_selector"] = key_selector
119 else:
120 key_selector = ""
121
122 signed_dict["hmac"] = utils.Sha1Hmac(key, txt, salt=salt + key_selector)
123
124 return DumpJson(signed_dict, indent=False)
125
126
128 """Verify that a given message was signed with the given key, and load it.
129
130 @param txt: json-encoded hmac-signed message
131 @param key: the shared hmac key or a callable taking one argument (the key
132 selector), which returns the hmac key belonging to the key selector.
133 Typical usage is to pass a reference to the get method of a dict.
134 @rtype: tuple of original data, string
135 @return: original data, salt
136 @raises errors.SignatureError: if the message signature doesn't verify
137
138 """
139 signed_dict = LoadJson(txt)
140 if not isinstance(signed_dict, dict):
141 raise errors.SignatureError("Invalid external message")
142 try:
143 msg = signed_dict["msg"]
144 salt = signed_dict["salt"]
145 hmac_sign = signed_dict["hmac"]
146 except KeyError:
147 raise errors.SignatureError("Invalid external message")
148
149 if callable(key):
150
151 key_selector = signed_dict.get("key_selector", None)
152 hmac_key = key(key_selector)
153 if not hmac_key:
154 raise errors.SignatureError("No key with key selector '%s' found" %
155 key_selector)
156 else:
157 key_selector = ""
158 hmac_key = key
159
160 if not utils.VerifySha1Hmac(hmac_key, msg, hmac_sign,
161 salt=salt + key_selector):
162 raise errors.SignatureError("Invalid Signature")
163
164 return LoadJson(msg), salt
165
166
167 Dump = DumpJson
168 Load = LoadJson
169 DumpSigned = DumpSignedJson
170 LoadSigned = LoadSignedJson
171