guard AuthCheck with try/except as it will raise an exception.
[nodemanager.git] / plcapi.py
1 # $Id$
2
3 import safexmlrpc
4 import hmac, sha
5
6 class PLCAPI:
7     """
8     Wrapper around safexmlrpc.ServerProxy to automagically add an Auth
9     struct as the first argument to every XML-RPC call. Initialize
10     auth with either:
11
12     (node_id, key) => BootAuth
13     or
14     session => SessionAuth
15
16     To authenticate using the Boot Manager authentication method, or
17     the new session-based method.
18     """
19
20     def __init__(self, uri, cacert, auth, timeout = 90, **kwds):
21         self.uri = uri
22         self.cacert = cacert
23         self.timeout = timeout
24
25         if isinstance(auth, (tuple, list)):
26             (self.node_id, self.key) = auth
27             self.session = None
28         elif isinstance(auth, (str, unicode)):
29             self.node_id = self.key = None
30             self.session = auth
31         else:
32             self.node_id = self.key = self.session = None
33
34         self.server = safexmlrpc.ServerProxy(self.uri, self.cacert, self.timeout, allow_none = 1, **kwds)
35         
36         self.__check_authentication()
37
38
39     def __update_session(self, f="/usr/boot/plnode.txt"):
40         # try authenticatipopulate /etc.planetlab/session 
41         def plnode(key):
42             try:
43                 return [i[:-1].split('=') for i in open(f).readlines() if i.startswith(key)][0][1].strip('"')
44             except:
45                 return None
46
47         auth = (int(plnode("NODE_ID")), plnode("NODE_KEY"))
48         plc = PLCAPI(self.uri, self.cacert, auth, self.timeout)
49         open("/etc/planetlab/session", 'w').write(plc.GetSession().strip())
50         self.session = open("/etc/planetlab/session").read().strip()
51         
52     def __check_authentication(self):
53         # just a simple call to check authentication
54         def check():
55             if (self.node_id and self.key) or self.session:
56                 try:
57                     if self.AuthCheck() == 1: return True
58                 except:
59                     return False
60             return False
61         if not check():
62             if self.node_id and self.key:
63                 # if hmac fails, just make it fail
64                 raise Exception, "Unable to authenticate with hmac"
65             else:
66                 self.__update_session()
67                 if not check():
68                     raise Exception, "Unable to authenticate with session"
69     
70
71     def add_auth(self, function):
72         """
73         Returns a wrapper which adds an Auth struct as the first
74         argument when the function is called.
75         """
76
77         def canonicalize(args):
78             """
79             BootAuth canonicalization method. Parameter values are
80             collected, sorted, converted to strings, then hashed with
81             the node key.
82             """
83
84             values = []
85
86             for arg in args:
87                 if isinstance(arg, list) or isinstance(arg, tuple):
88                     # The old implementation did not recursively handle
89                     # lists of lists. But neither did the old API itself.
90                     values += canonicalize(arg)
91                 elif isinstance(arg, dict):
92                     # Yes, the comments in the old implementation are
93                     # misleading. Keys of dicts are not included in the
94                     # hash.
95                     values += canonicalize(arg.values())
96                 else:
97                     # We use unicode() instead of str().
98                     values.append(unicode(arg))
99
100             return values
101
102         def wrapper(*params):
103             """
104             Adds an Auth struct as the first argument when the
105             function is called.
106             """
107
108             if self.session is not None:
109                 # Use session authentication
110                 auth = {'AuthMethod': "session",
111                         'session': self.session}
112             else:
113                 # Yes, this is the "canonicalization" method used.
114                 args = canonicalize(params)
115                 args.sort()
116                 msg = "[" + "".join(args) + "]"
117
118                 # We encode in UTF-8 before calculating the HMAC, which is
119                 # an 8-bit algorithm.
120                 digest = hmac.new(self.key, msg.encode('utf-8'), sha).hexdigest()
121
122                 auth = {'AuthMethod': "hmac",
123                         'node_id': self.node_id,
124                         'value': digest}
125
126             # Automagically add auth struct to every call
127             params = (auth,) + params
128
129             return function(*params)
130
131         return wrapper
132
133     def __getattr__(self, methodname):
134         function = getattr(self.server, methodname)
135         return self.add_auth(function)