X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/3718bf6d3d9d6232234d03bebb3a43906be9c2cc..f2d87a5e5:/lib/serializer.py diff --git a/lib/serializer.py b/lib/serializer.py index 9a5f1ce..f008155 100644 --- a/lib/serializer.py +++ b/lib/serializer.py @@ -24,63 +24,39 @@ This module introduces a simple abstraction over the serialization backend (currently json). """ -# pylint: disable-msg=C0103 +# pylint: disable=C0103 # C0103: Invalid name, since pylint doesn't see that Dump points to a # function and not a constant -import simplejson import re +# Python 2.6 and above contain a JSON module based on simplejson. Unfortunately +# the standard library version is significantly slower than the external +# module. While it should be better from at least Python 3.2 on (see Python +# issue 7451), for now Ganeti needs to work well with older Python versions +# too. +import simplejson + from ganeti import errors from ganeti import utils -_JSON_INDENT = 2 - -_RE_EOLSP = re.compile('[ \t]+$', re.MULTILINE) - - -def _GetJsonDumpers(_encoder_class=simplejson.JSONEncoder): - """Returns two JSON functions to serialize data. - - @rtype: (callable, callable) - @return: The function to generate a compact form of JSON and another one to - generate a more readable, indented form of JSON (if supported) - - """ - plain_encoder = _encoder_class(sort_keys=True) - - # Check whether the simplejson module supports indentation - try: - indent_encoder = _encoder_class(indent=_JSON_INDENT, sort_keys=True) - except TypeError: - # Indentation not supported - indent_encoder = plain_encoder - - return (plain_encoder.encode, indent_encoder.encode) - - -(_DumpJson, _DumpJsonIndent) = _GetJsonDumpers() +_RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE) -def DumpJson(data, indent=True): +def DumpJson(data): """Serialize a given object. @param data: the data to serialize - @param indent: whether to indent output (depends on simplejson version) - @return: the string representation of data """ - if indent: - fn = _DumpJsonIndent - else: - fn = _DumpJson + encoded = simplejson.dumps(data) - txt = _RE_EOLSP.sub("", fn(data)) - if not txt.endswith('\n'): - txt += '\n' + txt = _RE_EOLSP.sub("", encoded) + if not txt.endswith("\n"): + txt += "\n" return txt @@ -106,12 +82,12 @@ def DumpSignedJson(data, key, salt=None, key_selector=None): @return: the string representation of data signed by the hmac key """ - txt = DumpJson(data, indent=False) + txt = DumpJson(data) if salt is None: - salt = '' + salt = "" signed_dict = { - 'msg': txt, - 'salt': salt, + "msg": txt, + "salt": salt, } if key_selector: @@ -121,7 +97,7 @@ def DumpSignedJson(data, key, salt=None, key_selector=None): signed_dict["hmac"] = utils.Sha1Hmac(key, txt, salt=salt + key_selector) - return DumpJson(signed_dict, indent=False) + return DumpJson(signed_dict) def LoadSignedJson(txt, key): @@ -138,16 +114,16 @@ def LoadSignedJson(txt, key): """ signed_dict = LoadJson(txt) if not isinstance(signed_dict, dict): - raise errors.SignatureError('Invalid external message') + raise errors.SignatureError("Invalid external message") try: - msg = signed_dict['msg'] - salt = signed_dict['salt'] - hmac_sign = signed_dict['hmac'] + msg = signed_dict["msg"] + salt = signed_dict["salt"] + hmac_sign = signed_dict["hmac"] except KeyError: - raise errors.SignatureError('Invalid external message') + raise errors.SignatureError("Invalid external message") if callable(key): - # pylint: disable-msg=E1103 + # pylint: disable=E1103 key_selector = signed_dict.get("key_selector", None) hmac_key = key(key_selector) if not hmac_key: @@ -159,11 +135,33 @@ def LoadSignedJson(txt, key): if not utils.VerifySha1Hmac(hmac_key, msg, hmac_sign, salt=salt + key_selector): - raise errors.SignatureError('Invalid Signature') + raise errors.SignatureError("Invalid Signature") return LoadJson(msg), salt +def LoadAndVerifyJson(raw, verify_fn): + """Parses and verifies JSON data. + + @type raw: string + @param raw: Input data in JSON format + @type verify_fn: callable + @param verify_fn: Verification function, usually from L{ht} + @return: De-serialized data + + """ + try: + data = LoadJson(raw) + except Exception, err: + raise errors.ParseError("Can't parse input data: %s" % err) + + if not verify_fn(data): + raise errors.ParseError("Data does not match expected format: %s" % + verify_fn) + + return data + + Dump = DumpJson Load = LoadJson DumpSigned = DumpSignedJson