2 # Registry is a GeniServer that implements the Registry interface
9 from geni.util.credential import Credential
10 from geni.util.hierarchy import Hierarchy
11 from geni.util.trustedroot import TrustedRootList
12 from geni.util.cert import Keypair, Certificate
13 from geni.util.gid import GID, create_uuid
14 from geni.util.geniserver import GeniServer
15 from geni.util.geniclient import GeniClient
16 from geni.util.record import GeniRecord
17 from geni.util.rights import RightList
18 from geni.util.genitable import GeniTable
19 from geni.util.geniticket import Ticket
20 from geni.util.excep import *
21 from geni.util.misc import *
22 from geni.util.config import *
23 from geni.util.storage import *
26 # Convert geni fields to PLC fields for use when registering up updating
27 # registry record in the PLC database
29 # @param type type of record (user, slice, ...)
30 # @param hrn human readable name
31 # @param geni_fields dictionary of geni fields
32 # @param pl_fields dictionary of PLC fields (output)
34 def geni_fields_to_pl_fields(type, hrn, geni_fields, pl_fields):
36 if not "email" in pl_fields:
37 if not "email" in geni_fields:
38 raise MissingGeniInfo("email")
39 pl_fields["email"] = geni_fields["email"]
41 if not "first_name" in pl_fields:
42 pl_fields["first_name"] = "geni"
44 if not "last_name" in pl_fields:
45 pl_fields["last_name"] = hrn
48 if not "instantiation" in pl_fields:
49 pl_fields["instantiation"] = "delegated" # "plc-instantiated"
50 if not "name" in pl_fields:
51 pl_fields["name"] = hrn_to_pl_slicename(hrn)
52 if not "max_nodes" in pl_fields:
53 pl_fields["max_nodes"] = 10
56 if not "hostname" in pl_fields:
57 if not "dns" in geni_fields:
58 raise MissingGeniInfo("dns")
59 pl_fields["hostname"] = geni_fields["dns"]
61 if not "model" in pl_fields:
62 pl_fields["model"] = "geni"
65 pl_fields["login_base"] = hrn_to_pl_login_base(hrn)
67 if not "name" in pl_fields:
68 pl_fields["name"] = hrn
70 if not "abbreviated_name" in pl_fields:
71 pl_fields["abbreviated_name"] = hrn
73 if not "enabled" in pl_fields:
74 pl_fields["enabled"] = True
76 if not "is_public" in pl_fields:
77 pl_fields["is_public"] = True
80 # Registry is a GeniServer that serves registry and slice operations at PLC.
82 class Registry(GeniServer):
84 # Create a new registry object.
86 # @param ip the ip address to listen on
87 # @param port the port to listen on
88 # @param key_file private key filename of registry
89 # @param cert_file certificate filename containing public key (could be a GID file)
91 def __init__(self, ip, port, key_file, cert_file, config = '/usr/share/geniwrapper/geni/util/geni_config'):
92 GeniServer.__init__(self, ip, port, key_file, cert_file)
94 # get PL account settings from config module
95 self.pl_auth = get_pl_auth()
97 # connect to planetlab
98 if "Url" in self.pl_auth:
99 self.connect_remote_shell()
101 self.connect_local_shell()
103 self.key_file = key_file
104 self.cert_file = cert_file
105 self.config = Config(config)
106 self.basedir = self.config.GENI_BASE_DIR + os.sep
107 self.server_basedir = self.basedir + os.sep + "geni" + os.sep
108 self.hrn = self.config.GENI_INTERFACE_HRN
110 # get peer registry information
111 registries_file = self.server_basedir + os.sep + 'registries.xml'
112 connection_dict = {'hrn': '', 'addr': '', 'port': ''}
113 self.registry_info = XmlStorage(registries_file, {'registries': {'registry': [connection_dict]}})
114 self.registry_info.load()
115 self.connectRegistry()
116 #self.loadCredential()
117 self.connectRegistries()
121 # Connect to a remote shell via XMLRPC
123 def connect_remote_shell(self):
124 from geni.util import remoteshell
125 self.shell = remoteshell.RemoteShell()
128 # Connect to a local shell via local API functions
130 def connect_local_shell(self):
132 self.shell = PLC.Shell.Shell(globals = globals())
135 # Register the server RPCs for the registry
137 def register_functions(self):
138 GeniServer.register_functions(self)
140 self.server.register_function(self.create_gid)
141 self.server.register_function(self.get_self_credential)
142 self.server.register_function(self.get_credential)
143 self.server.register_function(self.get_gid)
144 self.server.register_function(self.get_ticket)
145 self.server.register_function(self.register)
146 self.server.register_function(self.remove)
147 self.server.register_function(self.update)
148 self.server.register_function(self.list)
149 self.server.register_function(self.resolve)
152 def loadCredential(self):
154 Attempt to load credential from file if it exists. If it doesnt get
155 credential from registry.
158 # see if this file exists
159 ma_cred_filename = self.server_basedir + os.sep + "reg." + self.hrn + ".sa.cred"
161 self.credential = Credential(filename = ma_cred_filename)
163 self.credential = self.getCredentialFromRegistry()
165 def getCredentialFromRegistry(self):
167 Get our current credential from the registry.
169 # get self credential
170 self_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".cred"
171 self_cred = self.registry.get_credential(None, 'ma', self.hrn)
172 self_cred.save_to_file(self_cred_filename, save_parents=True)
175 ma_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".sa.cred"
176 ma_cred = self.registry.get_credential(self_cred, 'sa', self.hrn)
177 ma_cred.save_to_file(ma_cred_filename, save_parents=True)
180 def connectRegistry(self):
182 Connect to the registry
184 # connect to registry using GeniClient
185 address = self.config.GENI_REGISTRY_HOSTNAME
186 port = self.config.GENI_REGISTRY_PORT
187 url = 'http://%(address)s:%(port)s' % locals()
188 self.registry = GeniClient(url, self.key_file, self.cert_file)
190 def connectRegistries(self):
192 Get connection details for the trusted peer registries from file and
193 create an GeniClient connection to each.
196 registries = self.registry_info['registries']['registry']
197 if isinstance(registries, dict):
198 registries = [registries]
199 if isinstance(registries, list):
200 for registry in registries:
201 # create xmlrpc connection using GeniClient
202 hrn, address, port = registry['hrn'], registry['addr'], registry['port']
203 url = 'http://%(address)s:%(port)s' % locals()
204 self.registries[hrn] = GeniClient(url, self.key_file, self.cert_file)
207 # Given an authority name, return the information for that authority. This
208 # is basically a stub that calls the hierarchy module.
210 # @param auth_hrn human readable name of authority
212 def get_auth_info(self, auth_hrn):
213 return self.hierarchy.get_auth_info(auth_hrn)
216 # Given an authority name, return the database table for that authority. If
217 # the database table does not exist, then one will be automatically
220 # @param auth_name human readable name of authority
222 def get_auth_table(self, auth_name):
223 auth_info = self.get_auth_info(auth_name)
225 table = GeniTable(hrn=auth_name,
226 cninfo=auth_info.get_dbinfo())
228 # if the table doesn't exist, then it means we haven't put any records
229 # into this authority yet.
231 if not table.exists():
232 print "Registry: creating table for authority", auth_name
238 # Verify that an authority belongs to this registry. This is basically left
239 # up to the implementation of the hierarchy module. If the specified name
240 # does not belong to this registry, an exception is thrown indicating the
241 # caller should contact someone else.
243 # @param auth_name human readable name of authority
245 def verify_auth_belongs_to_me(self, name):
246 # get_auth_info will throw an exception if the authority does not
248 self.get_auth_info(name)
251 # Verify that an object belongs to this registry. By extension, this implies
252 # that the authority that owns the object belongs to this registry. If the
253 # object does not belong to this registry, then an exception is thrown.
255 # @param name human readable name of object
257 def verify_object_belongs_to_me(self, name):
258 auth_name = get_authority(name)
260 # the root authority belongs to the registry by default?
261 # TODO: is this true?
263 self.verify_auth_belongs_to_me(auth_name)
266 # Verify that the object_gid that was specified in the credential allows
267 # permission to the object 'name'. This is done by a simple prefix test.
268 # For example, an object_gid for planetlab.us.arizona would match the
269 # objects planetlab.us.arizona.slice1 and planetlab.us.arizona.
271 # @param name human readable name to test
273 def verify_object_permission(self, name):
274 object_hrn = self.object_gid.get_hrn()
275 if object_hrn == name:
277 if name.startswith(object_hrn + "."):
279 raise PermissionError(name)
282 # Fill in the planetlab-specific fields of a Geni record. This involves
283 # calling the appropriate PLC methods to retrieve the database record for
286 # PLC data is filled into the pl_info field of the record.
288 # @param record record to fill in fields (in/out param)
290 def fill_record_pl_info(self, record):
291 type = record.get_type()
292 pointer = record.get_pointer()
294 # records with pointer==-1 do not have plc info associated with them.
295 # for example, the top level authority records which are
296 # authorities, but not PL "sites"
298 record.set_pl_info({})
301 if (type == "sa") or (type == "ma"):
302 pl_res = self.shell.GetSites(self.pl_auth, [pointer])
303 elif (type == "slice"):
304 pl_res = self.shell.GetSlices(self.pl_auth, [pointer])
305 elif (type == "user"):
306 pl_res = self.shell.GetPersons(self.pl_auth, [pointer])
307 elif (type == "node"):
308 pl_res = self.shell.GetNodes(self.pl_auth, [pointer])
310 raise UnknownGeniType(type)
313 # the planetlab record no longer exists
314 # TODO: delete the geni record ?
315 raise PlanetLabRecordDoesNotExist(record.get_name())
317 record.set_pl_info(pl_res[0])
320 # Look up user records given PLC user-ids. This is used as part of the
321 # process for reverse-mapping PLC records into Geni records.
323 # @param auth_table database table for the authority that holds the user records
324 # @param user_id_list list of user ids
325 # @param role either "*" or a string describing the role to look for ("pi", "user", ...)
327 # TODO: This function currently only searches one authority because it would
328 # be inefficient to brute-force search all authorities for a user id. The
329 # solution would likely be to implement a reverse mapping of user-id to
332 def lookup_users(self, auth_table, user_id_list, role="*"):
334 for person_id in user_id_list:
335 user_records = auth_table.find("user", person_id, "pointer")
336 for user_record in user_records:
337 self.fill_record_info(user_record)
339 user_roles = user_record.get_pl_info().get("roles")
340 if (role=="*") or (role in user_roles):
341 record_list.append(user_record.get_name())
345 # Fill in the geni-specific fields of the record.
347 # Note: It is assumed the fill_record_pl_info() has already been performed
350 def fill_record_geni_info(self, record):
352 type = record.get_type()
354 if (type == "slice"):
355 auth_table = self.get_auth_table(get_authority(record.get_name()))
356 person_ids = record.pl_info.get("person_ids", [])
357 researchers = self.lookup_users(auth_table, person_ids)
358 geni_info['researcher'] = researchers
361 auth_table = self.get_auth_table(record.get_name())
362 person_ids = record.pl_info.get("person_ids", [])
363 pis = self.lookup_users(auth_table, person_ids, "pi")
364 geni_info['pi'] = pis
365 # TODO: OrganizationName
368 auth_table = self.get_auth_table(record.get_name())
369 person_ids = record.pl_info.get("person_ids", [])
370 operators = self.lookup_users(auth_table, person_ids, "tech")
371 geni_info['operator'] = operators
372 # TODO: OrganizationName
374 auth_table = self.get_auth_table(record.get_name())
375 person_ids = record.pl_info.get("person_ids", [])
376 owners = self.lookup_users(auth_table, person_ids, "admin")
377 geni_info['owner'] = owners
379 elif (type == "node"):
380 geni_info['dns'] = record.pl_info.get("hostname", "")
381 # TODO: URI, LatLong, IP, DNS
383 elif (type == "user"):
384 geni_info['email'] = record.pl_info.get("email", "")
385 # TODO: PostalAddress, Phone
387 record.set_geni_info(geni_info)
390 # Given a Geni record, fill in the PLC-specific and Geni-specific fields
393 def fill_record_info(self, record):
394 self.fill_record_pl_info(record)
395 self.fill_record_geni_info(record)
397 def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
398 # get a list of the HRNs tht are members of the old and new records
\r
400 if oldRecord.pl_info == None:
\r
401 oldRecord.pl_info = {}
\r
402 oldList = oldRecord.get_geni_info().get(listName, [])
\r
405 newList = record.get_geni_info().get(listName, [])
\r
407 # if the lists are the same, then we don't have to update anything
\r
408 if (oldList == newList):
\r
411 # build a list of the new person ids, by looking up each person to get
\r
414 for hrn in newList:
\r
415 userRecord = self.resolve_raw("user", hrn)[0]
\r
416 newIdList.append(userRecord.get_pointer())
\r
418 # build a list of the old person ids from the person_ids field of the
\r
421 oldIdList = oldRecord.pl_info.get("person_ids", [])
\r
422 containerId = oldRecord.get_pointer()
\r
424 # if oldRecord==None, then we are doing a Register, instead of an
\r
427 containerId = record.get_pointer()
\r
429 # add people who are in the new list, but not the oldList
\r
430 for personId in newIdList:
\r
431 if not (personId in oldIdList):
\r
432 print "adding id", personId, "to", record.get_name()
\r
433 addFunc(self.pl_auth, personId, containerId)
\r
435 # remove people who are in the old list, but not the new list
\r
436 for personId in oldIdList:
\r
437 if not (personId in newIdList):
\r
438 print "removing id", personId, "from", record.get_name()
\r
439 delFunc(self.pl_auth, personId, containerId)
\r
441 def update_membership(self, oldRecord, record):
\r
442 if record.type == "slice":
\r
443 self.update_membership_list(oldRecord, record, 'researcher',
\r
444 self.shell.AddPersonToSlice,
\r
445 self.shell.DeletePersonFromSlice)
\r
446 elif record.type == "sa":
\r
449 elif record.type == "ma":
\r
456 # Register an object with the registry. In addition to being stored in the
457 # Geni database, the appropriate records will also be created in the
460 # @param cred credential string
461 # @param record_dict dictionary containing record fields
463 def register(self, cred, record_dict):
464 self.decode_authentication(cred, "register")
466 record = GeniRecord(dict = record_dict)
467 type = record.get_type()
468 name = record.get_name()
470 auth_name = get_authority(name)
471 self.verify_object_permission(auth_name)
472 auth_info = self.get_auth_info(auth_name)
473 table = self.get_auth_table(auth_name)
477 # check if record already exists
478 existing_records = table.resolve(type, name)
480 raise ExistingRecord(name)
482 if (type == "sa") or (type=="ma"):
484 if not self.hierarchy.auth_exists(name):
485 self.hierarchy.create_auth(name)
487 # authorities are special since they are managed by the registry
488 # rather than by the caller. We create our own GID for the
489 # authority rather than relying on the caller to supply one.
491 # get the GID from the newly created authority
492 child_auth_info = self.get_auth_info(name)
493 gid = auth_info.get_gid_object()
494 record.set_gid(gid.save_to_string(save_parents=True))
496 geni_fields = record.get_geni_info()
497 site_fields = record.get_pl_info()
499 # if registering a sa, see if a ma already exists
500 # if registering a ma, see if a sa already exists
502 other_rec = table.resolve("ma", record.get_name())
504 other_rec = table.resolve("sa", record.get_name())
507 print "linking ma and sa to the same plc site"
508 pointer = other_rec[0].get_pointer()
510 geni_fields_to_pl_fields(type, name, geni_fields, site_fields)
511 print "adding site with fields", site_fields
512 pointer = self.shell.AddSite(self.pl_auth, site_fields)
514 record.set_pointer(pointer)
516 elif (type == "slice"):
517 geni_fields = record.get_geni_info()
518 slice_fields = record.get_pl_info()
520 geni_fields_to_pl_fields(type, name, geni_fields, slice_fields)
522 pointer = self.shell.AddSlice(self.pl_auth, slice_fields)
523 record.set_pointer(pointer)
525 elif (type == "user"):
526 geni_fields = record.get_geni_info()
527 user_fields = record.get_pl_info()
529 geni_fields_to_pl_fields(type, name, geni_fields, user_fields)
531 pointer = self.shell.AddPerson(self.pl_auth, user_fields)
532 record.set_pointer(pointer)
534 elif (type == "node"):
535 geni_fields = record.get_geni_info()
536 node_fields = record.get_pl_info()
538 geni_fields_to_pl_fields(type, name, geni_fields, node_fields)
540 login_base = hrn_to_pl_login_base(auth_name)
542 print "calling addnode with", login_base, node_fields
543 pointer = self.shell.AddNode(self.pl_auth, login_base, node_fields)
544 record.set_pointer(pointer)
547 raise UnknownGeniType(type)
551 # update membership for researchers, pis, owners, operators
552 self.update_membership(None, record)
554 return record.get_gid_object().save_to_string(save_parents=True)
559 # Remove an object from the registry. If the object represents a PLC object,
560 # then the PLC records will also be removed.
562 # @param cred credential string
563 # @param record_dict dictionary containing record fields. The only relevant
564 # fields of the record are 'name' and 'type', which are used to lookup
565 # the current copy of the record in the Geni database, to make sure
566 # that the appopriate record is removed.
568 def remove(self, cred, type, hrn):
569 self.decode_authentication(cred, "remove")
571 self.verify_object_permission(hrn)
573 auth_name = get_authority(hrn)
574 table = self.get_auth_table(auth_name)
576 record_list = table.resolve(type, hrn)
578 raise RecordNotFound(hrn)
579 record = record_list[0]
583 self.shell.DeletePerson(self.pl_auth, record.get_pointer())
584 elif type == "slice":
585 self.shell.DeleteSlice(self.pl_auth, record.get_pointer())
587 self.shell.DeleteNode(self.pl_auth, record.get_pointer())
588 elif (type == "sa") or (type == "ma"):
590 other_rec = table.resolve("ma", record.get_name())
592 other_rec = table.resolve("sa", record.get_name())
595 # sa and ma both map to a site, so if we are deleting one
596 # but the other still exists, then do not delete the site
597 print "not removing site", record.get_name(), "because either sa or ma still exists"
600 print "removing site", record.get_name()
601 self.shell.DeleteSite(self.pl_auth, record.get_pointer())
603 raise UnknownGeniType(type)
612 # Update an object in the registry. Currently, this only updates the
613 # PLC information associated with the record. The Geni fields (name, type,
616 # The record is expected to have the pl_info field filled in with the data
617 # that should be updated.
619 # TODO: The geni_info member of the record should be parsed and the pl_info
620 # adjusted as necessary (add/remove users from a slice, etc)
622 # @param cred credential string specifying rights of the caller
623 # @param record a record dictionary to be updated
625 def update(self, cred, record_dict):
626 self.decode_authentication(cred, "update")
628 record = GeniRecord(dict = record_dict)
629 type = record.get_type()
631 self.verify_object_permission(record.get_name())
633 auth_name = get_authority(record.get_name())
635 auth_name = record.get_name()
636 table = self.get_auth_table(auth_name)
638 # make sure the record exists
639 existing_record_list = table.resolve(type, record.get_name())
640 if not existing_record_list:
641 raise RecordNotFound(record.get_name())
642 existing_record = existing_record_list[0]
644 # Update_membership needs the membership lists in the existing record
645 # filled in, so it can see if members were added or removed
646 self.fill_record_info(existing_record)
648 # Use the pointer from the existing record, not the one that the user
649 # gave us. This prevents the user from inserting a forged pointer
650 pointer = existing_record.get_pointer()
652 # update the PLC information that was specified with the record
654 if (type == "sa") or (type == "ma"):
655 self.shell.UpdateSite(self.pl_auth, pointer, record.get_pl_info())
657 elif type == "slice":
658 self.shell.UpdateSlice(self.pl_auth, pointer, record.get_pl_info())
661 # SMBAKER: UpdatePerson only allows a limited set of fields to be
662 # updated. Ideally we should have a more generic way of doing
663 # this. I copied the field names from UpdatePerson.py...
665 all_fields = record.get_pl_info()
666 for key in all_fields.keys():
667 if key in ['first_name', 'last_name', 'title', 'email',
668 'password', 'phone', 'url', 'bio', 'accepted_aup',
\r
670 update_fields[key] = all_fields[key]
671 self.shell.UpdatePerson(self.pl_auth, pointer, update_fields)
674 self.shell.UpdateNode(self.pl_auth, pointer, record.get_pl_info())
677 raise UnknownGeniType(type)
679 # update membership for researchers, pis, owners, operators
\r
680 self.update_membership(existing_record, record)
683 # List the records in an authority. The objectGID in the supplied credential
684 # should name the authority that will be listed.
686 # TODO: List doesn't take an hrn and uses the hrn contained in the
687 # objectGid of the credential. Does this mean the only way to list an
688 # authority is by having a credential for that authority?
690 # @param cred credential string specifying rights of the caller
692 # @return list of record dictionaries
693 def list(self, cred, auth_hrn):
694 self.decode_authentication(cred, "list")
696 if not self.hierarchy.auth_exists(auth_hrn):
697 raise MissingAuthority(auth_hrn)
699 table = self.get_auth_table(auth_hrn)
701 records = table.list()
704 for record in records:
706 self.fill_record_info(record)
707 good_records.append(record)
708 except PlanetLabRecordDoesNotExist:
709 # silently drop the ones that are missing in PL.
710 # is this the right thing to do?
711 print "ignoring geni record ", record.get_name(), " because pl record does not exist"
715 for record in good_records:
716 dicts.append(record.as_dict())
723 # Resolve a record. This is an internal version of the Resolve API call
724 # and returns records in record object format rather than dictionaries
725 # that may be sent over XMLRPC.
727 # @param type type of record to resolve (user | sa | ma | slice | node)
728 # @param name human readable name of object
729 # @param must_exist if True, throw an exception if no records are found
731 # @return a list of record objects, or an empty list []
733 def resolve_raw(self, type, name, must_exist=True):
734 auth_name = get_authority(name)
737 table = self.get_auth_table(auth_name)
738 records = table.resolve(type, name)
739 if (not records) and must_exist:
740 raise RecordNotFound(name)
743 for record in records:
745 self.fill_record_info(record)
746 good_records.append(record)
747 except PlanetLabRecordDoesNotExist:
748 # silently drop the ones that are missing in PL.
749 # is this the right thing to do?
750 print "ignoring geni record ", record.get_name(), "because pl record does not exist"
758 # This is a wrapper around resolve_raw that converts records objects into
759 # dictionaries before returning them to the user.
761 # @param cred credential string authorizing the caller
762 # @param name human readable name to resolve
764 # @return a list of record dictionaries, or an empty list
766 def resolve(self, cred, name):
767 self.decode_authentication(cred, "resolve")
770 records = self.resolve_raw("*", name)
771 except RecordNotFound:
773 for registry in self.registries:
774 if name.startswith(registry):
775 records = self.registries[registry].resolve(cred, name)
778 for record in records:
779 dicts.append(record.as_dict())
786 # Retrieve the GID for an object. This function looks up a record in the
787 # registry and returns the GID of the record if it exists.
788 # TODO: Is this function needed? It's a shortcut for Resolve()
790 # @param name hrn to look up
792 # @return the string representation of a GID object
794 def get_gid(self, name):
795 self.verify_object_belongs_to_me(name)
796 records = self.resolve_raw("*", name)
798 for record in records:
799 gid = record.get_gid_object()
800 gid_string_list.append(gid.save_to_string(save_parents=True))
801 return gid_string_list
804 # Determine tje rights that an object should have. The rights are entirely
805 # dependent on the type of the object. For example, users automatically
806 # get "refresh", "resolve", and "info".
808 # @param type the type of the object (user | sa | ma | slice | node)
809 # @param name human readable name of the object (not used at this time)
811 # @return RightList object containing rights
813 def determine_rights(self, type, name):
816 # rights seem to be somewhat redundant with the type of the credential.
817 # For example, a "sa" credential implies the authority right, because
818 # a sa credential cannot be issued to a user who is not an owner of
826 rl.add("authority,sa")
828 rl.add("authority,ma")
829 elif type == "slice":
835 elif type == "component":
841 # GENI API: Get_self_credential
843 # Get_self_credential a degenerate version of get_credential used by a
844 # client to get his initial credential when he doesn't have one. This is
845 # the same as get_credential(..., cred=None,...).
847 # The registry ensures that the client is the principal that is named by
848 # (type, name) by comparing the public key in the record's GID to the
849 # private key used to encrypt the client-side of the HTTPS connection. Thus
850 # it is impossible for one principal to retrieve another principal's
851 # credential without having the appropriate private key.
853 # @param type type of object (user | slice | sa | ma | node
854 # @param name human readable name of object
856 # @return the string representation of a credential object
858 def get_self_credential(self, type, name):
859 self.verify_object_belongs_to_me(name)
861 auth_hrn = get_authority(name)
864 auth_info = self.get_auth_info(auth_hrn)
865 # find a record that matches
866 records = self.resolve_raw(type, name, must_exist=True)
869 gid = record.get_gid_object()
870 peer_cert = self.server.peer_cert
871 if not peer_cert.is_pubkey(gid.get_pubkey()):
872 raise ConnectionKeyGIDMismatch(gid.get_subject())
874 # create the credential
875 gid = record.get_gid_object()
876 cred = Credential(subject = gid.get_subject())
877 cred.set_gid_caller(gid)
878 cred.set_gid_object(gid)
879 cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
880 cred.set_pubkey(gid.get_pubkey())
882 rl = self.determine_rights(type, name)
883 cred.set_privileges(rl)
885 # determine the type of credential that we want to use as a parent for
888 if (type == "ma") or (type == "node"):
889 auth_kind = "authority,ma"
890 else: # user, slice, sa
891 auth_kind = "authority,sa"
893 cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
898 return cred.save_to_string(save_parents=True)
901 # verify_cancreate_credential
903 # Verify that a user can retrieve a particular type of credential. For
904 # slices, the user must be on the researcher list. For SA and MA the user
905 # must be on the pi and operator lists respectively.
907 def verify_cancreate_credential(self, src_cred, record):
908 type = record.get_type()
909 cred_object_hrn = src_cred.get_gid_object().get_hrn()
911 if cred_object_hrn in [config.GENI_REGISTRY_ROOT_AUTH]:
914 researchers = record.get_geni_info().get("researcher", [])
915 if not (cred_object_hrn in researchers):
916 raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name())
918 pis = record.get_geni_info().get("pi", [])
919 if not (cred_object_hrn in pis):
920 raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name())
922 operators = record.get_geni_info().get("operator", [])
923 if not (cred_object_hrn in operators):
924 raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name())
927 # GENI API: Get_credential
929 # Retrieve a credential for an object.
931 # If cred==None, then the behavior reverts to get_self_credential()
933 # @param cred credential object specifying rights of the caller
934 # @param type type of object (user | slice | sa | ma | node)
935 # @param name human readable name of object
937 # @return the string representation of a credental object
939 def get_credential(self, cred, type, name):
941 return get_self_credential(self, type, name)
943 self.decode_authentication(cred, "getcredential")
945 self.verify_object_belongs_to_me(name)
947 auth_hrn = get_authority(name)
950 auth_info = self.get_auth_info(auth_hrn)
952 records = self.resolve_raw(type, name, must_exist=True)
955 # verify_cancreate_credential requires that the member lists
956 # (researchers, pis, etc) be filled in
957 self.fill_record_info(record)
959 self.verify_cancreate_credential(self.client_cred, record)
961 # TODO: Check permission that self.client_cred can access the object
963 object_gid = record.get_gid_object()
964 new_cred = Credential(subject = object_gid.get_subject())
965 new_cred.set_gid_caller(self.client_gid)
966 new_cred.set_gid_object(object_gid)
967 new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
968 new_cred.set_pubkey(object_gid.get_pubkey())
970 rl = self.determine_rights(type, name)
971 new_cred.set_privileges(rl)
973 # determine the type of credential that we want to use as a parent for
976 if (type == "ma") or (type == "node"):
977 auth_kind = "authority,ma"
978 else: # user, slice, sa
979 auth_kind = "authority,sa"
981 new_cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
986 return new_cred.save_to_string(save_parents=True)
989 # GENI API: get_ticket
991 # Retrieve a ticket. This operation is currently implemented on PLC
992 # only (see SFA, engineering decisions); it is not implemented on
995 # The ticket is filled in with information from the PLC database. This
996 # information includes resources, and attributes such as user keys and
999 # @param cred credential string
1000 # @param name name of the slice to retrieve a ticket for
1001 # @param rspec resource specification dictionary
1003 # @return the string representation of a ticket object
1005 def get_ticket(self, cred, name, rspec):
1006 self.decode_authentication(cred, "getticket")
1008 self.verify_object_belongs_to_me(name)
1010 self.verify_object_permission(name)
1012 # XXX much of this code looks like get_credential... are they so similar
1013 # that they should be combined?
1015 auth_hrn = get_authority(name)
1018 auth_info = self.get_auth_info(auth_hrn)
1020 records = self.resolve_raw("slice", name, must_exist=True)
1023 object_gid = record.get_gid_object()
1024 new_ticket = Ticket(subject = object_gid.get_subject())
1025 new_ticket.set_gid_caller(self.client_gid)
1026 new_ticket.set_gid_object(object_gid)
1027 new_ticket.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
1028 new_ticket.set_pubkey(object_gid.get_pubkey())
1030 self.fill_record_info(record)
1032 (attributes, rspec) = self.record_to_slice_info(record)
1034 new_ticket.set_attributes(attributes)
1035 new_ticket.set_rspec(rspec)
1037 new_ticket.set_parent(AuthHierarchy.get_auth_ticket(auth_hrn))
1042 return new_ticket.save_to_string(save_parents=True)
1045 # GENI_API: Create_gid
1047 # Create a new GID. For MAs and SAs that are physically located on the
1048 # registry, this allows a owner/operator/PI to create a new GID and have it
1049 # signed by his respective authority.
1051 # @param cred credential of caller
1052 # @param name hrn for new GID
1053 # @param uuid unique identifier for new GID
1054 # @param pkey_string public-key string (TODO: why is this a string and not a keypair object?)
1056 # @return the string representation of a GID object
1058 def create_gid(self, cred, name, uuid, pubkey_str):
1059 self.decode_authentication(cred, "getcredential")
1061 self.verify_object_belongs_to_me(name)
1063 self.verify_object_permission(name)
1066 uuid = create_uuid()
1069 pkey.load_pubkey_from_string(pubkey_str)
1070 gid = self.hierarchy.create_gid(name, uuid, pkey)
1072 return gid.save_to_string(save_parents=True)