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