resolve conflict manually
[sfa.git] / sfa / managers / registry_manager_pl.py
1 import types
2 import time 
3 from sfa.util.prefixTree import prefixTree
4 from sfa.util.record import SfaRecord
5 from sfa.util.table import SfaTable
6 from sfa.util.record import SfaRecord
7 from sfa.trust.gid import GID 
8 from sfa.util.namespace import get_leaf, get_authority, hrn_to_urn, hrn_to_pl_login_base, urn_to_hrn
9 from sfa.trust.credential import *
10 from sfa.trust.certificate import *
11 from sfa.util.faults import *
12 from sfa.trust.gid import create_uuid
13
14 def get_version(api):
15     version = {}
16     version['geni_api'] = 1
17     version['sfa'] = 1
18     return version
19
20 def get_credential(api, xrn, type, is_self=False):
21     # convert xrn to hrn     
22     if type:
23         hrn = urn_to_hrn(xrn)[0]
24     else:
25         hrn, type = urn_to_hrn(xrn)
26         
27     # Is this a root or sub authority
28     auth_hrn = api.auth.get_authority(hrn)
29     if not auth_hrn or hrn == api.config.SFA_INTERFACE_HRN:
30         auth_hrn = hrn
31     # get record info
32     auth_info = api.auth.get_auth_info(auth_hrn)
33     table = SfaTable()
34     records = table.findObjects({'type': type, 'hrn': hrn})
35     if not records:
36         raise RecordNotFound(hrn)
37     record = records[0]
38
39     # verify_cancreate_credential requires that the member lists
40     # (researchers, pis, etc) be filled in
41     api.fill_record_info(record)
42     if record['type']=='user':
43        if not record['enabled']:
44           raise AccountNotEnabled(": PlanetLab account %s is not enabled. Please contact your site PI" %(record['email']))
45
46     # get the callers gid
47     # if this is a self cred the record's gid is the caller's gid
48     if is_self:
49         caller_hrn = hrn
50         caller_gid = record.get_gid_object()
51     else:
52         caller_gid = api.auth.client_cred.get_gid_caller() 
53         caller_hrn = caller_gid.get_hrn()
54     
55     object_hrn = record.get_gid_object().get_hrn()
56     rights = api.auth.determine_user_rights(caller_hrn, record)
57     # make sure caller has rights to this object
58     if rights.is_empty():
59         raise PermissionError(caller_hrn + " has no rights to " + record['name'])
60
61     object_gid = GID(string=record['gid'])
62     new_cred = Credential(subject = object_gid.get_subject())
63     new_cred.set_gid_caller(caller_gid)
64     new_cred.set_gid_object(object_gid)
65     new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
66     #new_cred.set_pubkey(object_gid.get_pubkey())
67     new_cred.set_privileges(rights)
68     new_cred.get_privileges().delegate_all_privileges(True)
69     if 'expires' in record:
70         new_cred.set_expiration(int(record['expires']))
71     auth_kind = "authority,ma,sa"
72     # Parent not necessary, verify with certs
73     #new_cred.set_parent(api.auth.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
74     new_cred.encode()
75     new_cred.sign()
76
77     return new_cred.save_to_string(save_parents=True)
78
79
80 # The GENI GetVersion call
81 def GetVersion():
82     version = {}
83     version['geni_api'] = 1
84     return version
85
86 def resolve(api, xrns, type=None, full=True):
87
88     # load all know registry names into a prefix tree and attempt to find
89     # the longest matching prefix
90     if not isinstance(xrns, types.ListType):
91         xrns = [xrns]
92     hrns = [urn_to_hrn(xrn)[0] for xrn in xrns] 
93     # create a dict whre key is an registry hrn and its value is a
94     # hrns at that registry (determined by the known prefix tree).  
95     xrn_dict = {}
96     registries = api.registries
97     tree = prefixTree()
98     registry_hrns = registries.keys()
99     tree.load(registry_hrns)
100     for xrn in xrns:
101         registry_hrn = tree.best_match(urn_to_hrn(xrn)[0])
102         if registry_hrn not in xrn_dict:
103             xrn_dict[registry_hrn] = []
104         xrn_dict[registry_hrn].append(xrn)
105         
106     records = [] 
107     for registry_hrn in xrn_dict:
108         # skip the hrn without a registry hrn
109         # XX should we let the user know the authority is unknown?       
110         if not registry_hrn:
111             continue
112
113         # if the best match (longest matching hrn) is not the local registry,
114         # forward the request
115         xrns = xrn_dict[registry_hrn]
116         if registry_hrn != api.hrn:
117             credential = api.getCredential()
118             peer_records = registries[registry_hrn].Resolve(xrns, credential)
119             records.extend([SfaRecord(dict=record).as_dict() for record in peer_records])
120
121     # try resolving the remaining unfound records at the local registry
122     remaining_hrns = set(hrns).difference([record['hrn'] for record in records])
123     # convert set to list
124     remaining_hrns = [hrn for hrn in remaining_hrns] 
125     table = SfaTable()
126     local_records = table.findObjects({'hrn': remaining_hrns})
127     if full:
128         api.fill_record_info(local_records)
129     
130     # convert local record objects to dicts
131     records.extend([dict(record) for record in local_records])
132     if not records:
133         raise RecordNotFound(str(hrns))
134
135     if type:
136         records = filter(lambda rec: rec['type'] in [type], records)
137
138     return records
139
140 def list(api, xrn, origin_hrn=None):
141     hrn, type = urn_to_hrn(xrn)
142     # load all know registry names into a prefix tree and attempt to find
143     # the longest matching prefix
144     records = []
145     registries = api.registries
146     registry_hrns = registries.keys()
147     tree = prefixTree()
148     tree.load(registry_hrns)
149     registry_hrn = tree.best_match(hrn)
150    
151     #if there was no match then this record belongs to an unknow registry
152     if not registry_hrn:
153         raise MissingAuthority(xrn)
154     
155     # if the best match (longest matching hrn) is not the local registry,
156     # forward the request
157     records = []    
158     if registry_hrn != api.hrn:
159         credential = api.getCredential()
160         record_list = registries[registry_hrn].list(credential, xrn, origin_hrn)
161         records = [SfaRecord(dict=record).as_dict() for record in record_list]
162     
163     # if we still havnt found the record yet, try the local registry
164     if not records:
165         if not api.auth.hierarchy.auth_exists(hrn):
166             raise MissingAuthority(hrn)
167
168         table = SfaTable()
169         records = table.find({'authority': hrn})
170
171     return records
172
173
174 def register(api, record):
175
176     hrn, type = record['hrn'], record['type']
177     urn = hrn_to_urn(hrn,type)
178     # validate the type
179     if type not in ['authority', 'slice', 'node', 'user']:
180         raise UnknownSfaType(type) 
181     
182     # check if record already exists
183     table = SfaTable()
184     existing_records = table.find({'type': type, 'hrn': hrn})
185     if existing_records:
186         raise ExistingRecord(hrn)
187        
188     record = SfaRecord(dict = record)
189     record['authority'] = get_authority(record['hrn'])
190     type = record['type']
191     hrn = record['hrn']
192     api.auth.verify_object_permission(hrn)
193     auth_info = api.auth.get_auth_info(record['authority'])
194     pub_key = None
195     # make sure record has a gid
196     if 'gid' not in record:
197         uuid = create_uuid()
198         pkey = Keypair(create=True)
199         if 'key' in record and record['key']:
200             if isinstance(record['key'], types.ListType):
201                 pub_key = record['key'][0]
202             else:
203                 pub_key = record['key']
204             pkey = convert_public_key(pub_key)
205
206         gid_object = api.auth.hierarchy.create_gid(urn, uuid, pkey)
207         gid = gid_object.save_to_string(save_parents=True)
208         record['gid'] = gid
209         record.set_gid(gid)
210
211     if type in ["authority"]:
212         # update the tree
213         if not api.auth.hierarchy.auth_exists(hrn):
214             api.auth.hierarchy.create_auth(hrn_to_urn(hrn,'authority'))
215
216         # get the GID from the newly created authority
217         gid = auth_info.get_gid_object()
218         record.set_gid(gid.save_to_string(save_parents=True))
219         pl_record = api.sfa_fields_to_pl_fields(type, hrn, record)
220         sites = api.plshell.GetSites(api.plauth, [pl_record['login_base']])
221         if not sites:
222             pointer = api.plshell.AddSite(api.plauth, pl_record)
223         else:
224             pointer = sites[0]['site_id']
225
226         record.set_pointer(pointer)
227         record['pointer'] = pointer
228
229     elif (type == "slice"):
230         acceptable_fields=['url', 'instantiation', 'name', 'description']
231         pl_record = api.sfa_fields_to_pl_fields(type, hrn, record)
232         for key in pl_record.keys():
233             if key not in acceptable_fields:
234                 pl_record.pop(key)
235         slices = api.plshell.GetSlices(api.plauth, [pl_record['name']])
236         if not slices:
237              pointer = api.plshell.AddSlice(api.plauth, pl_record)
238         else:
239              pointer = slices[0]['slice_id']
240         record.set_pointer(pointer)
241         record['pointer'] = pointer
242
243     elif  (type == "user"):
244         persons = api.plshell.GetPersons(api.plauth, [record['email']])
245         if not persons:
246             pointer = api.plshell.AddPerson(api.plauth, dict(record))
247         else:
248             pointer = persons[0]['person_id']
249
250         if 'enabled' in record and record['enabled']:
251             api.plshell.UpdatePerson(api.plauth, pointer, {'enabled': record['enabled']})
252         # add this persons to the site only if he is being added for the first
253         # time by sfa and doesont already exist in plc
254         if not persons or not persons[0]['site_ids']:
255             login_base = get_leaf(record['authority'])
256             api.plshell.AddPersonToSite(api.plauth, pointer, login_base)
257
258         # What roles should this user have?
259         api.plshell.AddRoleToPerson(api.plauth, 'user', pointer)
260         # Add the user's key
261         if pub_key:
262             api.plshell.AddPersonKey(api.plauth, pointer, {'key_type' : 'ssh', 'key' : pub_key})
263
264     elif (type == "node"):
265         pl_record = api.sfa_fields_to_pl_fields(type, hrn, record)
266         login_base = hrn_to_pl_login_base(record['authority'])
267         nodes = api.plshell.GetNodes(api.plauth, [pl_record['hostname']])
268         if not nodes:
269             pointer = api.plshell.AddNode(api.plauth, login_base, pl_record)
270         else:
271             pointer = nodes[0]['node_id']
272
273     record['pointer'] = pointer
274     record.set_pointer(pointer)
275     record_id = table.insert(record)
276     record['record_id'] = record_id
277
278     # update membership for researchers, pis, owners, operators
279     api.update_membership(None, record)
280
281     return record.get_gid_object().save_to_string(save_parents=True)
282
283 def update(api, record_dict):
284     new_record = SfaRecord(dict = record_dict)
285     type = new_record['type']
286     hrn = new_record['hrn']
287     urn = hrn_to_urn(hrn,type)
288     api.auth.verify_object_permission(hrn)
289     table = SfaTable()
290     # make sure the record exists
291     records = table.findObjects({'type': type, 'hrn': hrn})
292     if not records:
293         raise RecordNotFound(hrn)
294     record = records[0]
295     record['last_updated'] = time.gmtime()
296
297     # Update_membership needs the membership lists in the existing record
298     # filled in, so it can see if members were added or removed
299     api.fill_record_info(record)
300
301     # Use the pointer from the existing record, not the one that the user
302     # gave us. This prevents the user from inserting a forged pointer
303     pointer = record['pointer']
304     # update the PLC information that was specified with the record
305
306     if (type == "authority"):
307         api.plshell.UpdateSite(api.plauth, pointer, new_record)
308
309     elif type == "slice":
310         pl_record=api.sfa_fields_to_pl_fields(type, hrn, new_record)
311         if 'name' in pl_record:
312             pl_record.pop('name')
313             api.plshell.UpdateSlice(api.plauth, pointer, pl_record)
314
315     elif type == "user":
316         # SMBAKER: UpdatePerson only allows a limited set of fields to be
317         #    updated. Ideally we should have a more generic way of doing
318         #    this. I copied the field names from UpdatePerson.py...
319         update_fields = {}
320         all_fields = new_record
321         for key in all_fields.keys():
322             if key in ['first_name', 'last_name', 'title', 'email',
323                        'password', 'phone', 'url', 'bio', 'accepted_aup',
324                        'enabled']:
325                 update_fields[key] = all_fields[key]
326         api.plshell.UpdatePerson(api.plauth, pointer, update_fields)
327
328         if 'key' in new_record and new_record['key']:
329             # must check this key against the previous one if it exists
330             persons = api.plshell.GetPersons(api.plauth, [pointer], ['key_ids'])
331             person = persons[0]
332             keys = person['key_ids']
333             keys = api.plshell.GetKeys(api.plauth, person['key_ids'])
334             key_exists = False
335             if isinstance(new_record['key'], types.ListType):
336                 new_key = new_record['key'][0]
337             else:
338                 new_key = new_record['key']
339             
340             # Delete all stale keys
341             for key in keys:
342                 if new_record['key'] != key['key']:
343                     api.plshell.DeleteKey(api.plauth, key['key_id'])
344                 else:
345                     key_exists = True
346             if not key_exists:
347                 api.plshell.AddPersonKey(api.plauth, pointer, {'key_type': 'ssh', 'key': new_key})
348
349             # update the openssl key and gid
350             pkey = convert_public_key(new_key)
351             uuid = create_uuid()
352             gid_object = api.auth.hierarchy.create_gid(urn, uuid, pkey)
353             gid = gid_object.save_to_string(save_parents=True)
354             record['gid'] = gid
355             record = SfaRecord(dict=record)
356             table.update(record)
357
358     elif type == "node":
359         api.plshell.UpdateNode(api.plauth, pointer, new_record)
360
361     else:
362         raise UnknownSfaType(type)
363
364     # update membership for researchers, pis, owners, operators
365     api.update_membership(record, new_record)
366     
367     return 1 
368
369 def remove(api, xrn, type, origin_hrn=None):
370     # convert xrn to hrn     
371     if type:
372         hrn = urn_to_hrn(xrn)[0]
373     else:
374         hrn, type = urn_to_hrn(xrn)    
375
376     table = SfaTable()
377     filter = {'hrn': hrn}
378     if type and type not in ['all', '*']:
379         filter['type'] = type
380     records = table.find(filter)
381     if not records:
382         raise RecordNotFound(hrn)
383     record = records[0]
384     type = record['type']
385
386     credential = api.getCredential()
387     registries = api.registries
388
389     # Try to remove the object from the PLCDB of federated agg.
390     # This is attempted before removing the object from the local agg's PLCDB and sfa table
391     if hrn.startswith(api.hrn) and type in ['user', 'slice', 'authority']:
392         for registry in registries:
393             if registry not in [api.hrn]:
394                 try:
395                     result=registries[registry].remove_peer_object(credential, record, origin_hrn)
396                 except:
397                     pass
398     if type == "user":
399         persons = api.plshell.GetPersons(api.plauth, record['pointer'])
400         # only delete this person if he has site ids. if he doesnt, it probably means
401         # he was just removed from a site, not actually deleted
402         if persons and persons[0]['site_ids']:
403             api.plshell.DeletePerson(api.plauth, record['pointer'])
404     elif type == "slice":
405         if api.plshell.GetSlices(api.plauth, record['pointer']):
406             api.plshell.DeleteSlice(api.plauth, record['pointer'])
407     elif type == "node":
408         if api.plshell.GetNodes(api.plauth, record['pointer']):
409             api.plshell.DeleteNode(api.plauth, record['pointer'])
410     elif type == "authority":
411         if api.plshell.GetSites(api.plauth, record['pointer']):
412             api.plshell.DeleteSite(api.plauth, record['pointer'])
413     else:
414         raise UnknownSfaType(type)
415
416     table.remove(record)
417
418     return 1
419
420 def remove_peer_object(api, record, origin_hrn=None):
421     pass
422
423 def register_peer_object(api, record, origin_hrn=None):
424     pass