4 from PLC.Parameter import Parameter
5 from PLC.Nodes import Node, Nodes
6 from PLC.Sessions import Session, Sessions
11 Base class for all API authentication methods, as well as a class
12 that can be used to represent all supported API authentication
16 def __init__(self, auth = None):
18 auth = {'AuthMethod': Parameter(str, "Authentication method to use", optional = False)}
19 Parameter.__init__(self, auth, "API authentication structure")
21 class PasswordAuth(Auth):
23 PlanetLab version 3.x password authentication structure.
28 'AuthMethod': Parameter(str, "Authentication method to use, always 'password' or 'capability'", optional = False),
29 'Username': Parameter(str, "Username, typically an e-mail address", optional = False),
30 'AuthString': Parameter(str, "Authentication string, typically a password", optional = False),
31 'Tenant': Parameter(str, "User Tenant", optional = False),
34 class SessionAuth(Auth):
36 Secondary authentication method. After authenticating with a
37 primary authentication method, call GetSession() to generate a
38 session key that may be used for subsequent calls.
43 'AuthMethod': Parameter(str, "Authentication method to use, always 'session'", optional = False),
44 'session': Parameter(str, "Session key", optional = False)
47 def check(self, method, auth, *args):
48 # Method.type_check() should have checked that all of the
49 # mandatory fields were present.
50 assert auth.has_key('session')
53 sessions = Sessions(method.api, [auth['session']], expires = None)
55 raise PLCAuthenticationFailure, "SessionAuth: No such session"
59 if session['node_id'] is not None:
60 nodes = Nodes(method.api, {'node_id': session['node_id'], 'peer_id': None})
62 raise PLCAuthenticationFailure, "SessionAuth: No such node"
65 if 'node' not in method.roles:
66 # using PermissionDenied rather than AuthenticationFailure here because
67 # if that fails we don't want to delete the session..
68 raise PLCPermissionDenied, "SessionAuth: Not allowed to call method %s, missing 'node' role"%method.name
72 elif session['person_id'] is not None and session['expires'] > time.time():
73 persons = Persons(method.api, {'person_id': session['person_id'], 'enabled': True, 'peer_id': None})
75 raise PLCAuthenticationFailure, "SessionAuth: No such enabled account"
78 if not set(person['roles']).intersection(method.roles):
79 method_message="method %s has roles [%s]"%(method.name,','.join(method.roles))
80 person_message="caller %s has roles [%s]"%(person['email'],','.join(person['roles']))
81 # not PLCAuthenticationFailure b/c that would end the session..
82 raise PLCPermissionDenied, "SessionAuth: missing role, %s -- %s"%(method_message,person_message)
84 method.caller = person
87 raise PLCAuthenticationFailure, "SessionAuth: Invalid session"
89 except PLCAuthenticationFailure, fault:
95 PlanetLab version 3.x node authentication structure. Used by the
96 Boot Manager to make authenticated calls to the API based on a
97 unique node key or boot nonce value.
99 The original parameter serialization code did not define the byte
100 encoding of strings, or the string encoding of all other types. We
101 define the byte encoding to be UTF-8, and the string encoding of
102 all other types to be however Python version 2.3 unicode() encodes
107 Auth.__init__(self, {
108 'AuthMethod': Parameter(str, "Authentication method to use, always 'hmac'", optional = False),
109 'node_id': Parameter(int, "Node identifier", optional = False),
110 'value': Parameter(str, "HMAC of node key and method call", optional = False)
113 def canonicalize(self, args):
117 if isinstance(arg, list) or isinstance(arg, tuple):
118 # The old implementation did not recursively handle
119 # lists of lists. But neither did the old API itself.
120 values += self.canonicalize(arg)
121 elif isinstance(arg, dict):
122 # Yes, the comments in the old implementation are
123 # misleading. Keys of dicts are not included in the
125 values += self.canonicalize(arg.values())
127 # We use unicode() instead of str().
128 values.append(unicode(arg))
132 def check(self, method, auth, *args):
133 # Method.type_check() should have checked that all of the
134 # mandatory fields were present.
135 assert auth.has_key('node_id')
137 if 'node' not in method.roles:
138 raise PLCAuthenticationFailure, "BootAuth: Not allowed to call method, missing 'node' role"
141 nodes = Nodes(method.api, {'node_id': auth['node_id'], 'peer_id': None})
143 raise PLCAuthenticationFailure, "BootAuth: No such node"
146 # Jan 2011 : removing support for old boot CDs
150 raise PLCAuthenticationFailure, "BootAuth: No node key"
152 # Yes, this is the "canonicalization" method used.
153 args = self.canonicalize(args)
155 msg = "[" + "".join(args) + "]"
157 # We encode in UTF-8 before calculating the HMAC, which is
158 # an 8-bit algorithm.
159 # python 2.6 insists on receiving a 'str' as opposed to a 'unicode'
160 digest = hmac.new(str(key), msg.encode('utf-8'), sha).hexdigest()
162 if digest != auth['value']:
163 raise PLCAuthenticationFailure, "BootAuth: Call could not be authenticated"
167 except PLCAuthenticationFailure, fault:
169 notify_owners(method, node, 'authfail', include_pis = True, include_techs = True, fault = fault)
172 class AnonymousAuth(Auth):
174 PlanetLab version 3.x anonymous authentication structure.
178 Auth.__init__(self, {
179 'AuthMethod': Parameter(str, "Authentication method to use, always 'anonymous'", False),
182 def check(self, method, auth, *args):
183 if 'anonymous' not in method.roles:
184 raise PLCAuthenticationFailure, "AnonymousAuth: method cannot be called anonymously"
188 path = os.path.dirname(__file__) + "/Auth.d"
190 extensions = os.listdir(path)
193 for extension in extensions:
194 if extension.startswith("."):
196 if not extension.endswith(".py"):
198 execfile("%s/%s" % (path, extension))