X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=geni%2Fregistry.py;h=bcd1e8d3813d132fbdfa9a160b136468d816602a;hb=49a935f8d2ff6f83a48f7f127450f4810bce06f0;hp=53581d439df1a2097b856ccd08ccac2a301b0a55;hpb=3f02043626b0511a3b92406411eb44810e607542;p=sfa.git diff --git a/geni/registry.py b/geni/registry.py index 53581d43..bcd1e8d3 100644 --- a/geni/registry.py +++ b/geni/registry.py @@ -6,20 +6,21 @@ import os import time import sys -from util.credential import Credential -from util.hierarchy import Hierarchy -from util.trustedroot import TrustedRootList -from util.cert import Keypair, Certificate -from util.gid import GID, create_uuid -from util.geniserver import GeniServer -from util.record import GeniRecord -from util.rights import RightList -from util.genitable import GeniTable -from util.geniticket import Ticket -from util.excep import * -from util.misc import * - -from util.config import * +from geni.util.credential import Credential +from geni.util.hierarchy import Hierarchy +from geni.util.trustedroot import TrustedRootList +from geni.util.cert import Keypair, Certificate +from geni.util.gid import GID, create_uuid +from geni.util.geniserver import GeniServer +from geni.util.geniclient import GeniClient +from geni.util.record import GeniRecord +from geni.util.rights import RightList +from geni.util.genitable import GeniTable +from geni.util.geniticket import Ticket +from geni.util.excep import * +from geni.util.misc import * +from geni.util.config import * +from geni.util.storage import * ## # Convert geni fields to PLC fields for use when registering up updating @@ -87,7 +88,7 @@ class Registry(GeniServer): # @param key_file private key filename of registry # @param cert_file certificate filename containing public key (could be a GID file) - def __init__(self, ip, port, key_file, cert_file): + def __init__(self, ip, port, key_file, cert_file, config = '/usr/share/geniwrapper/geni/util/geni_config'): GeniServer.__init__(self, ip, port, key_file, cert_file) # get PL account settings from config module @@ -99,11 +100,27 @@ class Registry(GeniServer): else: self.connect_local_shell() + self.key_file = key_file + self.cert_file = cert_file + self.config = Config(config) + self.basedir = self.config.GENI_BASE_DIR + os.sep + self.server_basedir = self.basedir + os.sep + "geni" + os.sep + self.hrn = self.config.GENI_INTERFACE_HRN + + # get peer registry information + registries_file = self.server_basedir + os.sep + 'registries.xml' + connection_dict = {'hrn': '', 'addr': '', 'port': ''} + self.registry_info = XmlStorage(registries_file, {'registries': {'registry': [connection_dict]}}) + self.registry_info.load() + self.connectRegistry() + self.connectRegistries() + + ## # Connect to a remote shell via XMLRPC def connect_remote_shell(self): - import remoteshell + from geni.util import remoteshell self.shell = remoteshell.RemoteShell() ## @@ -123,12 +140,75 @@ class Registry(GeniServer): self.server.register_function(self.get_self_credential) self.server.register_function(self.get_credential) self.server.register_function(self.get_gid) + self.server.register_function(self.get_ticket) self.server.register_function(self.register) self.server.register_function(self.remove) self.server.register_function(self.update) self.server.register_function(self.list) self.server.register_function(self.resolve) + + def loadCredential(self): + """ + Attempt to load credential from file if it exists. If it doesnt get + credential from registry. + """ + + # see if this file exists + # XX This is really the aggregate's credential. Using this is easier than getting + # the registry's credential from iteslf (ssl errors). + ma_cred_filename = self.server_basedir + os.sep + "agg." + self.hrn + ".ma.cred" + try: + self.credential = Credential(filename = ma_cred_filename) + except IOError: + self.credential = self.getCredentialFromRegistry() + + def getCredentialFromRegistry(self): + """ + Get our current credential from the registry. + """ + # get self credential + self_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".cred" + self_cred = self.registry.get_credential(None, 'ma', self.hrn) + self_cred.save_to_file(self_cred_filename, save_parents=True) + + # get ma credential + ma_cred_filename = self.server_basedir + os.sep + "smgr." + self.hrn + ".sa.cred" + ma_cred = self.registry.get_credential(self_cred, 'sa', self.hrn) + ma_cred.save_to_file(ma_cred_filename, save_parents=True) + return ma_cred + + def connectRegistry(self): + """ + Connect to the registry + """ + # connect to registry using GeniClient + address = self.config.GENI_REGISTRY_HOSTNAME + port = self.config.GENI_REGISTRY_PORT + url = 'http://%(address)s:%(port)s' % locals() + self.registry = GeniClient(url, self.key_file, self.cert_file) + + def connectRegistries(self): + """ + Get connection details for the trusted peer registries from file and + create an GeniClient connection to each. + """ + self.registries= {} + required_fields = ['hrn', 'addr', 'port'] + registries = self.registry_info['registries']['registry'] + if isinstance(registries, dict): + registries = [registries] + if isinstance(registries, list): + for registry in registries: + # create xmlrpc connection using GeniClient + if not set(required_fields).issubset(registry.keys()): + continue + hrn, address, port = registry['hrn'], registry['addr'], registry['port'] + if not hrn or not address or not port: + continue + url = 'http://%(address)s:%(port)s' % locals() + self.registries[hrn] = GeniClient(url, self.key_file, self.cert_file) + ## # Given an authority name, return the information for that authority. This # is basically a stub that calls the hierarchy module. @@ -230,6 +310,12 @@ class Registry(GeniServer): pl_res = self.shell.GetSlices(self.pl_auth, [pointer]) elif (type == "user"): pl_res = self.shell.GetPersons(self.pl_auth, [pointer]) + key_ids = pl_res[0]['key_ids'] + keys = self.shell.GetKeys(self.pl_auth, key_ids) + pubkeys = [] + if keys: + pubkeys = [key['key'] for key in keys] + pl_res[0]['keys'] = pubkeys elif (type == "node"): pl_res = self.shell.GetNodes(self.pl_auth, [pointer]) else: @@ -320,6 +406,62 @@ class Registry(GeniServer): self.fill_record_pl_info(record) self.fill_record_geni_info(record) + def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc): + # get a list of the HRNs tht are members of the old and new records + if oldRecord: + if oldRecord.pl_info == None: + oldRecord.pl_info = {} + oldList = oldRecord.get_geni_info().get(listName, []) + else: + oldList = [] + newList = record.get_geni_info().get(listName, []) + + # if the lists are the same, then we don't have to update anything + if (oldList == newList): + return + + # build a list of the new person ids, by looking up each person to get + # their pointer + newIdList = [] + for hrn in newList: + userRecord = self.resolve_raw("user", hrn)[0] + newIdList.append(userRecord.get_pointer()) + + # build a list of the old person ids from the person_ids field of the + # pl_info + if oldRecord: + oldIdList = oldRecord.pl_info.get("person_ids", []) + containerId = oldRecord.get_pointer() + else: + # if oldRecord==None, then we are doing a Register, instead of an + # update. + oldIdList = [] + containerId = record.get_pointer() + + # add people who are in the new list, but not the oldList + for personId in newIdList: + if not (personId in oldIdList): + print "adding id", personId, "to", record.get_name() + addFunc(self.pl_auth, personId, containerId) + + # remove people who are in the old list, but not the new list + for personId in oldIdList: + if not (personId in newIdList): + print "removing id", personId, "from", record.get_name() + delFunc(self.pl_auth, personId, containerId) + + def update_membership(self, oldRecord, record): + if record.type == "slice": + self.update_membership_list(oldRecord, record, 'researcher', + self.shell.AddPersonToSlice, + self.shell.DeletePersonFromSlice) + elif record.type == "sa": + # TODO + pass + elif record.type == "ma": + # TODO + pass + ## # GENI API: register # @@ -418,6 +560,9 @@ class Registry(GeniServer): table.insert(record) + # update membership for researchers, pis, owners, operators + self.update_membership(None, record) + return record.get_gid_object().save_to_string(save_parents=True) ## @@ -442,7 +587,7 @@ class Registry(GeniServer): record_list = table.resolve(type, hrn) if not record_list: - raise RecordNotFound(name) + raise RecordNotFound(hrn) record = record_list[0] # TODO: sa, ma @@ -474,7 +619,7 @@ class Registry(GeniServer): return True ## - # GENI API: Register + # GENI API: Update # # Update an object in the registry. Currently, this only updates the # PLC information associated with the record. The Geni fields (name, type, @@ -498,14 +643,22 @@ class Registry(GeniServer): self.verify_object_permission(record.get_name()) auth_name = get_authority(record.get_name()) + if not auth_name: + auth_name = record.get_name() table = self.get_auth_table(auth_name) # make sure the record exists existing_record_list = table.resolve(type, record.get_name()) if not existing_record_list: raise RecordNotFound(record.get_name()) - existing_record = existing_record_list[0] + + # Update_membership needs the membership lists in the existing record + # filled in, so it can see if members were added or removed + self.fill_record_info(existing_record) + + # Use the pointer from the existing record, not the one that the user + # gave us. This prevents the user from inserting a forged pointer pointer = existing_record.get_pointer() # update the PLC information that was specified with the record @@ -535,6 +688,9 @@ class Registry(GeniServer): else: raise UnknownGeniType(type) + # update membership for researchers, pis, owners, operators + self.update_membership(existing_record, record) + ## # List the records in an authority. The objectGID in the supplied credential # should name the authority that will be listed. @@ -588,11 +744,10 @@ class Registry(GeniServer): def resolve_raw(self, type, name, must_exist=True): auth_name = get_authority(name) - + if not auth_name: + auth_name = name table = self.get_auth_table(auth_name) - records = table.resolve(type, name) - if (not records) and must_exist: raise RecordNotFound(name) @@ -622,8 +777,16 @@ class Registry(GeniServer): def resolve(self, cred, name): self.decode_authentication(cred, "resolve") + + try: + records = self.resolve_raw("*", name) + except: + records = [] + for registry in self.registries: + if name.startswith(registry): + records = self.registries[registry].resolve(self.credential, name) + - records = self.resolve_raw("*", name) dicts = [] for record in records: dicts.append(record.as_dict()) @@ -646,7 +809,7 @@ class Registry(GeniServer): records = self.resolve_raw("*", name) gid_string_list = [] for record in records: - gid = record.get_gid() + gid = record.get_gid_object() gid_string_list.append(gid.save_to_string(save_parents=True)) return gid_string_list @@ -673,9 +836,9 @@ class Registry(GeniServer): rl.add("resolve") rl.add("info") elif type == "sa": - rl.add("authority") + rl.add("authority,sa") elif type == "ma": - rl.add("authority") + rl.add("authority,ma") elif type == "slice": rl.add("refresh") rl.add("embed") @@ -709,8 +872,9 @@ class Registry(GeniServer): self.verify_object_belongs_to_me(name) auth_hrn = get_authority(name) + if not auth_hrn: + auth_hrn = name auth_info = self.get_auth_info(auth_hrn) - # find a record that matches records = self.resolve_raw(type, name, must_exist=True) record = records[0] @@ -731,13 +895,47 @@ class Registry(GeniServer): rl = self.determine_rights(type, name) cred.set_privileges(rl) - cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn)) + # determine the type of credential that we want to use as a parent for + # this credential. + + if (type == "ma") or (type == "node"): + auth_kind = "authority,ma" + else: # user, slice, sa + auth_kind = "authority,sa" + + cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind)) cred.encode() cred.sign() return cred.save_to_string(save_parents=True) + ## + # verify_cancreate_credential + # + # Verify that a user can retrieve a particular type of credential. For + # slices, the user must be on the researcher list. For SA and MA the user + # must be on the pi and operator lists respectively. + + def verify_cancreate_credential(self, src_cred, record): + type = record.get_type() + cred_object_hrn = src_cred.get_gid_object().get_hrn() + config = Config() + if cred_object_hrn in [config.GENI_REGISTRY_ROOT_AUTH]: + return + if type=="slice": + researchers = record.get_geni_info().get("researcher", []) + if not (cred_object_hrn in researchers): + raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name()) + elif type == "sa": + pis = record.get_geni_info().get("pi", []) + if not (cred_object_hrn in pis): + raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name()) + elif type == "ma": + operators = record.get_geni_info().get("operator", []) + if not (cred_object_hrn in operators): + raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name()) + ## # GENI API: Get_credential # @@ -753,18 +951,26 @@ class Registry(GeniServer): def get_credential(self, cred, type, name): if not cred: - return get_self_credential(self, type, name) + return self.get_self_credential(type, name) self.decode_authentication(cred, "getcredential") self.verify_object_belongs_to_me(name) auth_hrn = get_authority(name) + if not auth_hrn: + auth_hrn = name auth_info = self.get_auth_info(auth_hrn) records = self.resolve_raw(type, name, must_exist=True) record = records[0] + # verify_cancreate_credential requires that the member lists + # (researchers, pis, etc) be filled in + self.fill_record_info(record) + + self.verify_cancreate_credential(self.client_cred, record) + # TODO: Check permission that self.client_cred can access the object object_gid = record.get_gid_object() @@ -777,13 +983,77 @@ class Registry(GeniServer): rl = self.determine_rights(type, name) new_cred.set_privileges(rl) - new_cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn)) + # determine the type of credential that we want to use as a parent for + # this credential. + + if (type == "ma") or (type == "node"): + auth_kind = "authority,ma" + else: # user, slice, sa + auth_kind = "authority,sa" + + new_cred.set_parent(self.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind)) new_cred.encode() new_cred.sign() return new_cred.save_to_string(save_parents=True) + ## + # GENI API: get_ticket + # + # Retrieve a ticket. This operation is currently implemented on PLC + # only (see SFA, engineering decisions); it is not implemented on + # components. + # + # The ticket is filled in with information from the PLC database. This + # information includes resources, and attributes such as user keys and + # initscripts. + # + # @param cred credential string + # @param name name of the slice to retrieve a ticket for + # @param rspec resource specification dictionary + # + # @return the string representation of a ticket object + + def get_ticket(self, cred, name, rspec): + self.decode_authentication(cred, "getticket") + + self.verify_object_belongs_to_me(name) + + self.verify_object_permission(name) + + # XXX much of this code looks like get_credential... are they so similar + # that they should be combined? + + auth_hrn = get_authority(name) + if not auth_hrn: + auth_hrn = name + auth_info = self.get_auth_info(auth_hrn) + + records = self.resolve_raw("slice", name, must_exist=True) + record = records[0] + + object_gid = record.get_gid_object() + new_ticket = Ticket(subject = object_gid.get_subject()) + new_ticket.set_gid_caller(self.client_gid) + new_ticket.set_gid_object(object_gid) + new_ticket.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn) + new_ticket.set_pubkey(object_gid.get_pubkey()) + + self.fill_record_info(record) + + (attributes, rspec) = self.record_to_slice_info(record) + + new_ticket.set_attributes(attributes) + new_ticket.set_rspec(rspec) + + new_ticket.set_parent(AuthHierarchy.get_auth_ticket(auth_hrn)) + + new_ticket.encode() + new_ticket.sign() + + return new_ticket.save_to_string(save_parents=True) + ## # GENI_API: Create_gid #