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 # XX This is really the aggregate's credential. Using this is easier than getting
159 # the registry's credential from iteslf (ssl errors).
160 ma_cred_filename = self.server_basedir + os.sep + "agg." + self.hrn + ".ma.cred"
162 self.credential = Credential(filename = ma_cred_filename)
164 self.credential = self.getCredentialFromRegistry()
166 def getCredentialFromRegistry(self):
168 Get our current credential from the registry.
170 # get self credential
171 self_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".cred"
172 self_cred = self.registry.get_credential(None, 'ma', self.hrn)
173 self_cred.save_to_file(self_cred_filename, save_parents=True)
176 ma_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".sa.cred"
177 ma_cred = self.registry.get_credential(self_cred, 'sa', self.hrn)
178 ma_cred.save_to_file(ma_cred_filename, save_parents=True)
181 def connectRegistry(self):
183 Connect to the registry
185 # connect to registry using GeniClient
186 address = self.config.GENI_REGISTRY_HOSTNAME
187 port = self.config.GENI_REGISTRY_PORT
188 url = 'http://%(address)s:%(port)s' % locals()
189 self.registry = GeniClient(url, self.key_file, self.cert_file)
191 def connectRegistries(self):
193 Get connection details for the trusted peer registries from file and
194 create an GeniClient connection to each.
197 registries = self.registry_info['registries']['registry']
198 if isinstance(registries, dict):
199 registries = [registries]
200 if isinstance(registries, list):
201 for registry in registries:
202 # create xmlrpc connection using GeniClient
203 hrn, address, port = registry['hrn'], registry['addr'], registry['port']
204 url = 'http://%(address)s:%(port)s' % locals()
205 self.registries[hrn] = GeniClient(url, self.key_file, self.cert_file)
208 # Given an authority name, return the information for that authority. This
209 # is basically a stub that calls the hierarchy module.
211 # @param auth_hrn human readable name of authority
213 def get_auth_info(self, auth_hrn):
214 return self.hierarchy.get_auth_info(auth_hrn)
217 # Given an authority name, return the database table for that authority. If
218 # the database table does not exist, then one will be automatically
221 # @param auth_name human readable name of authority
223 def get_auth_table(self, auth_name):
224 auth_info = self.get_auth_info(auth_name)
226 table = GeniTable(hrn=auth_name,
227 cninfo=auth_info.get_dbinfo())
229 # if the table doesn't exist, then it means we haven't put any records
230 # into this authority yet.
232 if not table.exists():
233 print "Registry: creating table for authority", auth_name
239 # Verify that an authority belongs to this registry. This is basically left
240 # up to the implementation of the hierarchy module. If the specified name
241 # does not belong to this registry, an exception is thrown indicating the
242 # caller should contact someone else.
244 # @param auth_name human readable name of authority
246 def verify_auth_belongs_to_me(self, name):
247 # get_auth_info will throw an exception if the authority does not
249 self.get_auth_info(name)
252 # Verify that an object belongs to this registry. By extension, this implies
253 # that the authority that owns the object belongs to this registry. If the
254 # object does not belong to this registry, then an exception is thrown.
256 # @param name human readable name of object
258 def verify_object_belongs_to_me(self, name):
259 auth_name = get_authority(name)
261 # the root authority belongs to the registry by default?
262 # TODO: is this true?
264 self.verify_auth_belongs_to_me(auth_name)
267 # Verify that the object_gid that was specified in the credential allows
268 # permission to the object 'name'. This is done by a simple prefix test.
269 # For example, an object_gid for planetlab.us.arizona would match the
270 # objects planetlab.us.arizona.slice1 and planetlab.us.arizona.
272 # @param name human readable name to test
274 def verify_object_permission(self, name):
275 object_hrn = self.object_gid.get_hrn()
276 if object_hrn == name:
278 if name.startswith(object_hrn + "."):
280 raise PermissionError(name)
283 # Fill in the planetlab-specific fields of a Geni record. This involves
284 # calling the appropriate PLC methods to retrieve the database record for
287 # PLC data is filled into the pl_info field of the record.
289 # @param record record to fill in fields (in/out param)
291 def fill_record_pl_info(self, record):
292 type = record.get_type()
293 pointer = record.get_pointer()
295 # records with pointer==-1 do not have plc info associated with them.
296 # for example, the top level authority records which are
297 # authorities, but not PL "sites"
299 record.set_pl_info({})
302 if (type == "sa") or (type == "ma"):
303 pl_res = self.shell.GetSites(self.pl_auth, [pointer])
304 elif (type == "slice"):
305 pl_res = self.shell.GetSlices(self.pl_auth, [pointer])
306 elif (type == "user"):
307 pl_res = self.shell.GetPersons(self.pl_auth, [pointer])
308 elif (type == "node"):
309 pl_res = self.shell.GetNodes(self.pl_auth, [pointer])
311 raise UnknownGeniType(type)
314 # the planetlab record no longer exists
315 # TODO: delete the geni record ?
316 raise PlanetLabRecordDoesNotExist(record.get_name())
318 record.set_pl_info(pl_res[0])
321 # Look up user records given PLC user-ids. This is used as part of the
322 # process for reverse-mapping PLC records into Geni records.
324 # @param auth_table database table for the authority that holds the user records
325 # @param user_id_list list of user ids
326 # @param role either "*" or a string describing the role to look for ("pi", "user", ...)
328 # TODO: This function currently only searches one authority because it would
329 # be inefficient to brute-force search all authorities for a user id. The
330 # solution would likely be to implement a reverse mapping of user-id to
333 def lookup_users(self, auth_table, user_id_list, role="*"):
335 for person_id in user_id_list:
336 user_records = auth_table.find("user", person_id, "pointer")
337 for user_record in user_records:
338 self.fill_record_info(user_record)
340 user_roles = user_record.get_pl_info().get("roles")
341 if (role=="*") or (role in user_roles):
342 record_list.append(user_record.get_name())
346 # Fill in the geni-specific fields of the record.
348 # Note: It is assumed the fill_record_pl_info() has already been performed
351 def fill_record_geni_info(self, record):
353 type = record.get_type()
355 if (type == "slice"):
356 auth_table = self.get_auth_table(get_authority(record.get_name()))
357 person_ids = record.pl_info.get("person_ids", [])
358 researchers = self.lookup_users(auth_table, person_ids)
359 geni_info['researcher'] = researchers
362 auth_table = self.get_auth_table(record.get_name())
363 person_ids = record.pl_info.get("person_ids", [])
364 pis = self.lookup_users(auth_table, person_ids, "pi")
365 geni_info['pi'] = pis
366 # TODO: OrganizationName
369 auth_table = self.get_auth_table(record.get_name())
370 person_ids = record.pl_info.get("person_ids", [])
371 operators = self.lookup_users(auth_table, person_ids, "tech")
372 geni_info['operator'] = operators
373 # TODO: OrganizationName
375 auth_table = self.get_auth_table(record.get_name())
376 person_ids = record.pl_info.get("person_ids", [])
377 owners = self.lookup_users(auth_table, person_ids, "admin")
378 geni_info['owner'] = owners
380 elif (type == "node"):
381 geni_info['dns'] = record.pl_info.get("hostname", "")
382 # TODO: URI, LatLong, IP, DNS
384 elif (type == "user"):
385 geni_info['email'] = record.pl_info.get("email", "")
386 # TODO: PostalAddress, Phone
388 record.set_geni_info(geni_info)
391 # Given a Geni record, fill in the PLC-specific and Geni-specific fields
394 def fill_record_info(self, record):
395 self.fill_record_pl_info(record)
396 self.fill_record_geni_info(record)
398 def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
399 # get a list of the HRNs tht are members of the old and new records
\r
401 if oldRecord.pl_info == None:
\r
402 oldRecord.pl_info = {}
\r
403 oldList = oldRecord.get_geni_info().get(listName, [])
\r
406 newList = record.get_geni_info().get(listName, [])
\r
408 # if the lists are the same, then we don't have to update anything
\r
409 if (oldList == newList):
\r
412 # build a list of the new person ids, by looking up each person to get
\r
415 for hrn in newList:
\r
416 userRecord = self.resolve_raw("user", hrn)[0]
\r
417 newIdList.append(userRecord.get_pointer())
\r
419 # build a list of the old person ids from the person_ids field of the
\r
422 oldIdList = oldRecord.pl_info.get("person_ids", [])
\r
423 containerId = oldRecord.get_pointer()
\r
425 # if oldRecord==None, then we are doing a Register, instead of an
\r
428 containerId = record.get_pointer()
\r
430 # add people who are in the new list, but not the oldList
\r
431 for personId in newIdList:
\r
432 if not (personId in oldIdList):
\r
433 print "adding id", personId, "to", record.get_name()
\r
434 addFunc(self.pl_auth, personId, containerId)
\r
436 # remove people who are in the old list, but not the new list
\r
437 for personId in oldIdList:
\r
438 if not (personId in newIdList):
\r
439 print "removing id", personId, "from", record.get_name()
\r
440 delFunc(self.pl_auth, personId, containerId)
\r
442 def update_membership(self, oldRecord, record):
\r
443 if record.type == "slice":
\r
444 self.update_membership_list(oldRecord, record, 'researcher',
\r
445 self.shell.AddPersonToSlice,
\r
446 self.shell.DeletePersonFromSlice)
\r
447 elif record.type == "sa":
\r
450 elif record.type == "ma":
\r
457 # Register an object with the registry. In addition to being stored in the
458 # Geni database, the appropriate records will also be created in the
461 # @param cred credential string
462 # @param record_dict dictionary containing record fields
464 def register(self, cred, record_dict):
465 self.decode_authentication(cred, "register")
467 record = GeniRecord(dict = record_dict)
468 type = record.get_type()
469 name = record.get_name()
471 auth_name = get_authority(name)
472 self.verify_object_permission(auth_name)
473 auth_info = self.get_auth_info(auth_name)
474 table = self.get_auth_table(auth_name)
478 # check if record already exists
479 existing_records = table.resolve(type, name)
481 raise ExistingRecord(name)
483 if (type == "sa") or (type=="ma"):
485 if not self.hierarchy.auth_exists(name):
486 self.hierarchy.create_auth(name)
488 # authorities are special since they are managed by the registry
489 # rather than by the caller. We create our own GID for the
490 # authority rather than relying on the caller to supply one.
492 # get the GID from the newly created authority
493 child_auth_info = self.get_auth_info(name)
494 gid = auth_info.get_gid_object()
495 record.set_gid(gid.save_to_string(save_parents=True))
497 geni_fields = record.get_geni_info()
498 site_fields = record.get_pl_info()
500 # if registering a sa, see if a ma already exists
501 # if registering a ma, see if a sa already exists
503 other_rec = table.resolve("ma", record.get_name())
505 other_rec = table.resolve("sa", record.get_name())
508 print "linking ma and sa to the same plc site"
509 pointer = other_rec[0].get_pointer()
511 geni_fields_to_pl_fields(type, name, geni_fields, site_fields)
512 print "adding site with fields", site_fields
513 pointer = self.shell.AddSite(self.pl_auth, site_fields)
515 record.set_pointer(pointer)
517 elif (type == "slice"):
518 geni_fields = record.get_geni_info()
519 slice_fields = record.get_pl_info()
521 geni_fields_to_pl_fields(type, name, geni_fields, slice_fields)
523 pointer = self.shell.AddSlice(self.pl_auth, slice_fields)
524 record.set_pointer(pointer)
526 elif (type == "user"):
527 geni_fields = record.get_geni_info()
528 user_fields = record.get_pl_info()
530 geni_fields_to_pl_fields(type, name, geni_fields, user_fields)
532 pointer = self.shell.AddPerson(self.pl_auth, user_fields)
533 record.set_pointer(pointer)
535 elif (type == "node"):
536 geni_fields = record.get_geni_info()
537 node_fields = record.get_pl_info()
539 geni_fields_to_pl_fields(type, name, geni_fields, node_fields)
541 login_base = hrn_to_pl_login_base(auth_name)
543 print "calling addnode with", login_base, node_fields
544 pointer = self.shell.AddNode(self.pl_auth, login_base, node_fields)
545 record.set_pointer(pointer)
548 raise UnknownGeniType(type)
552 # update membership for researchers, pis, owners, operators
553 self.update_membership(None, record)
555 return record.get_gid_object().save_to_string(save_parents=True)
560 # Remove an object from the registry. If the object represents a PLC object,
561 # then the PLC records will also be removed.
563 # @param cred credential string
564 # @param record_dict dictionary containing record fields. The only relevant
565 # fields of the record are 'name' and 'type', which are used to lookup
566 # the current copy of the record in the Geni database, to make sure
567 # that the appopriate record is removed.
569 def remove(self, cred, type, hrn):
570 self.decode_authentication(cred, "remove")
572 self.verify_object_permission(hrn)
574 auth_name = get_authority(hrn)
575 table = self.get_auth_table(auth_name)
577 record_list = table.resolve(type, hrn)
579 raise RecordNotFound(hrn)
580 record = record_list[0]
584 self.shell.DeletePerson(self.pl_auth, record.get_pointer())
585 elif type == "slice":
586 self.shell.DeleteSlice(self.pl_auth, record.get_pointer())
588 self.shell.DeleteNode(self.pl_auth, record.get_pointer())
589 elif (type == "sa") or (type == "ma"):
591 other_rec = table.resolve("ma", record.get_name())
593 other_rec = table.resolve("sa", record.get_name())
596 # sa and ma both map to a site, so if we are deleting one
597 # but the other still exists, then do not delete the site
598 print "not removing site", record.get_name(), "because either sa or ma still exists"
601 print "removing site", record.get_name()
602 self.shell.DeleteSite(self.pl_auth, record.get_pointer())
604 raise UnknownGeniType(type)
613 # Update an object in the registry. Currently, this only updates the
614 # PLC information associated with the record. The Geni fields (name, type,
617 # The record is expected to have the pl_info field filled in with the data
618 # that should be updated.
620 # TODO: The geni_info member of the record should be parsed and the pl_info
621 # adjusted as necessary (add/remove users from a slice, etc)
623 # @param cred credential string specifying rights of the caller
624 # @param record a record dictionary to be updated
626 def update(self, cred, record_dict):
627 self.decode_authentication(cred, "update")
629 record = GeniRecord(dict = record_dict)
630 type = record.get_type()
632 self.verify_object_permission(record.get_name())
634 auth_name = get_authority(record.get_name())
636 auth_name = record.get_name()
637 table = self.get_auth_table(auth_name)
639 # make sure the record exists
640 existing_record_list = table.resolve(type, record.get_name())
641 if not existing_record_list:
642 raise RecordNotFound(record.get_name())
643 existing_record = existing_record_list[0]
645 # Update_membership needs the membership lists in the existing record
646 # filled in, so it can see if members were added or removed
647 self.fill_record_info(existing_record)
649 # Use the pointer from the existing record, not the one that the user
650 # gave us. This prevents the user from inserting a forged pointer
651 pointer = existing_record.get_pointer()
653 # update the PLC information that was specified with the record
655 if (type == "sa") or (type == "ma"):
656 self.shell.UpdateSite(self.pl_auth, pointer, record.get_pl_info())
658 elif type == "slice":
659 self.shell.UpdateSlice(self.pl_auth, pointer, record.get_pl_info())
662 # SMBAKER: UpdatePerson only allows a limited set of fields to be
663 # updated. Ideally we should have a more generic way of doing
664 # this. I copied the field names from UpdatePerson.py...
666 all_fields = record.get_pl_info()
667 for key in all_fields.keys():
668 if key in ['first_name', 'last_name', 'title', 'email',
669 'password', 'phone', 'url', 'bio', 'accepted_aup',
\r
671 update_fields[key] = all_fields[key]
672 self.shell.UpdatePerson(self.pl_auth, pointer, update_fields)
675 self.shell.UpdateNode(self.pl_auth, pointer, record.get_pl_info())
678 raise UnknownGeniType(type)
680 # update membership for researchers, pis, owners, operators
\r
681 self.update_membership(existing_record, record)
684 # List the records in an authority. The objectGID in the supplied credential
685 # should name the authority that will be listed.
687 # TODO: List doesn't take an hrn and uses the hrn contained in the
688 # objectGid of the credential. Does this mean the only way to list an
689 # authority is by having a credential for that authority?
691 # @param cred credential string specifying rights of the caller
693 # @return list of record dictionaries
694 def list(self, cred, auth_hrn):
695 self.decode_authentication(cred, "list")
697 if not self.hierarchy.auth_exists(auth_hrn):
698 raise MissingAuthority(auth_hrn)
700 table = self.get_auth_table(auth_hrn)
702 records = table.list()
705 for record in records:
707 self.fill_record_info(record)
708 good_records.append(record)
709 except PlanetLabRecordDoesNotExist:
710 # silently drop the ones that are missing in PL.
711 # is this the right thing to do?
712 print "ignoring geni record ", record.get_name(), " because pl record does not exist"
716 for record in good_records:
717 dicts.append(record.as_dict())
724 # Resolve a record. This is an internal version of the Resolve API call
725 # and returns records in record object format rather than dictionaries
726 # that may be sent over XMLRPC.
728 # @param type type of record to resolve (user | sa | ma | slice | node)
729 # @param name human readable name of object
730 # @param must_exist if True, throw an exception if no records are found
732 # @return a list of record objects, or an empty list []
734 def resolve_raw(self, type, name, must_exist=True):
735 auth_name = get_authority(name)
738 table = self.get_auth_table(auth_name)
739 records = table.resolve(type, name)
740 if (not records) and must_exist:
741 raise RecordNotFound(name)
744 for record in records:
746 self.fill_record_info(record)
747 good_records.append(record)
748 except PlanetLabRecordDoesNotExist:
749 # silently drop the ones that are missing in PL.
750 # is this the right thing to do?
751 print "ignoring geni record ", record.get_name(), "because pl record does not exist"
759 # This is a wrapper around resolve_raw that converts records objects into
760 # dictionaries before returning them to the user.
762 # @param cred credential string authorizing the caller
763 # @param name human readable name to resolve
765 # @return a list of record dictionaries, or an empty list
767 def resolve(self, cred, name):
768 self.decode_authentication(cred, "resolve")
771 records = self.resolve_raw("*", name)
774 for registry in self.registries:
775 if name.startswith(registry):
776 records = self.registries[registry].resolve(self.credential, name)
780 for record in records:
781 dicts.append(record.as_dict())
788 # Retrieve the GID for an object. This function looks up a record in the
789 # registry and returns the GID of the record if it exists.
790 # TODO: Is this function needed? It's a shortcut for Resolve()
792 # @param name hrn to look up
794 # @return the string representation of a GID object
796 def get_gid(self, name):
797 self.verify_object_belongs_to_me(name)
798 records = self.resolve_raw("*", name)
800 for record in records:
801 gid = record.get_gid_object()
802 gid_string_list.append(gid.save_to_string(save_parents=True))
803 return gid_string_list
806 # Determine tje rights that an object should have. The rights are entirely
807 # dependent on the type of the object. For example, users automatically
808 # get "refresh", "resolve", and "info".
810 # @param type the type of the object (user | sa | ma | slice | node)
811 # @param name human readable name of the object (not used at this time)
813 # @return RightList object containing rights
815 def determine_rights(self, type, name):
818 # rights seem to be somewhat redundant with the type of the credential.
819 # For example, a "sa" credential implies the authority right, because
820 # a sa credential cannot be issued to a user who is not an owner of
828 rl.add("authority,sa")
830 rl.add("authority,ma")
831 elif type == "slice":
837 elif type == "component":
843 # GENI API: Get_self_credential
845 # Get_self_credential a degenerate version of get_credential used by a
846 # client to get his initial credential when he doesn't have one. This is
847 # the same as get_credential(..., cred=None,...).
849 # The registry ensures that the client is the principal that is named by
850 # (type, name) by comparing the public key in the record's GID to the
851 # private key used to encrypt the client-side of the HTTPS connection. Thus
852 # it is impossible for one principal to retrieve another principal's
853 # credential without having the appropriate private key.
855 # @param type type of object (user | slice | sa | ma | node
856 # @param name human readable name of object
858 # @return the string representation of a credential object
860 def get_self_credential(self, type, name):
861 self.verify_object_belongs_to_me(name)
863 auth_hrn = get_authority(name)
866 auth_info = self.get_auth_info(auth_hrn)
867 # find a record that matches
868 records = self.resolve_raw(type, name, must_exist=True)
871 gid = record.get_gid_object()
872 peer_cert = self.server.peer_cert
873 if not peer_cert.is_pubkey(gid.get_pubkey()):
874 raise ConnectionKeyGIDMismatch(gid.get_subject())
876 # create the credential
877 gid = record.get_gid_object()
878 cred = Credential(subject = gid.get_subject())
879 cred.set_gid_caller(gid)
880 cred.set_gid_object(gid)
881 cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
882 cred.set_pubkey(gid.get_pubkey())
884 rl = self.determine_rights(type, name)
885 cred.set_privileges(rl)
887 # determine the type of credential that we want to use as a parent for
890 if (type == "ma") or (type == "node"):
891 auth_kind = "authority,ma"
892 else: # user, slice, sa
893 auth_kind = "authority,sa"
895 cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
900 return cred.save_to_string(save_parents=True)
903 # verify_cancreate_credential
905 # Verify that a user can retrieve a particular type of credential. For
906 # slices, the user must be on the researcher list. For SA and MA the user
907 # must be on the pi and operator lists respectively.
909 def verify_cancreate_credential(self, src_cred, record):
910 type = record.get_type()
911 cred_object_hrn = src_cred.get_gid_object().get_hrn()
913 if cred_object_hrn in [config.GENI_REGISTRY_ROOT_AUTH]:
916 researchers = record.get_geni_info().get("researcher", [])
917 if not (cred_object_hrn in researchers):
918 raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name())
920 pis = record.get_geni_info().get("pi", [])
921 if not (cred_object_hrn in pis):
922 raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name())
924 operators = record.get_geni_info().get("operator", [])
925 if not (cred_object_hrn in operators):
926 raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name())
929 # GENI API: Get_credential
931 # Retrieve a credential for an object.
933 # If cred==None, then the behavior reverts to get_self_credential()
935 # @param cred credential object specifying rights of the caller
936 # @param type type of object (user | slice | sa | ma | node)
937 # @param name human readable name of object
939 # @return the string representation of a credental object
941 def get_credential(self, cred, type, name):
943 return self.get_self_credential(type, name)
945 self.decode_authentication(cred, "getcredential")
947 self.verify_object_belongs_to_me(name)
949 auth_hrn = get_authority(name)
952 auth_info = self.get_auth_info(auth_hrn)
954 records = self.resolve_raw(type, name, must_exist=True)
957 # verify_cancreate_credential requires that the member lists
958 # (researchers, pis, etc) be filled in
959 self.fill_record_info(record)
961 self.verify_cancreate_credential(self.client_cred, record)
963 # TODO: Check permission that self.client_cred can access the object
965 object_gid = record.get_gid_object()
966 new_cred = Credential(subject = object_gid.get_subject())
967 new_cred.set_gid_caller(self.client_gid)
968 new_cred.set_gid_object(object_gid)
969 new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
970 new_cred.set_pubkey(object_gid.get_pubkey())
972 rl = self.determine_rights(type, name)
973 new_cred.set_privileges(rl)
975 # determine the type of credential that we want to use as a parent for
978 if (type == "ma") or (type == "node"):
979 auth_kind = "authority,ma"
980 else: # user, slice, sa
981 auth_kind = "authority,sa"
983 new_cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
988 return new_cred.save_to_string(save_parents=True)
991 # GENI API: get_ticket
993 # Retrieve a ticket. This operation is currently implemented on PLC
994 # only (see SFA, engineering decisions); it is not implemented on
997 # The ticket is filled in with information from the PLC database. This
998 # information includes resources, and attributes such as user keys and
1001 # @param cred credential string
1002 # @param name name of the slice to retrieve a ticket for
1003 # @param rspec resource specification dictionary
1005 # @return the string representation of a ticket object
1007 def get_ticket(self, cred, name, rspec):
1008 self.decode_authentication(cred, "getticket")
1010 self.verify_object_belongs_to_me(name)
1012 self.verify_object_permission(name)
1014 # XXX much of this code looks like get_credential... are they so similar
1015 # that they should be combined?
1017 auth_hrn = get_authority(name)
1020 auth_info = self.get_auth_info(auth_hrn)
1022 records = self.resolve_raw("slice", name, must_exist=True)
1025 object_gid = record.get_gid_object()
1026 new_ticket = Ticket(subject = object_gid.get_subject())
1027 new_ticket.set_gid_caller(self.client_gid)
1028 new_ticket.set_gid_object(object_gid)
1029 new_ticket.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
1030 new_ticket.set_pubkey(object_gid.get_pubkey())
1032 self.fill_record_info(record)
1034 (attributes, rspec) = self.record_to_slice_info(record)
1036 new_ticket.set_attributes(attributes)
1037 new_ticket.set_rspec(rspec)
1039 new_ticket.set_parent(AuthHierarchy.get_auth_ticket(auth_hrn))
1044 return new_ticket.save_to_string(save_parents=True)
1047 # GENI_API: Create_gid
1049 # Create a new GID. For MAs and SAs that are physically located on the
1050 # registry, this allows a owner/operator/PI to create a new GID and have it
1051 # signed by his respective authority.
1053 # @param cred credential of caller
1054 # @param name hrn for new GID
1055 # @param uuid unique identifier for new GID
1056 # @param pkey_string public-key string (TODO: why is this a string and not a keypair object?)
1058 # @return the string representation of a GID object
1060 def create_gid(self, cred, name, uuid, pubkey_str):
1061 self.decode_authentication(cred, "getcredential")
1063 self.verify_object_belongs_to_me(name)
1065 self.verify_object_permission(name)
1068 uuid = create_uuid()
1071 pkey.load_pubkey_from_string(pubkey_str)
1072 gid = self.hierarchy.create_gid(name, uuid, pkey)
1074 return gid.save_to_string(save_parents=True)