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.connectRegistries()
120 # Connect to a remote shell via XMLRPC
122 def connect_remote_shell(self):
123 from geni.util import remoteshell
124 self.shell = remoteshell.RemoteShell()
127 # Connect to a local shell via local API functions
129 def connect_local_shell(self):
131 self.shell = PLC.Shell.Shell(globals = globals())
134 # Register the server RPCs for the registry
136 def register_functions(self):
137 GeniServer.register_functions(self)
139 self.server.register_function(self.create_gid)
140 self.server.register_function(self.get_self_credential)
141 self.server.register_function(self.get_credential)
142 self.server.register_function(self.get_gid)
143 self.server.register_function(self.get_ticket)
144 self.server.register_function(self.register)
145 self.server.register_function(self.remove)
146 self.server.register_function(self.update)
147 self.server.register_function(self.list)
148 self.server.register_function(self.resolve)
151 def loadCredential(self):
153 Attempt to load credential from file if it exists. If it doesnt get
154 credential from registry.
157 # see if this file exists
158 ma_cred_filename = self.server_basedir + os.sep + "agg." + self.hrn + ".ma.cred"
160 self.credential = Credential(filename = ma_cred_filename)
162 self.credential = self.getCredentialFromRegistry()
164 def getCredentialFromRegistry(self):
166 Get our current credential from the registry.
168 # get self credential
169 self_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".cred"
170 self_cred = self.registry.get_credential(None, 'ma', self.hrn)
171 self_cred.save_to_file(self_cred_filename, save_parents=True)
174 ma_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".sa.cred"
175 ma_cred = self.registry.get_credential(self_cred, 'sa', self.hrn)
176 ma_cred.save_to_file(ma_cred_filename, save_parents=True)
179 def connectRegistry(self):
181 Connect to the registry
183 # connect to registry using GeniClient
184 address = self.config.GENI_REGISTRY_HOSTNAME
185 port = self.config.GENI_REGISTRY_PORT
186 url = 'http://%(address)s:%(port)s' % locals()
187 self.registry = GeniClient(url, self.key_file, self.cert_file)
189 def connectRegistries(self):
191 Get connection details for the trusted peer registries from file and
192 create an GeniClient connection to each.
195 registries = self.registry_info['registries']['registry']
196 if isinstance(registries, dict):
197 registries = [registries]
198 if isinstance(registries, list):
199 for registry in registries:
200 # create xmlrpc connection using GeniClient
201 hrn, address, port = registry['hrn'], registry['addr'], registry['port']
202 url = 'http://%(address)s:%(port)s' % locals()
203 self.registries[hrn] = GeniClient(url, self.key_file, self.cert_file)
206 # Given an authority name, return the information for that authority. This
207 # is basically a stub that calls the hierarchy module.
209 # @param auth_hrn human readable name of authority
211 def get_auth_info(self, auth_hrn):
212 return self.hierarchy.get_auth_info(auth_hrn)
215 # Given an authority name, return the database table for that authority. If
216 # the database table does not exist, then one will be automatically
219 # @param auth_name human readable name of authority
221 def get_auth_table(self, auth_name):
222 auth_info = self.get_auth_info(auth_name)
224 table = GeniTable(hrn=auth_name,
225 cninfo=auth_info.get_dbinfo())
227 # if the table doesn't exist, then it means we haven't put any records
228 # into this authority yet.
230 if not table.exists():
231 print "Registry: creating table for authority", auth_name
237 # Verify that an authority belongs to this registry. This is basically left
238 # up to the implementation of the hierarchy module. If the specified name
239 # does not belong to this registry, an exception is thrown indicating the
240 # caller should contact someone else.
242 # @param auth_name human readable name of authority
244 def verify_auth_belongs_to_me(self, name):
245 # get_auth_info will throw an exception if the authority does not
247 self.get_auth_info(name)
250 # Verify that an object belongs to this registry. By extension, this implies
251 # that the authority that owns the object belongs to this registry. If the
252 # object does not belong to this registry, then an exception is thrown.
254 # @param name human readable name of object
256 def verify_object_belongs_to_me(self, name):
257 auth_name = get_authority(name)
259 # the root authority belongs to the registry by default?
260 # TODO: is this true?
262 self.verify_auth_belongs_to_me(auth_name)
265 # Verify that the object_gid that was specified in the credential allows
266 # permission to the object 'name'. This is done by a simple prefix test.
267 # For example, an object_gid for planetlab.us.arizona would match the
268 # objects planetlab.us.arizona.slice1 and planetlab.us.arizona.
270 # @param name human readable name to test
272 def verify_object_permission(self, name):
273 object_hrn = self.object_gid.get_hrn()
274 if object_hrn == name:
276 if name.startswith(object_hrn + "."):
278 raise PermissionError(name)
281 # Fill in the planetlab-specific fields of a Geni record. This involves
282 # calling the appropriate PLC methods to retrieve the database record for
285 # PLC data is filled into the pl_info field of the record.
287 # @param record record to fill in fields (in/out param)
289 def fill_record_pl_info(self, record):
290 type = record.get_type()
291 pointer = record.get_pointer()
293 # records with pointer==-1 do not have plc info associated with them.
294 # for example, the top level authority records which are
295 # authorities, but not PL "sites"
297 record.set_pl_info({})
300 if (type == "sa") or (type == "ma"):
301 pl_res = self.shell.GetSites(self.pl_auth, [pointer])
302 elif (type == "slice"):
303 pl_res = self.shell.GetSlices(self.pl_auth, [pointer])
304 elif (type == "user"):
305 pl_res = self.shell.GetPersons(self.pl_auth, [pointer])
306 elif (type == "node"):
307 pl_res = self.shell.GetNodes(self.pl_auth, [pointer])
309 raise UnknownGeniType(type)
312 # the planetlab record no longer exists
313 # TODO: delete the geni record ?
314 raise PlanetLabRecordDoesNotExist(record.get_name())
316 record.set_pl_info(pl_res[0])
319 # Look up user records given PLC user-ids. This is used as part of the
320 # process for reverse-mapping PLC records into Geni records.
322 # @param auth_table database table for the authority that holds the user records
323 # @param user_id_list list of user ids
324 # @param role either "*" or a string describing the role to look for ("pi", "user", ...)
326 # TODO: This function currently only searches one authority because it would
327 # be inefficient to brute-force search all authorities for a user id. The
328 # solution would likely be to implement a reverse mapping of user-id to
331 def lookup_users(self, auth_table, user_id_list, role="*"):
333 for person_id in user_id_list:
334 user_records = auth_table.find("user", person_id, "pointer")
335 for user_record in user_records:
336 self.fill_record_info(user_record)
338 user_roles = user_record.get_pl_info().get("roles")
339 if (role=="*") or (role in user_roles):
340 record_list.append(user_record.get_name())
344 # Fill in the geni-specific fields of the record.
346 # Note: It is assumed the fill_record_pl_info() has already been performed
349 def fill_record_geni_info(self, record):
351 type = record.get_type()
353 if (type == "slice"):
354 auth_table = self.get_auth_table(get_authority(record.get_name()))
355 person_ids = record.pl_info.get("person_ids", [])
356 researchers = self.lookup_users(auth_table, person_ids)
357 geni_info['researcher'] = researchers
360 auth_table = self.get_auth_table(record.get_name())
361 person_ids = record.pl_info.get("person_ids", [])
362 pis = self.lookup_users(auth_table, person_ids, "pi")
363 geni_info['pi'] = pis
364 # TODO: OrganizationName
367 auth_table = self.get_auth_table(record.get_name())
368 person_ids = record.pl_info.get("person_ids", [])
369 operators = self.lookup_users(auth_table, person_ids, "tech")
370 geni_info['operator'] = operators
371 # TODO: OrganizationName
373 auth_table = self.get_auth_table(record.get_name())
374 person_ids = record.pl_info.get("person_ids", [])
375 owners = self.lookup_users(auth_table, person_ids, "admin")
376 geni_info['owner'] = owners
378 elif (type == "node"):
379 geni_info['dns'] = record.pl_info.get("hostname", "")
380 # TODO: URI, LatLong, IP, DNS
382 elif (type == "user"):
383 geni_info['email'] = record.pl_info.get("email", "")
384 # TODO: PostalAddress, Phone
386 record.set_geni_info(geni_info)
389 # Given a Geni record, fill in the PLC-specific and Geni-specific fields
392 def fill_record_info(self, record):
393 self.fill_record_pl_info(record)
394 self.fill_record_geni_info(record)
396 def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
397 # get a list of the HRNs tht are members of the old and new records
\r
399 if oldRecord.pl_info == None:
\r
400 oldRecord.pl_info = {}
\r
401 oldList = oldRecord.get_geni_info().get(listName, [])
\r
404 newList = record.get_geni_info().get(listName, [])
\r
406 # if the lists are the same, then we don't have to update anything
\r
407 if (oldList == newList):
\r
410 # build a list of the new person ids, by looking up each person to get
\r
413 for hrn in newList:
\r
414 userRecord = self.resolve_raw("user", hrn)[0]
\r
415 newIdList.append(userRecord.get_pointer())
\r
417 # build a list of the old person ids from the person_ids field of the
\r
420 oldIdList = oldRecord.pl_info.get("person_ids", [])
\r
421 containerId = oldRecord.get_pointer()
\r
423 # if oldRecord==None, then we are doing a Register, instead of an
\r
426 containerId = record.get_pointer()
\r
428 # add people who are in the new list, but not the oldList
\r
429 for personId in newIdList:
\r
430 if not (personId in oldIdList):
\r
431 print "adding id", personId, "to", record.get_name()
\r
432 addFunc(self.pl_auth, personId, containerId)
\r
434 # remove people who are in the old list, but not the new list
\r
435 for personId in oldIdList:
\r
436 if not (personId in newIdList):
\r
437 print "removing id", personId, "from", record.get_name()
\r
438 delFunc(self.pl_auth, personId, containerId)
\r
440 def update_membership(self, oldRecord, record):
\r
441 if record.type == "slice":
\r
442 self.update_membership_list(oldRecord, record, 'researcher',
\r
443 self.shell.AddPersonToSlice,
\r
444 self.shell.DeletePersonFromSlice)
\r
445 elif record.type == "sa":
\r
448 elif record.type == "ma":
\r
455 # Register an object with the registry. In addition to being stored in the
456 # Geni database, the appropriate records will also be created in the
459 # @param cred credential string
460 # @param record_dict dictionary containing record fields
462 def register(self, cred, record_dict):
463 self.decode_authentication(cred, "register")
465 record = GeniRecord(dict = record_dict)
466 type = record.get_type()
467 name = record.get_name()
469 auth_name = get_authority(name)
470 self.verify_object_permission(auth_name)
471 auth_info = self.get_auth_info(auth_name)
472 table = self.get_auth_table(auth_name)
476 # check if record already exists
477 existing_records = table.resolve(type, name)
479 raise ExistingRecord(name)
481 if (type == "sa") or (type=="ma"):
483 if not self.hierarchy.auth_exists(name):
484 self.hierarchy.create_auth(name)
486 # authorities are special since they are managed by the registry
487 # rather than by the caller. We create our own GID for the
488 # authority rather than relying on the caller to supply one.
490 # get the GID from the newly created authority
491 child_auth_info = self.get_auth_info(name)
492 gid = auth_info.get_gid_object()
493 record.set_gid(gid.save_to_string(save_parents=True))
495 geni_fields = record.get_geni_info()
496 site_fields = record.get_pl_info()
498 # if registering a sa, see if a ma already exists
499 # if registering a ma, see if a sa already exists
501 other_rec = table.resolve("ma", record.get_name())
503 other_rec = table.resolve("sa", record.get_name())
506 print "linking ma and sa to the same plc site"
507 pointer = other_rec[0].get_pointer()
509 geni_fields_to_pl_fields(type, name, geni_fields, site_fields)
510 print "adding site with fields", site_fields
511 pointer = self.shell.AddSite(self.pl_auth, site_fields)
513 record.set_pointer(pointer)
515 elif (type == "slice"):
516 geni_fields = record.get_geni_info()
517 slice_fields = record.get_pl_info()
519 geni_fields_to_pl_fields(type, name, geni_fields, slice_fields)
521 pointer = self.shell.AddSlice(self.pl_auth, slice_fields)
522 record.set_pointer(pointer)
524 elif (type == "user"):
525 geni_fields = record.get_geni_info()
526 user_fields = record.get_pl_info()
528 geni_fields_to_pl_fields(type, name, geni_fields, user_fields)
530 pointer = self.shell.AddPerson(self.pl_auth, user_fields)
531 record.set_pointer(pointer)
533 elif (type == "node"):
534 geni_fields = record.get_geni_info()
535 node_fields = record.get_pl_info()
537 geni_fields_to_pl_fields(type, name, geni_fields, node_fields)
539 login_base = hrn_to_pl_login_base(auth_name)
541 print "calling addnode with", login_base, node_fields
542 pointer = self.shell.AddNode(self.pl_auth, login_base, node_fields)
543 record.set_pointer(pointer)
546 raise UnknownGeniType(type)
550 # update membership for researchers, pis, owners, operators
551 self.update_membership(None, record)
553 return record.get_gid_object().save_to_string(save_parents=True)
558 # Remove an object from the registry. If the object represents a PLC object,
559 # then the PLC records will also be removed.
561 # @param cred credential string
562 # @param record_dict dictionary containing record fields. The only relevant
563 # fields of the record are 'name' and 'type', which are used to lookup
564 # the current copy of the record in the Geni database, to make sure
565 # that the appopriate record is removed.
567 def remove(self, cred, type, hrn):
568 self.decode_authentication(cred, "remove")
570 self.verify_object_permission(hrn)
572 auth_name = get_authority(hrn)
573 table = self.get_auth_table(auth_name)
575 record_list = table.resolve(type, hrn)
577 raise RecordNotFound(hrn)
578 record = record_list[0]
582 self.shell.DeletePerson(self.pl_auth, record.get_pointer())
583 elif type == "slice":
584 self.shell.DeleteSlice(self.pl_auth, record.get_pointer())
586 self.shell.DeleteNode(self.pl_auth, record.get_pointer())
587 elif (type == "sa") or (type == "ma"):
589 other_rec = table.resolve("ma", record.get_name())
591 other_rec = table.resolve("sa", record.get_name())
594 # sa and ma both map to a site, so if we are deleting one
595 # but the other still exists, then do not delete the site
596 print "not removing site", record.get_name(), "because either sa or ma still exists"
599 print "removing site", record.get_name()
600 self.shell.DeleteSite(self.pl_auth, record.get_pointer())
602 raise UnknownGeniType(type)
611 # Update an object in the registry. Currently, this only updates the
612 # PLC information associated with the record. The Geni fields (name, type,
615 # The record is expected to have the pl_info field filled in with the data
616 # that should be updated.
618 # TODO: The geni_info member of the record should be parsed and the pl_info
619 # adjusted as necessary (add/remove users from a slice, etc)
621 # @param cred credential string specifying rights of the caller
622 # @param record a record dictionary to be updated
624 def update(self, cred, record_dict):
625 self.decode_authentication(cred, "update")
627 record = GeniRecord(dict = record_dict)
628 type = record.get_type()
630 self.verify_object_permission(record.get_name())
632 auth_name = get_authority(record.get_name())
634 auth_name = record.get_name()
635 table = self.get_auth_table(auth_name)
637 # make sure the record exists
638 existing_record_list = table.resolve(type, record.get_name())
639 if not existing_record_list:
640 raise RecordNotFound(record.get_name())
641 existing_record = existing_record_list[0]
643 # Update_membership needs the membership lists in the existing record
644 # filled in, so it can see if members were added or removed
645 self.fill_record_info(existing_record)
647 # Use the pointer from the existing record, not the one that the user
648 # gave us. This prevents the user from inserting a forged pointer
649 pointer = existing_record.get_pointer()
651 # update the PLC information that was specified with the record
653 if (type == "sa") or (type == "ma"):
654 self.shell.UpdateSite(self.pl_auth, pointer, record.get_pl_info())
656 elif type == "slice":
657 self.shell.UpdateSlice(self.pl_auth, pointer, record.get_pl_info())
660 # SMBAKER: UpdatePerson only allows a limited set of fields to be
661 # updated. Ideally we should have a more generic way of doing
662 # this. I copied the field names from UpdatePerson.py...
664 all_fields = record.get_pl_info()
665 for key in all_fields.keys():
666 if key in ['first_name', 'last_name', 'title', 'email',
667 'password', 'phone', 'url', 'bio', 'accepted_aup',
\r
669 update_fields[key] = all_fields[key]
670 self.shell.UpdatePerson(self.pl_auth, pointer, update_fields)
673 self.shell.UpdateNode(self.pl_auth, pointer, record.get_pl_info())
676 raise UnknownGeniType(type)
678 # update membership for researchers, pis, owners, operators
\r
679 self.update_membership(existing_record, record)
682 # List the records in an authority. The objectGID in the supplied credential
683 # should name the authority that will be listed.
685 # TODO: List doesn't take an hrn and uses the hrn contained in the
686 # objectGid of the credential. Does this mean the only way to list an
687 # authority is by having a credential for that authority?
689 # @param cred credential string specifying rights of the caller
691 # @return list of record dictionaries
692 def list(self, cred, auth_hrn):
693 self.decode_authentication(cred, "list")
695 if not self.hierarchy.auth_exists(auth_hrn):
696 raise MissingAuthority(auth_hrn)
698 table = self.get_auth_table(auth_hrn)
700 records = table.list()
703 for record in records:
705 self.fill_record_info(record)
706 good_records.append(record)
707 except PlanetLabRecordDoesNotExist:
708 # silently drop the ones that are missing in PL.
709 # is this the right thing to do?
710 print "ignoring geni record ", record.get_name(), " because pl record does not exist"
714 for record in good_records:
715 dicts.append(record.as_dict())
722 # Resolve a record. This is an internal version of the Resolve API call
723 # and returns records in record object format rather than dictionaries
724 # that may be sent over XMLRPC.
726 # @param type type of record to resolve (user | sa | ma | slice | node)
727 # @param name human readable name of object
728 # @param must_exist if True, throw an exception if no records are found
730 # @return a list of record objects, or an empty list []
732 def resolve_raw(self, type, name, must_exist=True):
733 auth_name = get_authority(name)
736 table = self.get_auth_table(auth_name)
737 records = table.resolve(type, name)
738 if (not records) and must_exist:
739 raise RecordNotFound(name)
742 for record in records:
744 self.fill_record_info(record)
745 good_records.append(record)
746 except PlanetLabRecordDoesNotExist:
747 # silently drop the ones that are missing in PL.
748 # is this the right thing to do?
749 print "ignoring geni record ", record.get_name(), "because pl record does not exist"
757 # This is a wrapper around resolve_raw that converts records objects into
758 # dictionaries before returning them to the user.
760 # @param cred credential string authorizing the caller
761 # @param name human readable name to resolve
763 # @return a list of record dictionaries, or an empty list
765 def resolve(self, cred, name):
766 self.decode_authentication(cred, "resolve")
769 records = self.resolve_raw("*", name)
772 for registry in self.registries:
773 if name.startswith(registry):
774 records = self.registries[registry].resolve(self.credential, 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 self.get_self_credential(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)