Also replace positional calls with explicit keyword argument calls in test/ganeti.serializer_unittest.py
Signed-off-by: Balazs Lecz <[email protected]> --- lib/serializer.py | 21 ++++++++++++++++++--- test/ganeti.serializer_unittest.py | 27 ++++++++++++++++++++------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/lib/serializer.py b/lib/serializer.py index b568497..895f33e 100644 --- a/lib/serializer.py +++ b/lib/serializer.py @@ -101,11 +101,13 @@ def LoadJson(txt): return simplejson.loads(txt) -def DumpSignedJson(data, key, salt=None): +def DumpSignedJson(data, key, salt=None, key_selector=None): """Serialize a given object and authenticate it. @param data: the data to serialize @param key: shared hmac key + @param key_selector: name/id that identifies the key (in case there are + multiple keys in use, e.g. in a multi-cluster environment) @return: the string representation of data signed by the hmac key """ @@ -117,6 +119,8 @@ def DumpSignedJson(data, key, salt=None): 'salt': salt, 'hmac': hmac.new(key, salt + txt, sha1).hexdigest(), } + if key_selector: + signed_dict["key_selector"] = key_selector return DumpJson(signed_dict, indent=False) @@ -124,7 +128,9 @@ def LoadSignedJson(txt, key): """Verify that a given message was signed with the given key, and load it. @param txt: json-encoded hmac-signed message - @param key: shared hmac key + @param key: the shared hmac key or a callable taking one argument (the key + selector), which returns the hmac key belonging to the key selector. + Typical usage is to pass a reference to the get method of a dict. @rtype: tuple of original data, string @return: original data, salt @raises errors.SignatureError: if the message signature doesn't verify @@ -140,7 +146,16 @@ def LoadSignedJson(txt, key): except KeyError: raise errors.SignatureError('Invalid external message') - if hmac.new(key, salt + msg, sha1).hexdigest() != hmac_sign: + if callable(key): + key_selector = signed_dict.get("key_selector", None) + hmac_key = key(key_selector) + if not hmac_key: + raise errors.SignatureError("No key with key selector '%s' found" % + key_selector) + else: + hmac_key = key + + if hmac.new(hmac_key, salt + msg, sha1).hexdigest() != hmac_sign: raise errors.SignatureError('Invalid Signature') return LoadJson(msg), salt diff --git a/test/ganeti.serializer_unittest.py b/test/ganeti.serializer_unittest.py index 11a60e6..ed703d9 100755 --- a/test/ganeti.serializer_unittest.py +++ b/test/ganeti.serializer_unittest.py @@ -68,16 +68,29 @@ class TestSerializer(testutils.GanetiTestCase): DumpSigned = serializer.DumpSigned for data in self._TESTDATA: - self.assertEqualValues(LoadSigned(DumpSigned(data, "mykey"), "mykey"), + self.assertEqualValues(LoadSigned(DumpSigned(data, + key="mykey"), + key="mykey"), (data, '')) - self.assertEqualValues(LoadSigned(DumpSigned(data, "myprivatekey", - "mysalt"), - "myprivatekey"), + self.assertEqualValues(LoadSigned(DumpSigned(data, + key="myprivatekey", + salt="mysalt"), + key="myprivatekey"), (data, "mysalt")) - self.assertRaises(errors.SignatureError, serializer.LoadSigned, - serializer.DumpSigned("test", "myprivatekey"), - "myotherkey") + keydict = {"mykey_id": "myprivatekey"} + self.assertEqualValues(LoadSigned(DumpSigned(data, + key="myprivatekey", + salt="mysalt", + key_selector="mykey_id"), + key=keydict.get), + (data, "mysalt")) + + self.assertRaises(errors.SignatureError, + serializer.LoadSigned, + serializer.DumpSigned("test", + key="myprivatekey"), + key="myotherkey") if __name__ == '__main__': -- 1.6.6.2
