From f25305ef9eaf0aa2f83f78dcdc1b7a545e81ca64 Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Tue, 31 Oct 2006 23:09:30 +0000 Subject: [PATCH] - utility class for accessing PLCAPI --- plcapi.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 plcapi.py diff --git a/plcapi.py b/plcapi.py new file mode 100644 index 0000000..fd04b6a --- /dev/null +++ b/plcapi.py @@ -0,0 +1,91 @@ +import xmlrpclib +import hmac, sha + +class PLCAPI: + """ + Wrapper around xmlrpclib.ServerProxy to automagically add an Auth + struct as the first argument to every XML-RPC call. Initialize + auth with either: + + (node_id, key) => BootAuth + or + session => SessionAuth + + To authenticate using the Boot Manager authentication method, or + the new session-based method. + """ + + def __init__(self, uri, auth, **kwds): + if isinstance(auth, (tuple, list)): + (self.node_id, self.key) = auth + self.session = None + else: + self.node_id = self.key = None + self.session = auth + + self.server = xmlrpclib.ServerProxy(uri, allow_none = 1, **kwds) + + def add_auth(self, function): + """ + Returns a wrapper which adds an Auth struct as the first + argument when the function is called. + """ + + def canonicalize(args): + """ + BootAuth canonicalization method. Parameter values are + collected, sorted, converted to strings, then hashed with + the node key. + """ + + values = [] + + for arg in args: + if isinstance(arg, list) or isinstance(arg, tuple): + # The old implementation did not recursively handle + # lists of lists. But neither did the old API itself. + values += self.canonicalize(arg) + elif isinstance(arg, dict): + # Yes, the comments in the old implementation are + # misleading. Keys of dicts are not included in the + # hash. + values += self.canonicalize(arg.values()) + else: + # We use unicode() instead of str(). + values.append(unicode(arg)) + + return values + + def wrapper(*params): + """ + Adds an Auth struct as the first argument when the + function is called. + """ + + if self.session is not None: + # Use session authentication + auth = {'session': self.session} + else: + # Yes, this is the "canonicalization" method used. + args = canonicalize(params) + args.sort() + msg = "[" + "".join(args) + "]" + + # We encode in UTF-8 before calculating the HMAC, which is + # an 8-bit algorithm. + digest = hmac.new(self.key, msg.encode('utf-8'), sha).hexdigest() + + auth = {'AuthMethod': "hmac", + 'node_id': self.node_id, + 'value': digest} + + # Automagically add auth struct to every call + params = (auth,) + params + + return function(*params) + + return wrapper + + def __getattr__(self, methodname): + function = getattr(self.server, methodname) + return self.add_auth(function) -- 2.43.0