Changeset 4064742


Ignore:
Timestamp:
Nov 23, 2008 8:20:05 PM (15 years ago)
Author:
Ted Faber <faber@…>
Branches:
axis_example, compt_changes, info-ops, master, version-1.30, version-2.00, version-3.01, version-3.02
Children:
bf0a80e
Parents:
0df6015
Message:

Add access control to the experiment control module.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • fedd/fedd_experiment_control.py

    r0df6015 r4064742  
    178178                        self.trusted_certs = config.get(s, "trusted_certs")
    179179
     180
    180181        self.exp_stem = "fed-stem"
    181182        self.log = logging.getLogger("fedd.experiment_control")
     
    190191        self.ssh_identity_file = None
    191192
     193        # XXX remove if/else?
    192194        if config.has_section("experiment_control"):
    193195            self.debug = config.get("experiment_control", "create_debug")
     
    196198            self.splitter_url = config.get("experiment_control", "splitter_url")
    197199            self.fedkit = config.get("experiment_control", "fedkit")
     200            accessdb_file = config.get("experiment_control", "accessdb")
    198201        else:
    199202            self.debug = False
     
    236239        if self.ssh_pubkey_file:
    237240            try:
    238                 print "reading %s" % self.ssh_pubkey_file
    239241                f = open(self.ssh_pubkey_file, 'r')
    240242                self.ssh_pubkey = f.read()
     
    252254
    253255        set_log_level(config, "experiment_control", self.log)
     256
     257        if auth:
     258            self.auth = auth
     259        else:
     260            self.log.error(\
     261                    "[access]: No authorizer initialized, creating local one.")
     262            auth = authorizer()
     263
     264        if accessdb_file:
     265            try:
     266                self.accessdb = self.read_accessdb(accessdb_file)
     267            except IOError:
     268                raise service_error(service_error.internal,
     269                        "Error opening %s as experiment control accessdb" % \
     270                                accessdb_file)
     271            for fid in self.accessdb.keys():
     272                self.auth.set_attribute(fid, 'create')
     273        else:
     274            raise service_error(service_error.internal,
     275                    "No accessdb specified in config")
    254276
    255277        # Grab saved state.  OK to do this w/o locking because it's read only
     
    360382            self.log.warning(("[read_state]: No saved state: " + \
    361383                    "Unpickling failed: %s") % e)
     384       
     385        for k in self.state.keys():
     386            try:
     387                # This list should only have one element in it, but phrasing it
     388                # as a for loop doesn't cost much, really.  We have to find the
     389                # fedid elements anyway.
     390                for eid in [ f['fedid'] \
     391                        for f in self.state[k]['experimentID']\
     392                            if f.has_key('fedid') ]:
     393                    self.auth.set_attribute(self.state[k]['owner'], eid)
     394            except KeyError, e:
     395                self.log.warning("[read_state]: State ownership or identity " +\
     396                        "misformatted in %s: %s" % (self.state_filename, e))
     397
     398
     399    def read_accessdb(self, accessdb_file):
     400        access = {}
     401        name_expr = "[" + string.ascii_letters + string.digits + "\.\-]+"
     402        project_line = re.compile("^\s*fedid:([" + string.hexdigits + "]+)"+ \
     403                "\s*->\(\s*("+name_expr+")\s*,\s*("+name_expr+")\s*\)\s*$")
     404        user_line = re.compile("^\s*fedid:([" + string.hexdigits + "]+)"+ \
     405                "\s*->\s*(" + name_expr + ")\s*$")
     406        lineno = 0
     407
     408        f = open(accessdb_file, "r")
     409        for line in f:
     410            lineno += 1
     411            line.strip()
     412            if len(line) == 0 or line.startswith('#'):
     413                continue
     414            m = project_line.match(line)
     415            if m:
     416                fid = fedid(hexstr=m.group(1))
     417                project, user = m.group(2,3)
     418                if not access.has_key(fid):
     419                    access[fid] = []
     420                access[fid].append((project, user))
     421                continue
     422
     423            m = user_line.match(line)
     424            if m:
     425                fid = fedid(hexstr=m.group(1))
     426                project = None
     427                user = m.group(2)
     428                if not access.has_key(fid):
     429                    access[fid] = []
     430                access[fid].append((project, user))
     431                continue
     432            raise service_error(service_error.internal,
     433                    "Error parsing access db %s at line %d" % \
     434                        (accessdb_file, lineno))
     435        f.close()
     436        return access
     437
    362438
    363439    def scp_file(self, file, user, host, dest=""):
     
    793869        else: return None
    794870
    795     def get_access(self, tb, nodes, user, tbparam, master, export_project):
     871    def get_access(self, tb, nodes, user, tbparam, master, export_project,
     872            access_user):
    796873        """
    797874        Get access to testbed through fedd and set the parameters for that tb
     
    824901
    825902
    826         # The basic request
    827         req = {\
    828                 'destinationTestbed' : { 'uri' : uri },
    829                 'user':  user,
    830                 'allocID' : { 'localname': 'test' },
    831                 'createAccess' : [ { 'sshPubkey' : self.ssh_pubkey } ],
    832                 'serviceAccess' : service_keys
    833             }
    834 
    835         print "service %s" % service_keys
    836         print "ssh pubkey %s" % self.ssh_pubkey
    837         print "ssh pubkey file %s" % self.ssh_pubkey_file
    838 
    839         if tb == master:
    840             # NB, the export_project parameter is a dict that includes
    841             # the type
    842             req['exportProject'] = export_project
    843 
    844         # node resources if any
    845         if nodes != None and len(nodes) > 0:
    846             rnodes = [ ]
    847             for n in nodes:
    848                 rn = { }
    849                 image, hw, count = n.split(":")
    850                 if image: rn['image'] = [ image ]
    851                 if hw: rn['hardware'] = [ hw ]
    852                 if count: rn['count'] = int(count)
    853                 rnodes.append(rn)
    854             req['resources']= { }
    855             req['resources']['node'] = rnodes
    856 
    857         r = self.call_RequestAccess(uri, req,
    858                 self.cert_file, self.cert_pwd, self.trusted_certs)
    859 
    860         if r.has_key('RequestAccessResponseBody'):
    861             r = r['RequestAccessResponseBody']
    862         else:
    863             raise service_error(service_error.protocol,
    864                     "Bad proxy response")
     903        for p, u in access_user:
     904
     905            if p:
     906                # Request with user and project specified
     907                req = {\
     908                        'destinationTestbed' : { 'uri' : uri },
     909                        'project': {
     910                            'name': {'localname': p},
     911                            'user': [ {'userID': { 'localname': u } } ],
     912                            },
     913                        'user':  user,
     914                        'allocID' : { 'localname': 'test' },
     915                        'createAccess' : [ { 'sshPubkey' : self.ssh_pubkey } ],
     916                        'serviceAccess' : service_keys
     917                    }
     918            else:
     919                # Request with only user specified
     920                req = {\
     921                        'destinationTestbed' : { 'uri' : uri },
     922                        'user':  [ {'userID': { 'localname': u } } ],
     923                        'allocID' : { 'localname': 'test' },
     924                        'createAccess' : [ { 'sshPubkey' : self.ssh_pubkey } ],
     925                        'serviceAccess' : service_keys
     926                    }
     927
     928            if tb == master:
     929                # NB, the export_project parameter is a dict that includes
     930                # the type
     931                req['exportProject'] = export_project
     932
     933            # node resources if any
     934            if nodes != None and len(nodes) > 0:
     935                rnodes = [ ]
     936                for n in nodes:
     937                    rn = { }
     938                    image, hw, count = n.split(":")
     939                    if image: rn['image'] = [ image ]
     940                    if hw: rn['hardware'] = [ hw ]
     941                    if count: rn['count'] = int(count)
     942                    rnodes.append(rn)
     943                req['resources']= { }
     944                req['resources']['node'] = rnodes
     945
     946            try:
     947                r = self.call_RequestAccess(uri, req,
     948                        self.cert_file, self.cert_pwd, self.trusted_certs)
     949            except service_error, e:
     950                if e.code == service_error.access:
     951                    r = None
     952                    continue
     953                else:
     954                    raise e
     955
     956            if r.has_key('RequestAccessResponseBody'):
     957                r = r['RequestAccessResponseBody']
     958            else:
     959                raise service_error(service_error.protocol,
     960                        "Bad proxy response")
     961       
     962        if not r:
     963            raise service_error(service_error.access,
     964                    "Access denied by %s (%s)" % (tb, uri))
    865965
    866966        e = r['emulab']
     
    10311131            self.get_access = get_access
    10321132
    1033         def __call__(self, line, user, tbparams, master, export_project):
     1133        def __call__(self, line, user, tbparams, master, export_project,
     1134                access_user):
    10341135            # Testbed access parameters
    10351136            if not self.in_allbeds:
     
    10461147                    tb = nodes.pop(0)
    10471148                    self.get_access(tb, nodes, user, tbparams, master,
    1048                             export_project)
     1149                            export_project, access_user)
    10491150                return True
    10501151
     
    13551456        to instantiate them and start it all up.
    13561457        """
     1458
     1459        if not self.auth.check_attribute(fid, 'create'):
     1460            raise service_error(service_error.access, "Create access denied")
     1461
    13571462        try:
    13581463            tmpdir = tempfile.mkdtemp(prefix="split-")
     
    13661471        tclfile = tmpdir + "/experiment.tcl"
    13671472        tbparams = { }
     1473        try:
     1474            access_user = self.accessdb[fid]
     1475        except KeyError:
     1476            raise service_error(service_error.internal,
     1477                    "Access map and authorizer out of sync in " + \
     1478                            "create_experiment for fedid %s"  % fid)
    13681479
    13691480        pid = "dummy"
     
    14791590            if parse_current_testbed(line, master, allocated, tbparams):
    14801591                continue
    1481             elif parse_allbeds(line, user, tbparams, master, export_project):
     1592            elif parse_allbeds(line, user, tbparams, master, export_project,
     1593                    access_user):
    14821594                continue
    14831595            elif parse_gateways(line, allocated, tbparams):
     
    16201732                    'vtopo': vtopo,\
    16211733                    'vis' : vis,
     1734                    'owner': fid,
    16221735                    'experimentID' : [\
    16231736                            { 'fedid': expid }, { 'localname': eid },\
     
    16281741        self.state_lock.release()
    16291742
     1743        self.auth.set_attribute(fid, expid)
     1744        self.auth.set_attribute(expid, expid)
     1745
    16301746        if not failed:
    16311747            return resp
     
    16331749            raise service_error(service_error.partial, \
    16341750                    "Partial swap in on %s" % ",".join(succeeded))
     1751
     1752    def check_experiment_access(self, fid, key):
     1753        """
     1754        Confirm that the fid has access to the experiment.  Though a request
     1755        may be made in terms of a local name, the access attribute is always
     1756        the experiment's fedid.
     1757        """
     1758        if not isinstance(key, fedid):
     1759            self.state_lock.acquire()
     1760            if self.state.has_key(key):
     1761                try:
     1762                    kl = [ f['fedid'] for f in self.state[key]['experimentID']\
     1763                            if f.has_key('fedid') ]
     1764                except KeyError:
     1765                    self.state_lock.release()
     1766                    raise service_error(service_error.internal,
     1767                            "No fedid for experiment %s when checking " +\
     1768                                    "access(!?)" % key)
     1769                if len(kl) == 1:
     1770                    key = kl[0]
     1771                else:
     1772                    self.state_lock.release()
     1773                    raise service_error(service_error.internal,
     1774                            "multiple fedids for experiment %s when " +\
     1775                                    "checking access(!?)" % key)
     1776            else:
     1777                self.state_lock.release()
     1778                raise service_error(service_error.access, "Access Denied")
     1779            self.state_lock.release()
     1780
     1781        if self.auth.check_attribute(fid, key):
     1782            return True
     1783        else:
     1784            raise service_error(service_error.access, "Access Denied")
     1785
    16351786
    16361787
     
    16581809            raise service_error(service_error.req, "No request?")
    16591810
     1811        self.check_experiment_access(fid, key)
     1812
    16601813        self.state_lock.acquire()
    16611814        if self.state.has_key(key):
     
    16911844            raise service_error(service_error.req, "No request?")
    16921845
     1846        self.check_experiment_access(fid, key)
     1847
    16931848        self.state_lock.acquire()
    16941849        if self.state.has_key(key):
     
    17231878        else:
    17241879            raise service_error(service_error.req, "No request?")
     1880
     1881        self.check_experiment_access(fid, key)
    17251882
    17261883        # The state may be massaged by the service function that called
     
    17581915        else:
    17591916            raise service_error(service_error.req, "No request?")
     1917
     1918        self.check_experiment_access(fid, key)
    17601919
    17611920        self.state_lock.acquire()
Note: See TracChangeset for help on using the changeset viewer.