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 required_fields = ['hrn', 'addr', 'port']
198 registries = self.registry_info['registries']['registry']
199 if isinstance(registries, dict):
200 registries = [registries]
201 if isinstance(registries, list):
202 for registry in registries:
203 # create xmlrpc connection using GeniClient
204 if not set(required_fields).issubset(registry.keys()):
206 hrn, address, port = registry['hrn'], registry['addr'], registry['port']
207 if not hrn or not address or not port:
209 url = 'http://%(address)s:%(port)s' % locals()
210 self.registries[hrn] = GeniClient(url, self.key_file, self.cert_file)
213 # Given an authority name, return the information for that authority. This
214 # is basically a stub that calls the hierarchy module.
216 # @param auth_hrn human readable name of authority
218 def get_auth_info(self, auth_hrn):
219 return self.hierarchy.get_auth_info(auth_hrn)
222 # Given an authority name, return the database table for that authority. If
223 # the database table does not exist, then one will be automatically
226 # @param auth_name human readable name of authority
228 def get_auth_table(self, auth_name):
229 auth_info = self.get_auth_info(auth_name)
231 table = GeniTable(hrn=auth_name,
232 cninfo=auth_info.get_dbinfo())
234 # if the table doesn't exist, then it means we haven't put any records
235 # into this authority yet.
237 if not table.exists():
238 print "Registry: creating table for authority", auth_name
244 # Verify that an authority belongs to this registry. This is basically left
245 # up to the implementation of the hierarchy module. If the specified name
246 # does not belong to this registry, an exception is thrown indicating the
247 # caller should contact someone else.
249 # @param auth_name human readable name of authority
251 def verify_auth_belongs_to_me(self, name):
252 # get_auth_info will throw an exception if the authority does not
254 self.get_auth_info(name)
257 # Verify that an object belongs to this registry. By extension, this implies
258 # that the authority that owns the object belongs to this registry. If the
259 # object does not belong to this registry, then an exception is thrown.
261 # @param name human readable name of object
263 def verify_object_belongs_to_me(self, name):
264 auth_name = get_authority(name)
266 # the root authority belongs to the registry by default?
267 # TODO: is this true?
269 self.verify_auth_belongs_to_me(auth_name)
272 # Verify that the object_gid that was specified in the credential allows
273 # permission to the object 'name'. This is done by a simple prefix test.
274 # For example, an object_gid for planetlab.us.arizona would match the
275 # objects planetlab.us.arizona.slice1 and planetlab.us.arizona.
277 # @param name human readable name to test
279 def verify_object_permission(self, name):
280 object_hrn = self.object_gid.get_hrn()
281 if object_hrn == name:
283 if name.startswith(object_hrn + "."):
285 raise PermissionError(name)
288 # Fill in the planetlab-specific fields of a Geni record. This involves
289 # calling the appropriate PLC methods to retrieve the database record for
292 # PLC data is filled into the pl_info field of the record.
294 # @param record record to fill in fields (in/out param)
296 def fill_record_pl_info(self, record):
297 type = record.get_type()
298 pointer = record.get_pointer()
300 # records with pointer==-1 do not have plc info associated with them.
301 # for example, the top level authority records which are
302 # authorities, but not PL "sites"
304 record.set_pl_info({})
307 if (type == "sa") or (type == "ma"):
308 pl_res = self.shell.GetSites(self.pl_auth, [pointer])
309 elif (type == "slice"):
310 pl_res = self.shell.GetSlices(self.pl_auth, [pointer])
311 elif (type == "user"):
312 pl_res = self.shell.GetPersons(self.pl_auth, [pointer])
313 key_ids = pl_res[0]['key_ids']
314 keys = self.shell.GetKeys(self.pl_auth, key_ids)
317 pubkeys = [key['key'] for key in keys]
318 pl_res[0]['keys'] = pubkeys
319 elif (type == "node"):
320 pl_res = self.shell.GetNodes(self.pl_auth, [pointer])
322 raise UnknownGeniType(type)
325 # the planetlab record no longer exists
326 # TODO: delete the geni record ?
327 raise PlanetLabRecordDoesNotExist(record.get_name())
329 record.set_pl_info(pl_res[0])
332 # Look up user records given PLC user-ids. This is used as part of the
333 # process for reverse-mapping PLC records into Geni records.
335 # @param auth_table database table for the authority that holds the user records
336 # @param user_id_list list of user ids
337 # @param role either "*" or a string describing the role to look for ("pi", "user", ...)
339 # TODO: This function currently only searches one authority because it would
340 # be inefficient to brute-force search all authorities for a user id. The
341 # solution would likely be to implement a reverse mapping of user-id to
344 def lookup_users(self, auth_table, user_id_list, role="*"):
346 for person_id in user_id_list:
347 user_records = auth_table.find("user", person_id, "pointer")
348 for user_record in user_records:
349 self.fill_record_info(user_record)
351 user_roles = user_record.get_pl_info().get("roles")
352 if (role=="*") or (role in user_roles):
353 record_list.append(user_record.get_name())
357 # Fill in the geni-specific fields of the record.
359 # Note: It is assumed the fill_record_pl_info() has already been performed
362 def fill_record_geni_info(self, record):
364 type = record.get_type()
366 if (type == "slice"):
367 auth_table = self.get_auth_table(get_authority(record.get_name()))
368 person_ids = record.pl_info.get("person_ids", [])
369 researchers = self.lookup_users(auth_table, person_ids)
370 geni_info['researcher'] = researchers
373 auth_table = self.get_auth_table(record.get_name())
374 person_ids = record.pl_info.get("person_ids", [])
375 pis = self.lookup_users(auth_table, person_ids, "pi")
376 geni_info['pi'] = pis
377 # TODO: OrganizationName
380 auth_table = self.get_auth_table(record.get_name())
381 person_ids = record.pl_info.get("person_ids", [])
382 operators = self.lookup_users(auth_table, person_ids, "tech")
383 geni_info['operator'] = operators
384 # TODO: OrganizationName
386 auth_table = self.get_auth_table(record.get_name())
387 person_ids = record.pl_info.get("person_ids", [])
388 owners = self.lookup_users(auth_table, person_ids, "admin")
389 geni_info['owner'] = owners
391 elif (type == "node"):
392 geni_info['dns'] = record.pl_info.get("hostname", "")
393 # TODO: URI, LatLong, IP, DNS
395 elif (type == "user"):
396 geni_info['email'] = record.pl_info.get("email", "")
397 # TODO: PostalAddress, Phone
399 record.set_geni_info(geni_info)
402 # Given a Geni record, fill in the PLC-specific and Geni-specific fields
405 def fill_record_info(self, record):
406 self.fill_record_pl_info(record)
407 self.fill_record_geni_info(record)
409 def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
410 # get a list of the HRNs tht are members of the old and new records
\r
412 if oldRecord.pl_info == None:
\r
413 oldRecord.pl_info = {}
\r
414 oldList = oldRecord.get_geni_info().get(listName, [])
\r
417 newList = record.get_geni_info().get(listName, [])
\r
419 # if the lists are the same, then we don't have to update anything
\r
420 if (oldList == newList):
\r
423 # build a list of the new person ids, by looking up each person to get
\r
426 for hrn in newList:
\r
427 userRecord = self.resolve_raw("user", hrn)[0]
\r
428 newIdList.append(userRecord.get_pointer())
\r
430 # build a list of the old person ids from the person_ids field of the
\r
433 oldIdList = oldRecord.pl_info.get("person_ids", [])
\r
434 containerId = oldRecord.get_pointer()
\r
436 # if oldRecord==None, then we are doing a Register, instead of an
\r
439 containerId = record.get_pointer()
\r
441 # add people who are in the new list, but not the oldList
\r
442 for personId in newIdList:
\r
443 if not (personId in oldIdList):
\r
444 print "adding id", personId, "to", record.get_name()
\r
445 addFunc(self.pl_auth, personId, containerId)
\r
447 # remove people who are in the old list, but not the new list
\r
448 for personId in oldIdList:
\r
449 if not (personId in newIdList):
\r
450 print "removing id", personId, "from", record.get_name()
\r
451 delFunc(self.pl_auth, personId, containerId)
\r
453 def update_membership(self, oldRecord, record):
\r
454 if record.type == "slice":
\r
455 self.update_membership_list(oldRecord, record, 'researcher',
\r
456 self.shell.AddPersonToSlice,
\r
457 self.shell.DeletePersonFromSlice)
\r
458 elif record.type == "sa":
\r
461 elif record.type == "ma":
\r
468 # Register an object with the registry. In addition to being stored in the
469 # Geni database, the appropriate records will also be created in the
472 # @param cred credential string
473 # @param record_dict dictionary containing record fields
475 def register(self, cred, record_dict):
476 self.decode_authentication(cred, "register")
478 record = GeniRecord(dict = record_dict)
479 type = record.get_type()
480 name = record.get_name()
482 auth_name = get_authority(name)
483 self.verify_object_permission(auth_name)
484 auth_info = self.get_auth_info(auth_name)
485 table = self.get_auth_table(auth_name)
489 # check if record already exists
490 existing_records = table.resolve(type, name)
492 raise ExistingRecord(name)
494 if (type == "sa") or (type=="ma"):
496 if not self.hierarchy.auth_exists(name):
497 self.hierarchy.create_auth(name)
499 # authorities are special since they are managed by the registry
500 # rather than by the caller. We create our own GID for the
501 # authority rather than relying on the caller to supply one.
503 # get the GID from the newly created authority
504 child_auth_info = self.get_auth_info(name)
505 gid = auth_info.get_gid_object()
506 record.set_gid(gid.save_to_string(save_parents=True))
508 geni_fields = record.get_geni_info()
509 site_fields = record.get_pl_info()
511 # if registering a sa, see if a ma already exists
512 # if registering a ma, see if a sa already exists
514 other_rec = table.resolve("ma", record.get_name())
516 other_rec = table.resolve("sa", record.get_name())
519 print "linking ma and sa to the same plc site"
520 pointer = other_rec[0].get_pointer()
522 geni_fields_to_pl_fields(type, name, geni_fields, site_fields)
523 print "adding site with fields", site_fields
524 pointer = self.shell.AddSite(self.pl_auth, site_fields)
526 record.set_pointer(pointer)
528 elif (type == "slice"):
529 geni_fields = record.get_geni_info()
530 slice_fields = record.get_pl_info()
532 geni_fields_to_pl_fields(type, name, geni_fields, slice_fields)
534 pointer = self.shell.AddSlice(self.pl_auth, slice_fields)
535 record.set_pointer(pointer)
537 elif (type == "user"):
538 geni_fields = record.get_geni_info()
539 user_fields = record.get_pl_info()
541 geni_fields_to_pl_fields(type, name, geni_fields, user_fields)
543 pointer = self.shell.AddPerson(self.pl_auth, user_fields)
544 record.set_pointer(pointer)
546 elif (type == "node"):
547 geni_fields = record.get_geni_info()
548 node_fields = record.get_pl_info()
550 geni_fields_to_pl_fields(type, name, geni_fields, node_fields)
552 login_base = hrn_to_pl_login_base(auth_name)
554 print "calling addnode with", login_base, node_fields
555 pointer = self.shell.AddNode(self.pl_auth, login_base, node_fields)
556 record.set_pointer(pointer)
559 raise UnknownGeniType(type)
563 # update membership for researchers, pis, owners, operators
564 self.update_membership(None, record)
566 return record.get_gid_object().save_to_string(save_parents=True)
571 # Remove an object from the registry. If the object represents a PLC object,
572 # then the PLC records will also be removed.
574 # @param cred credential string
575 # @param record_dict dictionary containing record fields. The only relevant
576 # fields of the record are 'name' and 'type', which are used to lookup
577 # the current copy of the record in the Geni database, to make sure
578 # that the appopriate record is removed.
580 def remove(self, cred, type, hrn):
581 self.decode_authentication(cred, "remove")
583 self.verify_object_permission(hrn)
585 auth_name = get_authority(hrn)
586 table = self.get_auth_table(auth_name)
588 record_list = table.resolve(type, hrn)
590 raise RecordNotFound(hrn)
591 record = record_list[0]
595 self.shell.DeletePerson(self.pl_auth, record.get_pointer())
596 elif type == "slice":
597 self.shell.DeleteSlice(self.pl_auth, record.get_pointer())
599 self.shell.DeleteNode(self.pl_auth, record.get_pointer())
600 elif (type == "sa") or (type == "ma"):
602 other_rec = table.resolve("ma", record.get_name())
604 other_rec = table.resolve("sa", record.get_name())
607 # sa and ma both map to a site, so if we are deleting one
608 # but the other still exists, then do not delete the site
609 print "not removing site", record.get_name(), "because either sa or ma still exists"
612 print "removing site", record.get_name()
613 self.shell.DeleteSite(self.pl_auth, record.get_pointer())
615 raise UnknownGeniType(type)
624 # Update an object in the registry. Currently, this only updates the
625 # PLC information associated with the record. The Geni fields (name, type,
628 # The record is expected to have the pl_info field filled in with the data
629 # that should be updated.
631 # TODO: The geni_info member of the record should be parsed and the pl_info
632 # adjusted as necessary (add/remove users from a slice, etc)
634 # @param cred credential string specifying rights of the caller
635 # @param record a record dictionary to be updated
637 def update(self, cred, record_dict):
638 self.decode_authentication(cred, "update")
640 record = GeniRecord(dict = record_dict)
641 type = record.get_type()
643 self.verify_object_permission(record.get_name())
645 auth_name = get_authority(record.get_name())
647 auth_name = record.get_name()
648 table = self.get_auth_table(auth_name)
650 # make sure the record exists
651 existing_record_list = table.resolve(type, record.get_name())
652 if not existing_record_list:
653 raise RecordNotFound(record.get_name())
654 existing_record = existing_record_list[0]
656 # Update_membership needs the membership lists in the existing record
657 # filled in, so it can see if members were added or removed
658 self.fill_record_info(existing_record)
660 # Use the pointer from the existing record, not the one that the user
661 # gave us. This prevents the user from inserting a forged pointer
662 pointer = existing_record.get_pointer()
664 # update the PLC information that was specified with the record
666 if (type == "sa") or (type == "ma"):
667 self.shell.UpdateSite(self.pl_auth, pointer, record.get_pl_info())
669 elif type == "slice":
670 self.shell.UpdateSlice(self.pl_auth, pointer, record.get_pl_info())
673 # SMBAKER: UpdatePerson only allows a limited set of fields to be
674 # updated. Ideally we should have a more generic way of doing
675 # this. I copied the field names from UpdatePerson.py...
677 all_fields = record.get_pl_info()
678 for key in all_fields.keys():
679 if key in ['first_name', 'last_name', 'title', 'email',
680 'password', 'phone', 'url', 'bio', 'accepted_aup',
\r
682 update_fields[key] = all_fields[key]
683 self.shell.UpdatePerson(self.pl_auth, pointer, update_fields)
686 self.shell.UpdateNode(self.pl_auth, pointer, record.get_pl_info())
689 raise UnknownGeniType(type)
691 # update membership for researchers, pis, owners, operators
\r
692 self.update_membership(existing_record, record)
695 # List the records in an authority. The objectGID in the supplied credential
696 # should name the authority that will be listed.
698 # TODO: List doesn't take an hrn and uses the hrn contained in the
699 # objectGid of the credential. Does this mean the only way to list an
700 # authority is by having a credential for that authority?
702 # @param cred credential string specifying rights of the caller
704 # @return list of record dictionaries
705 def list(self, cred, auth_hrn):
706 self.decode_authentication(cred, "list")
708 if not self.hierarchy.auth_exists(auth_hrn):
709 raise MissingAuthority(auth_hrn)
711 table = self.get_auth_table(auth_hrn)
713 records = table.list()
718 # Resolve a record. This is an internal version of the Resolve API call
719 # and returns records in record object format rather than dictionaries
720 # that may be sent over XMLRPC.
722 # @param type type of record to resolve (user | sa | ma | slice | node)
723 # @param name human readable name of object
724 # @param must_exist if True, throw an exception if no records are found
726 # @return a list of record objects, or an empty list []
728 def resolve_raw(self, type, name, must_exist=True):
729 auth_name = get_authority(name)
732 table = self.get_auth_table(auth_name)
733 records = table.resolve(type, name)
734 if (not records) and must_exist:
735 raise RecordNotFound(name)
738 for record in records:
740 self.fill_record_info(record)
741 good_records.append(record)
742 except PlanetLabRecordDoesNotExist:
743 # silently drop the ones that are missing in PL.
744 # is this the right thing to do?
745 print "ignoring geni record ", record.get_name(), "because pl record does not exist"
753 # This is a wrapper around resolve_raw that converts records objects into
754 # dictionaries before returning them to the user.
756 # @param cred credential string authorizing the caller
757 # @param name human readable name to resolve
759 # @return a list of record dictionaries, or an empty list
761 def resolve(self, cred, name):
762 self.decode_authentication(cred, "resolve")
765 records = self.resolve_raw("*", name)
768 for registry in self.registries:
769 if name.startswith(registry):
770 records = self.registries[registry].resolve(self.credential, name)
774 for record in records:
775 dicts.append(record.as_dict())
782 # Retrieve the GID for an object. This function looks up a record in the
783 # registry and returns the GID of the record if it exists.
784 # TODO: Is this function needed? It's a shortcut for Resolve()
786 # @param name hrn to look up
788 # @return the string representation of a GID object
790 def get_gid(self, name):
791 self.verify_object_belongs_to_me(name)
792 records = self.resolve_raw("*", name)
794 for record in records:
795 gid = record.get_gid_object()
796 gid_string_list.append(gid.save_to_string(save_parents=True))
797 return gid_string_list
800 # Determine tje rights that an object should have. The rights are entirely
801 # dependent on the type of the object. For example, users automatically
802 # get "refresh", "resolve", and "info".
804 # @param type the type of the object (user | sa | ma | slice | node)
805 # @param name human readable name of the object (not used at this time)
807 # @return RightList object containing rights
809 def determine_rights(self, type, name):
812 # rights seem to be somewhat redundant with the type of the credential.
813 # For example, a "sa" credential implies the authority right, because
814 # a sa credential cannot be issued to a user who is not an owner of
822 rl.add("authority,sa")
824 rl.add("authority,ma")
825 elif type == "slice":
831 elif type == "component":
837 # GENI API: Get_self_credential
839 # Get_self_credential a degenerate version of get_credential used by a
840 # client to get his initial credential when he doesn't have one. This is
841 # the same as get_credential(..., cred=None,...).
843 # The registry ensures that the client is the principal that is named by
844 # (type, name) by comparing the public key in the record's GID to the
845 # private key used to encrypt the client-side of the HTTPS connection. Thus
846 # it is impossible for one principal to retrieve another principal's
847 # credential without having the appropriate private key.
849 # @param type type of object (user | slice | sa | ma | node
850 # @param name human readable name of object
852 # @return the string representation of a credential object
854 def get_self_credential(self, type, name):
855 self.verify_object_belongs_to_me(name)
857 auth_hrn = get_authority(name)
860 auth_info = self.get_auth_info(auth_hrn)
861 # find a record that matches
862 records = self.resolve_raw(type, name, must_exist=True)
865 gid = record.get_gid_object()
866 peer_cert = self.server.peer_cert
867 if not peer_cert.is_pubkey(gid.get_pubkey()):
868 raise ConnectionKeyGIDMismatch(gid.get_subject())
870 # create the credential
871 gid = record.get_gid_object()
872 cred = Credential(subject = gid.get_subject())
873 cred.set_gid_caller(gid)
874 cred.set_gid_object(gid)
875 cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
876 cred.set_pubkey(gid.get_pubkey())
878 rl = self.determine_rights(type, name)
879 cred.set_privileges(rl)
881 # determine the type of credential that we want to use as a parent for
884 if (type == "ma") or (type == "node"):
885 auth_kind = "authority,ma"
886 else: # user, slice, sa
887 auth_kind = "authority,sa"
889 cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
894 return cred.save_to_string(save_parents=True)
897 # verify_cancreate_credential
899 # Verify that a user can retrieve a particular type of credential. For
900 # slices, the user must be on the researcher list. For SA and MA the user
901 # must be on the pi and operator lists respectively.
903 def verify_cancreate_credential(self, src_cred, record):
904 type = record.get_type()
905 cred_object_hrn = src_cred.get_gid_object().get_hrn()
907 if cred_object_hrn in [config.GENI_REGISTRY_ROOT_AUTH]:
910 researchers = record.get_geni_info().get("researcher", [])
911 if not (cred_object_hrn in researchers):
912 raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name())
914 pis = record.get_geni_info().get("pi", [])
915 if not (cred_object_hrn in pis):
916 raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name())
918 operators = record.get_geni_info().get("operator", [])
919 if not (cred_object_hrn in operators):
920 raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name())
923 # GENI API: Get_credential
925 # Retrieve a credential for an object.
927 # If cred==None, then the behavior reverts to get_self_credential()
929 # @param cred credential object specifying rights of the caller
930 # @param type type of object (user | slice | sa | ma | node)
931 # @param name human readable name of object
933 # @return the string representation of a credental object
935 def get_credential(self, cred, type, name):
937 return self.get_self_credential(type, name)
939 self.decode_authentication(cred, "getcredential")
941 self.verify_object_belongs_to_me(name)
943 auth_hrn = get_authority(name)
946 auth_info = self.get_auth_info(auth_hrn)
948 records = self.resolve_raw(type, name, must_exist=True)
951 # verify_cancreate_credential requires that the member lists
952 # (researchers, pis, etc) be filled in
953 self.fill_record_info(record)
955 self.verify_cancreate_credential(self.client_cred, record)
957 # TODO: Check permission that self.client_cred can access the object
959 object_gid = record.get_gid_object()
960 new_cred = Credential(subject = object_gid.get_subject())
961 new_cred.set_gid_caller(self.client_gid)
962 new_cred.set_gid_object(object_gid)
963 new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
964 new_cred.set_pubkey(object_gid.get_pubkey())
966 rl = self.determine_rights(type, name)
967 new_cred.set_privileges(rl)
969 # determine the type of credential that we want to use as a parent for
972 if (type == "ma") or (type == "node"):
973 auth_kind = "authority,ma"
974 else: # user, slice, sa
975 auth_kind = "authority,sa"
977 new_cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
982 return new_cred.save_to_string(save_parents=True)
985 # GENI API: get_ticket
987 # Retrieve a ticket. This operation is currently implemented on PLC
988 # only (see SFA, engineering decisions); it is not implemented on
991 # The ticket is filled in with information from the PLC database. This
992 # information includes resources, and attributes such as user keys and
995 # @param cred credential string
996 # @param name name of the slice to retrieve a ticket for
997 # @param rspec resource specification dictionary
999 # @return the string representation of a ticket object
1001 def get_ticket(self, cred, name, rspec):
1002 self.decode_authentication(cred, "getticket")
1004 self.verify_object_belongs_to_me(name)
1006 self.verify_object_permission(name)
1008 # XXX much of this code looks like get_credential... are they so similar
1009 # that they should be combined?
1011 auth_hrn = get_authority(name)
1014 auth_info = self.get_auth_info(auth_hrn)
1016 records = self.resolve_raw("slice", name, must_exist=True)
1019 object_gid = record.get_gid_object()
1020 new_ticket = Ticket(subject = object_gid.get_subject())
1021 new_ticket.set_gid_caller(self.client_gid)
1022 new_ticket.set_gid_object(object_gid)
1023 new_ticket.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
1024 new_ticket.set_pubkey(object_gid.get_pubkey())
1026 self.fill_record_info(record)
1028 (attributes, rspec) = self.record_to_slice_info(record)
1030 new_ticket.set_attributes(attributes)
1031 new_ticket.set_rspec(rspec)
1033 new_ticket.set_parent(AuthHierarchy.get_auth_ticket(auth_hrn))
1038 return new_ticket.save_to_string(save_parents=True)
1041 # GENI_API: Create_gid
1043 # Create a new GID. For MAs and SAs that are physically located on the
1044 # registry, this allows a owner/operator/PI to create a new GID and have it
1045 # signed by his respective authority.
1047 # @param cred credential of caller
1048 # @param name hrn for new GID
1049 # @param uuid unique identifier for new GID
1050 # @param pkey_string public-key string (TODO: why is this a string and not a keypair object?)
1052 # @return the string representation of a GID object
1054 def create_gid(self, cred, name, uuid, pubkey_str):
1055 self.decode_authentication(cred, "getcredential")
1057 self.verify_object_belongs_to_me(name)
1059 self.verify_object_permission(name)
1062 uuid = create_uuid()
1065 pkey.load_pubkey_from_string(pubkey_str)
1066 gid = self.hierarchy.create_gid(name, uuid, pkey)
1068 return gid.save_to_string(save_parents=True)