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 re
33
34
35
36
37
38
39 import simplejson
40
41 from ganeti import errors
42 from ganeti import utils
43
44
45 _RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE)
46
47
49 """Serialize a given object.
50
51 @param data: the data to serialize
52 @return: the string representation of data
53
54 """
55 encoded = simplejson.dumps(data)
56
57 txt = _RE_EOLSP.sub("", encoded)
58 if not txt.endswith("\n"):
59 txt += "\n"
60
61 return txt
62
63
65 """Unserialize data from a string.
66
67 @param txt: the json-encoded form
68
69 @return: the original data
70
71 """
72 return simplejson.loads(txt)
73
74
76 """Serialize a given object and authenticate it.
77
78 @param data: the data to serialize
79 @param key: shared hmac key
80 @param key_selector: name/id that identifies the key (in case there are
81 multiple keys in use, e.g. in a multi-cluster environment)
82 @return: the string representation of data signed by the hmac key
83
84 """
85 txt = DumpJson(data)
86 if salt is None:
87 salt = ""
88 signed_dict = {
89 "msg": txt,
90 "salt": salt,
91 }
92
93 if key_selector:
94 signed_dict["key_selector"] = key_selector
95 else:
96 key_selector = ""
97
98 signed_dict["hmac"] = utils.Sha1Hmac(key, txt, salt=salt + key_selector)
99
100 return DumpJson(signed_dict)
101
102
104 """Verify that a given message was signed with the given key, and load it.
105
106 @param txt: json-encoded hmac-signed message
107 @param key: the shared hmac key or a callable taking one argument (the key
108 selector), which returns the hmac key belonging to the key selector.
109 Typical usage is to pass a reference to the get method of a dict.
110 @rtype: tuple of original data, string
111 @return: original data, salt
112 @raises errors.SignatureError: if the message signature doesn't verify
113
114 """
115 signed_dict = LoadJson(txt)
116 if not isinstance(signed_dict, dict):
117 raise errors.SignatureError("Invalid external message")
118 try:
119 msg = signed_dict["msg"]
120 salt = signed_dict["salt"]
121 hmac_sign = signed_dict["hmac"]
122 except KeyError:
123 raise errors.SignatureError("Invalid external message")
124
125 if callable(key):
126
127 key_selector = signed_dict.get("key_selector", None)
128 hmac_key = key(key_selector)
129 if not hmac_key:
130 raise errors.SignatureError("No key with key selector '%s' found" %
131 key_selector)
132 else:
133 key_selector = ""
134 hmac_key = key
135
136 if not utils.VerifySha1Hmac(hmac_key, msg, hmac_sign,
137 salt=salt + key_selector):
138 raise errors.SignatureError("Invalid Signature")
139
140 return LoadJson(msg), salt
141
142
144 """Parses and verifies JSON data.
145
146 @type raw: string
147 @param raw: Input data in JSON format
148 @type verify_fn: callable
149 @param verify_fn: Verification function, usually from L{ht}
150 @return: De-serialized data
151
152 """
153 try:
154 data = LoadJson(raw)
155 except Exception, err:
156 raise errors.ParseError("Can't parse input data: %s" % err)
157
158 if not verify_fn(data):
159 raise errors.ParseError("Data does not match expected format: %s" %
160 verify_fn)
161
162 return data
163
164
165 Dump = DumpJson
166 Load = LoadJson
167 DumpSigned = DumpSignedJson
168 LoadSigned = LoadSignedJson
169