fix missing module imports
[sfa.git] / plc / registry.py
1 ##
2 # Registry is a GeniServer that implements the Registry interface
3
4 import tempfile
5 import os
6 import time
7 import sys
8
9 from util.credential import Credential
10 from util.hierarchy import Hierarchy
11 from util.trustedroot import TrustedRootList
12 from util.cert import Keypair, Certificate
13 from util.gid import GID
14 from util.geniserver import GeniServer
15 from util.record import GeniRecord
16 from util.rights import RightList
17 from util.genitable import GeniTable
18 from util.geniticket import Ticket
19 from util.excep import *
20 from util.misc import *
21
22 from util.config import *
23
24 ##
25 # Convert geni fields to PLC fields for use when registering up updating
26 # registry record in the PLC database
27 #
28 # @param type type of record (user, slice, ...)
29 # @param hrn human readable name
30 # @param geni_fields dictionary of geni fields
31 # @param pl_fields dictionary of PLC fields (output)
32
33 def geni_fields_to_pl_fields(type, hrn, geni_fields, pl_fields):
34     if type == "user":
35         if not "email" in pl_fields:
36             if not "email" in geni_fields:
37                 raise MissingGeniInfo("email")
38             pl_fields["email"] = geni_fields["email"]
39
40         if not "first_name" in pl_fields:
41             pl_fields["first_name"] = "geni"
42
43         if not "last_name" in pl_fields:
44             pl_fields["last_name"] = hrn
45
46     elif type == "slice":
47         if not "instantiation" in pl_fields:
48             pl_fields["instantiation"] = "delegated"  # "plc-instantiated"
49         if not "name" in pl_fields:
50             pl_fields["name"] = hrn_to_pl_slicename(hrn)
51         if not "max_nodes" in pl_fields:
52             pl_fields["max_nodes"] = 10
53
54     elif type == "node":
55         if not "hostname" in pl_fields:
56             if not "dns" in geni_fields:
57                 raise MissingGeniInfo("dns")
58             pl_fields["hostname"] = geni_fields["dns"]
59
60         if not "model" in pl_fields:
61             pl_fields["model"] = "geni"
62
63     elif type == "sa":
64         pl_fields["login_base"] = hrn_to_pl_login_base(hrn)
65
66         if not "name" in pl_fields:
67             pl_fields["name"] = hrn
68
69         if not "abbreviated_name" in pl_fields:
70             pl_fields["abbreviated_name"] = hrn
71
72         if not "enabled" in pl_fields:
73             pl_fields["enabled"] = True
74
75         if not "is_public" in pl_fields:
76             pl_fields["is_public"] = True
77
78 ##
79 # Registry is a GeniServer that serves registry and slice operations at PLC.
80
81 class Registry(GeniServer):
82     ##
83     # Create a new registry object.
84     #
85     # @param ip the ip address to listen on
86     # @param port the port to listen on
87     # @param key_file private key filename of registry
88     # @param cert_file certificate filename containing public key (could be a GID file)
89
90     def __init__(self, ip, port, key_file, cert_file):
91         GeniServer.__init__(self, ip, port, key_file, cert_file)
92
93         # get PL account settings from config module
94         self.pl_auth = get_pl_auth()
95
96         # connect to planetlab
97         if "Url" in self.pl_auth:
98             self.connect_remote_shell()
99         else:
100             self.connect_local_shell()
101
102     ##
103     # Connect to a remote shell via XMLRPC
104
105     def connect_remote_shell(self):
106         import remoteshell
107         self.shell = remoteshell.RemoteShell()
108
109     ##
110     # Connect to a local shell via local API functions
111
112     def connect_local_shell(self):
113         import PLC.Shell
114         self.shell = PLC.Shell.Shell(globals = globals())
115
116     ##
117     # Register the server RPCs for the registry
118
119     def register_functions(self):
120         GeniServer.register_functions(self)
121         # registry interface
122         self.server.register_function(self.create_gid)
123         self.server.register_function(self.get_self_credential)
124         self.server.register_function(self.get_credential)
125         self.server.register_function(self.get_gid)
126         self.server.register_function(self.register)
127         self.server.register_function(self.remove)
128         self.server.register_function(self.update)
129         self.server.register_function(self.list)
130         self.server.register_function(self.resolve)
131
132     ##
133     # Given an authority name, return the information for that authority. This
134     # is basically a stub that calls the hierarchy module.
135     #
136     # @param auth_hrn human readable name of authority
137
138     def get_auth_info(self, auth_hrn):
139         return self.hierarchy.get_auth_info(auth_hrn)
140
141     ##
142     # Given an authority name, return the database table for that authority. If
143     # the database table does not exist, then one will be automatically
144     # created.
145     #
146     # @param auth_name human readable name of authority
147
148     def get_auth_table(self, auth_name):
149         auth_info = self.get_auth_info(auth_name)
150
151         table = GeniTable(hrn=auth_name,
152                           cninfo=auth_info.get_dbinfo())
153
154         # if the table doesn't exist, then it means we haven't put any records
155         # into this authority yet.
156
157         if not table.exists():
158             report.trace("Registry: creating table for authority " + auth_name)
159             table.create()
160
161         return table
162
163     ##
164     # Verify that an authority belongs to this registry. This is basically left
165     # up to the implementation of the hierarchy module. If the specified name
166     # does not belong to this registry, an exception is thrown indicating the
167     # caller should contact someone else.
168     #
169     # @param auth_name human readable name of authority
170
171     def verify_auth_belongs_to_me(self, name):
172         # get_auth_info will throw an exception if the authority does not
173         # exist
174         self.get_auth_info(name)
175
176     ##
177     # Verify that an object belongs to this registry. By extension, this implies
178     # that the authority that owns the object belongs to this registry. If the
179     # object does not belong to this registry, then an exception is thrown.
180     #
181     # @param name human readable name of object
182
183     def verify_object_belongs_to_me(self, name):
184         auth_name = get_authority(name)
185         if not auth_name:
186             # the root authority belongs to the registry by default?
187             # TODO: is this true?
188             return
189         self.verify_auth_belongs_to_me(auth_name)
190
191     ##
192     # Verify that the object_gid that was specified in the credential allows
193     # permission to the object 'name'. This is done by a simple prefix test.
194     # For example, an object_gid for planetlab.us.arizona would match the
195     # objects planetlab.us.arizona.slice1 and planetlab.us.arizona.
196     #
197     # @param name human readable name to test
198
199     def verify_object_permission(self, name):
200         object_hrn = self.object_gid.get_hrn()
201         if object_hrn == name:
202             return
203         if name.startswith(object_hrn + "."):
204             return
205         raise PermissionError(name)
206
207     ##
208     # Fill in the planetlab-specific fields of a Geni record. This involves
209     # calling the appropriate PLC methods to retrieve the database record for
210     # the object.
211     #
212     # PLC data is filled into the pl_info field of the record.
213     #
214     # @param record record to fill in fields (in/out param)
215
216     def fill_record_pl_info(self, record):
217         type = record.get_type()
218         pointer = record.get_pointer()
219
220         # records with pointer==-1 do not have plc info associated with them.
221         # for example, the top level authority records which are
222         # authorities, but not PL "sites"
223         if pointer == -1:
224             record.set_pl_info({})
225             return
226
227         if (type == "sa") or (type == "ma"):
228             pl_res = self.shell.GetSites(self.pl_auth, [pointer])
229         elif (type == "slice"):
230             pl_res = self.shell.GetSlices(self.pl_auth, [pointer])
231         elif (type == "user"):
232             pl_res = self.shell.GetPersons(self.pl_auth, [pointer])
233         elif (type == "node"):
234             pl_res = self.shell.GetNodes(self.pl_auth, [pointer])
235         else:
236             raise UnknownGeniType(type)
237
238         if not pl_res:
239             # the planetlab record no longer exists
240             # TODO: delete the geni record ?
241             raise PlanetLabRecordDoesNotExist(record.get_name())
242
243         record.set_pl_info(pl_res[0])
244
245     ##
246     # Look up user records given PLC user-ids. This is used as part of the
247     # process for reverse-mapping PLC records into Geni records.
248     #
249     # @param auth_table database table for the authority that holds the user records
250     # @param user_id_list list of user ids
251     # @param role either "*" or a string describing the role to look for ("pi", "user", ...)
252     #
253     # TODO: This function currently only searches one authority because it would
254     # be inefficient to brute-force search all authorities for a user id. The
255     # solution would likely be to implement a reverse mapping of user-id to
256     # (type, hrn) pairs.
257
258     def lookup_users(self, auth_table, user_id_list, role="*"):
259         record_list = []
260         for person_id in user_id_list:
261             user_records = auth_table.find("user", person_id, "pointer")
262             for user_record in user_records:
263                 self.fill_record_info(user_record)
264
265                 user_roles = user_record.get_pl_info().get("roles")
266                 if (role=="*") or (role in user_roles):
267                     record_list.append(user_record.get_name())
268         return record_list
269
270     ##
271     # Fill in the geni-specific fields of the record.
272     #
273     # Note: It is assumed the fill_record_pl_info() has already been performed
274     # on the record.
275
276     def fill_record_geni_info(self, record):
277         geni_info = {}
278         type = record.get_type()
279
280         if (type == "slice"):
281             auth_table = self.get_auth_table(get_authority(record.get_name()))
282             person_ids = record.pl_info.get("person_ids", [])
283             researchers = self.lookup_users(auth_table, person_ids)
284             geni_info['researcher'] = researchers
285
286         elif (type == "sa"):
287             auth_table = self.get_auth_table(record.get_name())
288             person_ids = record.pl_info.get("person_ids", [])
289             pis = self.lookup_users(auth_table, person_ids, "pi")
290             geni_info['pi'] = pis
291             # TODO: OrganizationName
292
293         elif (type == "ma"):
294             auth_table = self.get_auth_table(record.get_name())
295             person_ids = record.pl_info.get("person_ids", [])
296             operators = self.lookup_users(auth_table, person_ids, "tech")
297             geni_info['operator'] = operators
298             # TODO: OrganizationName
299
300             auth_table = self.get_auth_table(record.get_name())
301             person_ids = record.pl_info.get("person_ids", [])
302             owners = self.lookup_users(auth_table, person_ids, "admin")
303             geni_info['owner'] = owners
304
305         elif (type == "node"):
306             geni_info['dns'] = record.pl_info.get("hostname", "")
307             # TODO: URI, LatLong, IP, DNS
308
309         elif (type == "user"):
310             geni_info['email'] = record.pl_info.get("email", "")
311             # TODO: PostalAddress, Phone
312
313         record.set_geni_info(geni_info)
314
315     ##
316     # Given a Geni record, fill in the PLC-specific and Geni-specific fields
317     # in the record.
318
319     def fill_record_info(self, record):
320         self.fill_record_pl_info(record)
321         self.fill_record_geni_info(record)
322
323     ##
324     # GENI API: register
325     #
326     # Register an object with the registry. In addition to being stored in the
327     # Geni database, the appropriate records will also be created in the
328     # PLC databases
329     #
330     # @param cred credential string
331     # @param record_dict dictionary containing record fields
332
333     def register(self, cred, record_dict):
334         self.decode_authentication(cred, "register")
335
336         record = GeniRecord(dict = record_dict)
337         type = record.get_type()
338         name = record.get_name()
339
340         auth_name = get_authority(name)
341         self.verify_object_permission(auth_name)
342         auth_info = self.get_auth_info(auth_name)
343         table = self.get_auth_table(auth_name)
344
345         pkey = None
346
347         # check if record already exists
348         existing_records = table.resolve(type, name)
349         if existing_records:
350             raise ExistingRecord(name)
351
352         if (type == "sa") or (type=="ma"):
353             # update the tree
354             if not self.hierarchy.auth_exists(name):
355                 self.hierarchy.create_auth(name)
356
357             # authorities are special since they are managed by the registry
358             # rather than by the caller. We create our own GID for the
359             # authority rather than relying on the caller to supply one.
360
361             # get the GID from the newly created authority
362             child_auth_info = self.get_auth_info(name)
363             gid = auth_info.get_gid_object()
364             record.set_gid(gid.save_to_string(save_parents=True))
365
366             geni_fields = record.get_geni_info()
367             site_fields = record.get_pl_info()
368
369             # if registering a sa, see if a ma already exists
370             # if registering a ma, see if a sa already exists
371             if (type == "sa"):
372                 other_rec = table.resolve("ma", record.get_name())
373             elif (type == "ma"):
374                 other_rec = table.resolve("sa", record.get_name())
375
376             if other_rec:
377                 print "linking ma and sa to the same plc site"
378                 pointer = other_rec[0].get_pointer()
379             else:
380                 geni_fields_to_pl_fields(type, name, geni_fields, site_fields)
381                 print "adding site with fields", site_fields
382                 pointer = self.shell.AddSite(self.pl_auth, site_fields)
383
384             record.set_pointer(pointer)
385
386         elif (type == "slice"):
387             geni_fields = record.get_geni_info()
388             slice_fields = record.get_pl_info()
389
390             geni_fields_to_pl_fields(type, name, geni_fields, slice_fields)
391
392             pointer = self.shell.AddSlice(self.pl_auth, slice_fields)
393             record.set_pointer(pointer)
394
395         elif (type == "user"):
396             geni_fields = record.get_geni_info()
397             user_fields = record.get_pl_info()
398
399             geni_fields_to_pl_fields(type, name, geni_fields, user_fields)
400
401             pointer = self.shell.AddPerson(self.pl_auth, user_fields)
402             record.set_pointer(pointer)
403
404         elif (type == "node"):
405             geni_fields = record.get_geni_info()
406             node_fields = record.get_pl_info()
407
408             geni_fields_to_pl_fields(type, name, geni_fields, node_fields)
409
410             login_base = hrn_to_pl_login_base(auth_name)
411
412             print "calling addnode with", login_base, node_fields
413             pointer = self.shell.AddNode(self.pl_auth, login_base, node_fields)
414             record.set_pointer(pointer)
415
416         else:
417             raise UnknownGeniType(type)
418
419         table.insert(record)
420
421         return record.get_gid_object().save_to_string(save_parents=True)
422
423     ##
424     # GENI API: remove
425     #
426     # Remove an object from the registry. If the object represents a PLC object,
427     # then the PLC records will also be removed.
428     #
429     # @param cred credential string
430     # @param record_dict dictionary containing record fields. The only relevant
431     #     fields of the record are 'name' and 'type', which are used to lookup
432     #     the current copy of the record in the Geni database, to make sure
433     #     that the appopriate record is removed.
434
435     def remove(self, cred, record_dict):
436         self.decode_authentication(cred, "remove")
437
438         record = GeniRecord(dict = record_dict)
439         type = record.get_type()
440
441         self.verify_object_permission(record.get_name())
442
443         auth_name = get_authority(record.get_name())
444         table = self.get_auth_table(auth_name)
445
446         # let's not trust that the caller has a well-formed record (a forged
447         # pointer field could be a disaster), so look it up ourselves
448         record_list = table.resolve(type, record.get_name())
449         if not record_list:
450             raise RecordNotFound(name)
451         record = record_list[0]
452
453         # TODO: sa, ma
454         if type == "user":
455             self.shell.DeletePerson(self.pl_auth, record.get_pointer())
456         elif type == "slice":
457             self.shell.DeleteSlice(self.pl_auth, record.get_pointer())
458         elif type == "node":
459             self.shell.DeleteNode(self.pl_auth, record.get_pointer())
460         elif (type == "sa") or (type == "ma"):
461             if (type == "sa"):
462                 other_rec = table.resolve("ma", record.get_name())
463             elif (type == "ma"):
464                 other_rec = table.resolve("sa", record.get_name())
465
466             if other_rec:
467                 # sa and ma both map to a site, so if we are deleting one
468                 # but the other still exists, then do not delete the site
469                 print "not removing site", record.get_name(), "because either sa or ma still exists"
470                 pass
471             else:
472                 print "removing site", record.get_name()
473                 self.shell.DeleteSite(self.pl_auth, record.get_pointer())
474         else:
475             raise UnknownGeniType(type)
476
477         table.remove(record)
478
479         return True
480
481     ##
482     # GENI API: Register
483     #
484     # Update an object in the registry. Currently, this only updates the
485     # PLC information associated with the record. The Geni fields (name, type,
486     # GID) are fixed.
487     #
488     # The record is expected to have the pl_info field filled in with the data
489     # that should be updated.
490     #
491     # TODO: The geni_info member of the record should be parsed and the pl_info
492     # adjusted as necessary (add/remove users from a slice, etc)
493     #
494     # @param cred credential string specifying rights of the caller
495     # @param record a record dictionary to be updated
496
497     def update(self, cred, record_dict):
498         self.decode_authentication(cred, "update")
499
500         record = GeniRecord(dict = record_dict)
501         type = record.get_type()
502
503         self.verify_object_permission(record.get_name())
504
505         auth_name = get_authority(record.get_name())
506         table = self.get_auth_table(auth_name)
507
508         # make sure the record exists
509         existing_record_list = table.resolve(type, record.get_name())
510         if not existing_record_list:
511             raise RecordNotFound(record.get_name())
512
513         existing_record = existing_record_list[0]
514         pointer = existing_record.get_pointer()
515
516         # update the PLC information that was specified with the record
517
518         if (type == "sa") or (type == "ma"):
519             self.shell.UpdateSite(self.pl_auth, pointer, record.get_pl_info())
520
521         elif type == "slice":
522             self.shell.UpdateSlice(self.pl_auth, pointer, record.get_pl_info())
523
524         elif type == "user":
525             # SMBAKER: UpdatePerson only allows a limited set of fields to be
526             #    updated. Ideally we should have a more generic way of doing
527             #    this. I copied the field names from UpdatePerson.py...
528             update_fields = {}
529             all_fields = record.get_pl_info()
530             for key in all_fields.keys():
531                 if key in ['first_name', 'last_name', 'title', 'email',
532                            'password', 'phone', 'url', 'bio', 'accepted_aup',\r
533                            'enabled']:
534                     update_fields[key] = all_fields[key]
535             self.shell.UpdatePerson(self.pl_auth, pointer, update_fields)
536
537         elif type == "node":
538             self.shell.UpdateNode(self.pl_auth, pointer, record.get_pl_info())
539
540         else:
541             raise UnknownGeniType(type)
542
543     ##
544     # List the records in an authority. The objectGID in the supplied credential
545     # should name the authority that will be listed.
546     #
547     # TODO: List doesn't take an hrn and uses the hrn contained in the
548     #    objectGid of the credential. Does this mean the only way to list an
549     #    authority is by having a credential for that authority?
550     #
551     # @param cred credential string specifying rights of the caller
552     #
553     # @return list of record dictionaries
554     def list(self, cred):
555         self.decode_authentication(cred, "list")
556
557         auth_name = self.object_gid.get_hrn()
558         table = self.get_auth_table(auth_name)
559
560         records = table.list()
561
562         good_records = []
563         for record in records:
564             try:
565                 self.fill_record_info(record)
566                 good_records.append(record)
567             except PlanetLabRecordDoesNotExist:
568                 # silently drop the ones that are missing in PL.
569                 # is this the right thing to do?
570                 report.error("ignoring geni record " + record.get_name() + " because pl record does not exist")
571                 table.remove(record)
572
573         dicts = []
574         for record in good_records:
575             dicts.append(record.as_dict())
576
577         return dicts
578
579         return dict_list
580
581     ##
582     # Resolve a record. This is an internal version of the Resolve API call
583     # and returns records in record object format rather than dictionaries
584     # that may be sent over XMLRPC.
585     #
586     # @param type type of record to resolve (user | sa | ma | slice | node)
587     # @param name human readable name of object
588     # @param must_exist if True, throw an exception if no records are found
589     #
590     # @return a list of record objects, or an empty list []
591
592     def resolve_raw(self, type, name, must_exist=True):
593         auth_name = get_authority(name)
594
595         table = self.get_auth_table(auth_name)
596
597         records = table.resolve(type, name)
598
599         if (not records) and must_exist:
600             raise RecordNotFound(name)
601
602         good_records = []
603         for record in records:
604             try:
605                 self.fill_record_info(record)
606                 good_records.append(record)
607             except PlanetLabRecordDoesNotExist:
608                 # silently drop the ones that are missing in PL.
609                 # is this the right thing to do?
610                 report.error("ignoring geni record " + record.get_name() + " because pl record does not exist")
611                 table.remove(record)
612
613         return good_records
614
615     ##
616     # GENI API: Resolve
617     #
618     # This is a wrapper around resolve_raw that converts records objects into
619     # dictionaries before returning them to the user.
620     #
621     # @param cred credential string authorizing the caller
622     # @param name human readable name to resolve
623     #
624     # @return a list of record dictionaries, or an empty list
625
626     def resolve(self, cred, name):
627         self.decode_authentication(cred, "resolve")
628
629         records = self.resolve_raw("*", name)
630         dicts = []
631         for record in records:
632             dicts.append(record.as_dict())
633
634         return dicts
635
636     ##
637     # GENI API: get_gid
638     #
639     # Retrieve the GID for an object. This function looks up a record in the
640     # registry and returns the GID of the record if it exists.
641     # TODO: Is this function needed? It's a shortcut for Resolve()
642     #
643     # @param name hrn to look up
644     #
645     # @return the string representation of a GID object
646
647     def get_gid(self, name):
648         self.verify_object_belongs_to_me(name)
649         records = self.resolve_raw("*", name)
650         gid_string_list = []
651         for record in records:
652             gid = record.get_gid()
653             gid_string_list.append(gid.save_to_string(save_parents=True))
654         return gid_string_list
655
656     ##
657     # Determine tje rights that an object should have. The rights are entirely
658     # dependent on the type of the object. For example, users automatically
659     # get "refresh", "resolve", and "info".
660     #
661     # @param type the type of the object (user | sa | ma | slice | node)
662     # @param name human readable name of the object (not used at this time)
663     #
664     # @return RightList object containing rights
665
666     def determine_rights(self, type, name):
667         rl = RightList()
668
669         # rights seem to be somewhat redundant with the type of the credential.
670         # For example, a "sa" credential implies the authority right, because
671         # a sa credential cannot be issued to a user who is not an owner of
672         # the authority
673
674         if type == "user":
675             rl.add("refresh")
676             rl.add("resolve")
677             rl.add("info")
678         elif type == "sa":
679             rl.add("authority")
680         elif type == "ma":
681             rl.add("authority")
682         elif type == "slice":
683             rl.add("embed")
684             rl.add("bind")
685             rl.add("control")
686             rl.add("info")
687         elif type == "component":
688             rl.add("operator")
689
690         return rl
691
692     ##
693     # GENI API: Get_self_credential
694     #
695     # Get_self_credential a degenerate version of get_credential used by a
696     # client to get his initial credential when he doesn't have one. This is
697     # the same as get_credential(..., cred=None,...).
698     #
699     # The registry ensures that the client is the principal that is named by
700     # (type, name) by comparing the public key in the record's GID to the
701     # private key used to encrypt the client-side of the HTTPS connection. Thus
702     # it is impossible for one principal to retrieve another principal's
703     # credential without having the appropriate private key.
704     #
705     # @param type type of object (user | slice | sa | ma | node
706     # @param name human readable name of object
707     #
708     # @return the string representation of a credential object
709
710     def get_self_credential(self, type, name):
711         self.verify_object_belongs_to_me(name)
712
713         auth_hrn = get_authority(name)
714         auth_info = self.get_auth_info(auth_hrn)
715
716         # find a record that matches
717         records = self.resolve_raw(type, name, must_exist=True)
718         record = records[0]
719
720         gid = record.get_gid_object()
721         peer_cert = self.server.peer_cert
722         if not peer_cert.is_pubkey(gid.get_pubkey()):
723            raise ConnectionKeyGIDMismatch(gid.get_subject())
724
725         # create the credential
726         gid = record.get_gid_object()
727         cred = Credential(subject = gid.get_subject())
728         cred.set_gid_caller(gid)
729         cred.set_gid_object(gid)
730         cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
731         cred.set_pubkey(gid.get_pubkey())
732
733         rl = self.determine_rights(type, name)
734         cred.set_privileges(rl)
735
736         cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn))
737
738         cred.encode()
739         cred.sign()
740
741         return cred.save_to_string(save_parents=True)
742
743     ##
744     # GENI API: Get_credential
745     #
746     # Retrieve a credential for an object.
747     #
748     # If cred==None, then the behavior reverts to get_self_credential()
749     #
750     # @param cred credential object specifying rights of the caller
751     # @param type type of object (user | slice | sa | ma | node)
752     # @param name human readable name of object
753     #
754     # @return the string representation of a credental object
755
756     def get_credential(self, cred, type, name):
757         if not cred:
758             return get_self_credential(self, type, name)
759
760         self.decode_authentication(cred, "getcredential")
761
762         self.verify_object_belongs_to_me(name)
763
764         auth_hrn = get_authority(name)
765         auth_info = self.get_auth_info(auth_hrn)
766
767         records = self.resolve_raw(type, name, must_exist=True)
768         record = records[0]
769
770         # TODO: Check permission that self.client_cred can access the object
771
772         object_gid = record.get_gid_object()
773         new_cred = Credential(subject = object_gid.get_subject())
774         new_cred.set_gid_caller(self.client_gid)
775         new_cred.set_gid_object(object_gid)
776         new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
777         new_cred.set_pubkey(object_gid.get_pubkey())
778
779         rl = self.determine_rights(type, name)
780         new_cred.set_privileges(rl)
781
782         new_cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn))
783
784         new_cred.encode()
785         new_cred.sign()
786
787         return new_cred.save_to_string(save_parents=True)
788
789     ##
790     # GENI_API: Create_gid
791     #
792     # Create a new GID. For MAs and SAs that are physically located on the
793     # registry, this allows a owner/operator/PI to create a new GID and have it
794     # signed by his respective authority.
795     #
796     # @param cred credential of caller
797     # @param name hrn for new GID
798     # @param uuid unique identifier for new GID
799     # @param pkey_string public-key string (TODO: why is this a string and not a keypair object?)
800     #
801     # @return the string representation of a GID object
802
803     def create_gid(self, cred, name, uuid, pubkey_str):
804         self.decode_authentication(cred, "getcredential")
805
806         self.verify_object_belongs_to_me(name)
807
808         self.verify_object_permission(name)
809
810         if uuid == None:
811             uuid = create_uuid()
812
813         pkey = Keypair()
814         pkey.load_pubkey_from_string(pubkey_str)
815         gid = self.hierarchy.create_gid(name, uuid, pkey)
816
817         return gid.save_to_string(save_parents=True)
818