08496a40901058151eedd514e99005395c359298
[sfa.git] / registry / registry.py
1 import tempfile
2 import os
3
4 import sys
5
6 from cert import *
7 from gid import *
8 from geniserver import *
9 from excep import *
10 from hierarchy import *
11 from misc import *
12 from record import *
13
14 def geni_fields_to_pl_fields(type, hrn, geni_fields, pl_fields):
15     if type=="user":
16         if not "email" in pl_fields:
17             if not "email" in geni_fields:
18                 raise MissingGeniInfo("email")
19             pl_fields["email"] = geni_fields["email"]
20
21         if not "first_name" in pl_fields:
22             pl_fields["first_name"] = "geni"
23
24         if not "last_name" in pl_fields:
25             pl_fields["last_name"] = hrn
26
27
28 class Registry(GeniServer):
29     def __init__(self, ip, port, key_file, cert_file):
30         GeniServer.__init__(self, ip, port, key_file, cert_file)
31
32         # get PL account settings from config module
33         self.pl_auth = get_pl_auth()
34
35         # connect to planetlab
36         if "Url" in self.pl_auth:
37             self.connect_remote_shell()
38         else:
39             self.connect_local_shell()
40
41     def connect_remote_shell(self):
42         import remoteshell
43         self.shell = remoteshell.RemoteShell()
44
45     def connect_local_shell(self):
46         import PLC.Shell
47         self.shell = PLC.Shell.Shell(globals = globals())
48
49     def register_functions(self):
50         GeniServer.register_functions(self)
51         self.server.register_function(self.create_gid)
52         self.server.register_function(self.get_self_credential)
53         self.server.register_function(self.get_credential)
54         self.server.register_function(self.get_gid)
55         self.server.register_function(self.register)
56         self.server.register_function(self.remove)
57         self.server.register_function(self.update)
58         self.server.register_function(self.list)
59         self.server.register_function(self.resolve)
60
61     def get_auth_info(self, name):
62         return AuthHierarchy.get_auth_info(name)
63
64     def get_auth_table(self, auth_name):
65         auth_info = self.get_auth_info(auth_name)
66
67         table = GeniTable(hrn=auth_name,
68                           cninfo=auth_info.get_dbinfo())
69
70         # if the table doesn't exist, then it means we haven't put any records
71         # into this authority yet.
72
73         if not table.exists():
74             report.trace("Registry: creating table for authority " + auth_name)
75             table.create()
76
77         return table
78
79     def verify_auth_belongs_to_me(self, name):
80         # get_auth_info will throw an exception if the authority does not
81         # exist
82         self.get_auth_info(name)
83
84     def verify_object_belongs_to_me(self, name):
85         auth_name = get_authority(name)
86         if not auth_name:
87             # the root authority belongs to the registry by default?
88             # TODO: is this true?
89             return
90         self.verify_auth_belongs_to_me(auth_name)
91
92     def verify_object_permission(self, name):
93         object_hrn = self.object_gid.get_hrn()
94         if object_hrn == name:
95             return
96         if name.startswith(object_hrn + "."):
97             return
98         raise PermissionError(name)
99
100     def fill_record_pl_info(self, record):
101         type = record.get_type()
102         pointer = record.get_pointer()
103
104         if (type == "sa") or (type == "ma"):
105             pl_res = self.shell.GetSites(self.pl_auth, [pointer])
106         elif (type == "slice"):
107             pl_res = self.shell.GetSlices(self.pl_auth, [pointer])
108         elif (type == "user"):
109             pl_res = self.shell.GetPersons(self.pl_auth, [pointer])
110         elif (type == "node"):
111             pl_res = self.shell.GetNodes(self.pl_auth, [pointer])
112         else:
113             raise UnknownGeniType(type)
114
115         if not pl_res:
116             # the planetlab record no longer exists
117             # TODO: delete the geni record ?
118             raise PlanetLabRecordDoesNotExist(record.get_name())
119
120         record.set_pl_info(pl_res[0])
121
122     def fill_record_geni_info(self, record):
123         pass
124
125     def fill_record_info(self, record):
126         self.fill_record_pl_info(record)
127         self.fill_record_geni_info(record)
128
129     def register(self, cred, record_dict):
130         self.decode_authentication(cred, "register")
131
132         record = GeniRecord(dict = record_dict)
133         type = record.get_type()
134         name = record.get_name()
135
136         auth_name = get_authority(name)
137         self.verify_object_permission(auth_name)
138         auth_info = self.get_auth_info(auth_name)
139         table = self.get_auth_table(auth_name)
140
141         pkey = None
142
143         # check if record already exists
144         existing_records = table.resolve(name, type)
145         if existing_records:
146             raise ExistingRecord(name)
147
148         if (type == "sa") or (type=="ma"):
149             # update the tree
150             if not AuthHierarchy.auth_exists(name):
151                 AuthHierarchy.create_auth(name)
152
153             # authorities are special since they are managed by the registry
154             # rather than by the caller. We create our own GID for the
155             # authority rather than relying on the caller to supply one.
156
157             # get the public key from the newly created authority
158             child_auth_info = self.get_auth_info(name)
159             pkey = child_auth_info.get_pkey_object()
160             gid = AuthHierarchy.create_gid(name, create_uuid(), pkey)
161             record.set_gid(gid.save_to_string())
162
163             site_fields = record.get_pl_info()
164             pointer = self.shell.AddSite(self.pl_auth, site_fields)
165             record.set_pointer(pointer)
166
167         elif (type == "slice"):
168             slice_fields = record.get_pl_info()
169             pointer = self.shell.AddSlice(self.pl_auth, slice_fields)
170             record.set_pointer(pointer)
171
172         elif (type == "user"):
173             # TODO: extract pkey from user_fields?
174             geni_fields = record.get_geni_info()
175             user_fields = record.get_pl_info()
176
177             geni_fields_to_pl_fields(type, name, geni_fields, user_fields)
178
179             pointer = self.shell.AddPerson(self.pl_auth, user_fields)
180             record.set_pointer(pointer)
181
182         elif (type == "node"):
183             node_fields = record.get_pl_info()
184             pointer = self.shell.AddNode(self.pl_auth, login_base, node_fields)
185             record.set_pointer(pointer)
186
187         else:
188             raise UnknownGeniType(type)
189
190         table.insert(record)
191
192         return record.get_gid_object().save_to_string()
193
194     def remove(self, cred, record_dict):
195         self.decode_authentication(cred, "remove")
196
197         record = GeniRecord(dict = record_dict)
198         type = record.get_type()
199
200         self.verify_object_permission(record.get_name())
201
202         auth_name = get_authority(record.get_name())
203         table = self.get_auth_table(auth_name)
204
205         # let's not trust that the caller has a well-formed record (a forged
206         # pointer field could be a disaster), so look it up ourselves
207         record_list = table.resolve(type, record.get_name())
208         if not record_list:
209             raise RecordNotFound(name)
210         record = record_list[0]
211
212         # TODO: sa, ma
213         if type == "user":
214             self.shell.DeletePerson(self.pl_auth, record.get_pointer())
215         elif type == "slice":
216             self.shell.DeleteSlice(self.pl_auth, record.get_pointer())
217         elif type == "node":
218             self.shell.DeleteNode(self.pl_auth, record.get_pointer())
219         else:
220             raise UnknownGeniType(type)
221
222         table.remove(record)
223
224         return True
225
226     def update(self, cred, record_dict):
227         self.decode_authentication(cred, "update")
228
229         record = GeniRecord(dict = record_dict)
230         type = record.get_type()
231
232         self.verify_object_permission(record.get_name())
233
234         auth_name = get_authority(record.get_name())
235         table = self.get_auth_table(auth_name)
236
237         # make sure the record exists
238         existing_record = table.resolve(type, record.get_name(), must_exist=True)[0]
239         pointer = existing_record.get_pointer()
240
241         if (type == "sa") or (type == "ma"):
242             self.shell.UpdateSite(self.pl_auth, pointer, record.get_pl_info())
243
244         elif type == "slice":
245             self.shell.UpdateSlice(self.pl_auth, pointer, record.get_pl_info())
246
247         elif type == "user":
248             self.shell.UpdatePerson(self.pl_auth, pointer, record.get_pl_info())
249
250         elif type == "node":
251             self.shell.UpdateNode(self.pl_auth, pointer, record.get_pl_info())
252
253         else:
254             raise UnknownGeniType(type)
255
256     # TODO: List doesn't take an hrn and uses the hrn contained in the
257     #    objectGid of the credential. Does this mean the only way to list an
258     #    authority is by having a credential for that authority? 
259     def list(self, cred):
260         self.decode_authentication(cred, "list")
261
262         auth_name = self.object_gid.get_hrn()
263         table = self.get_auth_table(auth_name)
264
265         records = table.list()
266
267         good_records = []
268         for record in records:
269             try:
270                 self.fill_record_info(record)
271                 good_records.append(record)
272             except PlanetLabRecordDoesNotExist:
273                 # silently drop the ones that are missing in PL.
274                 # is this the right thing to do?
275                 report.error("ignoring geni record " + record.get_name() + " because pl record does not exist")
276                 table.remove(record)
277
278         dicts = []
279         for record in good_records:
280             dicts.append(record.as_dict())
281
282         return dicts
283
284         return dict_list
285
286     def resolve_raw(self, type, name, must_exist=True):
287         auth_name = get_authority(name)
288
289         table = self.get_auth_table(auth_name)
290
291         records = table.resolve(type, name)
292
293         if (not records) and must_exist:
294             raise RecordNotFound(name)
295
296         good_records = []
297         for record in records:
298             try:
299                 self.fill_record_info(record)
300                 good_records.append(record)
301             except PlanetLabRecordDoesNotExist:
302                 # silently drop the ones that are missing in PL.
303                 # is this the right thing to do?
304                 report.error("ignoring geni record " + record.get_hrn() + " because pl record does not exist")
305                 table.remove(record)
306
307         return good_records
308
309     def resolve(self, cred, name):
310         self.decode_authentication(cred, "resolve")
311
312         records = self.resolve_raw("*", name)
313         dicts = []
314         for record in records:
315             dicts.append(record.as_dict())
316
317         return dicts
318
319     def get_gid(self, name):
320         self.verify_object_belongs_to_me(name) # XXX Fixme
321         records = self.resolve_raw("*", name)
322         gid_string_list = []
323         for record in records:
324             gid = record.get_gid()
325             gid_string_list.append(gid.save_to_string())
326         return gid_string_list
327
328     def determine_rights(self, type, name):
329         rl = RightList()
330
331         # rights seem to be somewhat redundant with the type of the credential.
332         # For example, a "sa" credential implies the authority right, because
333         # a sa credential cannot be issued to a user who is not an owner of
334         # the authority
335
336         if type == "user":
337             rl.add("refresh")
338             rl.add("resolve")
339         elif type == "sa":
340             rl.add("authority")
341         elif type == "ma":
342             rl.add("authority")
343         elif type == "slice":
344             rl.add("embed")
345             rl.add("bind")
346             rl.add("control")
347         elif type == "component":
348             rl.add("operator")
349
350         return rl
351
352
353     def get_self_credential(self, type, name):
354         self.verify_object_belongs_to_me(name)
355
356         auth_hrn = get_authority(name)
357         auth_info = self.get_auth_info(auth_hrn)
358
359         # find a record that matches
360         records = self.resolve_raw(type, name, must_exist=True)
361         record = records[0]
362
363         gid = record.get_gid_object()
364         peer_cert = self.server.peer_cert
365         if not peer_cert.is_pubkey(gid.get_pubkey()):
366            raise ConnectionKeyGIDMismatch(gid.get_subject())
367
368         # create the credential
369         gid = record.get_gid_object()
370         cred = Credential(subject = gid.get_subject())
371         cred.set_gid_caller(gid)
372         cred.set_gid_object(gid)
373         cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
374         cred.set_pubkey(gid.get_pubkey())
375
376         rl = self.determine_rights(type, name)
377         cred.set_privileges(rl)
378
379         cred.encode()
380         cred.sign()
381
382         return cred.save_to_string()
383
384     def get_credential(self, cred, type, name):
385         if not cred:
386             return get_self_credential(self, type, name)
387
388         self.decode_authentication(cred, "getcredential")
389
390         self.verify_object_belongs_to_me(name)
391
392         auth_hrn = get_authority(name)
393         auth_info = self.get_auth_info(auth_hrn)
394
395         records = self.resolve_raw(type, name, must_exist=True)
396         record = records[0]
397
398         # TODO: Check permission that self.client_cred can access the object
399
400         object_gid = record.get_gid_object()
401         new_cred = Credential(subject = object_gid.get_subject())
402         new_cred.set_gid_caller(self.client_gid)
403         new_cred.set_gid_object(object_gid)
404         new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
405         new_cred.set_pubkey(object_gid.get_pubkey())
406
407         rl = self.determine_rights(type, name)
408         new_cred.set_privileges(rl)
409
410         new_cred.encode()
411         new_cred.sign()
412
413         return new_cred.save_to_string()
414
415     def create_gid(self, cred, name, uuid, pubkey_str):
416         self.decode_authentication(cred, "getcredential")
417
418         self.verify_object_belongs_to_me(name)
419
420         self.verify_object_permission(name)
421
422         if uuid == None:
423             uuid = create_uuid()
424
425         pkey = Keypair()
426         pkey.load_pubkey_from_string(pubkey_str)
427         gid = AuthHierarchy.create_gid(name, uuid, pkey)
428
429         return gid.save_to_string() ### save_parents=True)
430
431
432 if __name__ == "__main__":
433     global AuthHierarchy
434
435     key_file = "server.key"
436     cert_file = "server.cert"
437
438     # if no key is specified, then make one up
439     if (not os.path.exists(key_file)) or (not os.path.exists(cert_file)):
440         key = Keypair(create=True)
441         key.save_to_file(key_file)
442
443         cert = Certificate(subject="registry")
444         cert.set_issuer(key=key, subject="registry")
445         cert.set_pubkey(key)
446         cert.sign()
447         cert.save_to_file(cert_file)
448
449     AuthHierarchy = Hierarchy()
450
451     s = Registry("localhost", 12345, key_file, cert_file)
452     s.run()
453