Changeset 6e63513 for fedd/federation


Ignore:
Timestamp:
Nov 23, 2010 6:42:19 PM (14 years ago)
Author:
Ted Faber <faber@…>
Branches:
axis_example, compt_changes, info-ops, master
Children:
25f66c3
Parents:
353db8c
Message:

Checkpoint

Location:
fedd/federation
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • fedd/federation/access.py

    r353db8c r6e63513  
    194194        finally:
    195195            if f: f.close()
     196
     197    def read_abac_access(self, fn, access_obj=None):
     198        """
     199        Read an access DB of the form
     200            abac.attribute -> local_auth_data
     201        The access dict is filled with mappings from the abac attributes (as
     202        strings) to the access objects.  The objects are strings by default,
     203        but the class constructor is called with the string following the ->
     204        and whitespace in the file.
     205        """
     206
     207        map_re = re.compile("(\S+)\s+->\s+(.*)");
     208        if access_obj is None:
     209            access_obj = lambda(x): "%s" % x
     210
     211        f = open(fn, 'r')
     212        try:
     213            lineno = 0
     214            for line in f:
     215                lineno += 1
     216                line = line.strip();
     217                if len(line) == 0 or line.startswith('#'):
     218                    continue
     219                m = map_re.match(line)
     220                if m != None:
     221                    self.access[m.group(1)] = access_obj(m.group(2))
     222                    continue
     223
     224                # Nothing matched to here: unknown line - raise exception
     225                # (finally will close f)
     226                raise self.parse_error(
     227                        "Unknown statement at line %d of %s" % \
     228                        (lineno, fn))
     229        finally:
     230            if f: f.close()
     231
    196232
    197233    def get_users(self, obj):
  • fedd/federation/authorizer.py

    r353db8c r6e63513  
    192192    bad_name = authorizer_base.bad_name
    193193    attribute_error = authorizer_base.attribute_error
    194     class no_file(RuntimeError): pass
    195     class bad_cert(RuntimeError): pass
     194    class no_file_error(RuntimeError): pass
     195    class bad_cert_error(RuntimeError): pass
    196196
    197197    def __init__(self, certs=None, me=None, key=None, load=None, save=None):
     
    203203        # If the me parameter is a combination certificate, split it into the
    204204        # abac_authorizer save directory (if any) for use with creddy.
    205         if abac_pem_type(self.me) == 'both':
     205        if self.me is not None and abac_pem_type(self.me) == 'both':
    206206            if self.save_dir:
    207207                self.key, self.me = abac_split_cert(self.me,
     
    209209                        certfile = "%s/cert.pem" % self.save_dir)
    210210            else:
    211                 raise abac_authorizer.bad_cert("Combination certificate " + \
    212                         "and nowhere to split it");
     211                raise abac_authorizer.bad_cert_error("Combination " + \
     212                        "certificate and nowhere to split it");
    213213        else:
    214214            self.key = key
  • fedd/federation/emulab_access.py

    r353db8c r6e63513  
    2121from access_project import access_project
    2222from fedid import fedid, generate_fedid
    23 from authorizer import authorizer
     23from authorizer import authorizer, abac_authorizer
    2424from service_error import service_error
    2525from remote_service import xmlrpc_handler, soap_handler, service_caller
     
    105105        self.restricted = [ ]
    106106        self.access = { }
    107         if config.has_option("access", "accessdb"):
    108             self.read_access(config.get("access", "accessdb"))
     107        # XXX: this should go?
     108        #if config.has_option("access", "accessdb"):
     109        #    self.read_access(config.get("access", "accessdb"))
    109110        tb = config.get('access', 'testbed')
    110111        if tb: self.testbed = [ t.strip() for t in tb.split(',') ]
    111112        else: self.testbed = [ ]
    112113
    113         if config.has_option("access", "accessdb"):
    114             self.read_access(config.get("access", "accessdb"),
    115                     self.make_access_project)
     114        # authorization information
     115        self.auth_type = config.get('access', 'auth_type') \
     116                or 'legacy'
     117        self.auth_dir = config.get('access', 'auth_dir')
     118        accessdb = config.get("access", "accessdb")
     119        # initialize the authorization system
     120        if self.auth_type == 'legacy':
     121            if accessdb:
     122                self.read_access(accessdb, self.make_access_project)
     123        elif self.auth_type == 'abac':
     124            self.auth = abac_authorizer(load=self.auth_dir)
     125            if accessdb:
     126                self.read_abac_access(accessdb, self.make_abac_access_project)
     127        else:
     128            raise service_error(service_error.internal,
     129                    "Unknown auth_type: %s" % self.auth_type)
    116130
    117131        # read_state in the base_class
     
    124138        self.keys = self.state['keys']
    125139        self.types = self.state['types']
    126         # Add the ownership attributes to the authorizer.  Note that the
    127         # indices of the allocation dict are strings, but the attributes are
    128         # fedids, so there is a conversion.
    129         for k in self.allocation.keys():
    130             for o in self.allocation[k].get('owners', []):
    131                 self.auth.set_attribute(o, fedid(hexstr=k))
    132             if self.allocation[k].has_key('userconfig'):
    133                 sfid = self.allocation[k]['userconfig']
    134                 fid = fedid(hexstr=sfid)
    135                 self.auth.set_attribute(fid, "/%s" % sfid)
     140        if self.auth_type == "legacy":
     141            # Add the ownership attributes to the authorizer.  Note that the
     142            # indices of the allocation dict are strings, but the attributes are
     143            # fedids, so there is a conversion.
     144            for k in self.allocation.keys():
     145                for o in self.allocation[k].get('owners', []):
     146                    self.auth.set_attribute(o, fedid(hexstr=k))
     147                if self.allocation[k].has_key('userconfig'):
     148                    sfid = self.allocation[k]['userconfig']
     149                    fid = fedid(hexstr=sfid)
     150                    self.auth.set_attribute(fid, "/%s" % sfid)
    136151        self.state_lock.release()
    137152        self.exports = {
     
    220235        else:
    221236            raise self.parse_error('Bad mapping (unbalanced parens)')
     237
     238    @staticmethod
     239    def make_abac_access_project(str):
     240        """
     241        Convert a string of the form (id, id) into an access_project.  This is
     242        called by read_abac_access to convert to local attributes.  It returns
     243        a tuple of the form (project, user, user) where the two users are
     244        always the same.
     245        """
     246
     247        str = str.strip()
     248        if str.startswith('(') and str.endswith(')') and str.count(',') == 1:
     249            proj, user = str.split(',')
     250            return ( proj.strip(), user.strip(), user.strip())
     251        else:
     252            raise self.parse_error(
     253                    'Bad mapping (unbalanced parens or more than 1 comma)')
    222254
    223255
     
    296328                [ fid ]
    297329
     330    def lookup_abac_access(self, req, fid):
     331        # Import request credentials into this (clone later??)
     332        if self.auth.import_credentials(data_list=req.get('abac_credential', [])):
     333            self.auth.save()
     334
     335        # Check every attribute that we know how to map and take the first
     336        # success.
     337        for attr in (self.access.keys()):
     338            if self.auth.check_attribute(fid, attr):
     339                # XXX: needs to deal with dynamics
     340                return copy.copy(self.access[attr]), (False, False, False), \
     341                        [ fid ]
     342            else:
     343                self.log.debug("Access failed for %s %s" % (attr, fid))
     344        else:
     345            raise service_error(service_error.access, "Access denied")
     346
     347
    298348    def do_project_allocation(self, dyn, project, user):
    299349        """
     
    433483
    434484
    435         found, dyn, owners = self.lookup_access(req, fid)
     485        if self.auth_type == "legacy":
     486            found, dyn, owners = self.lookup_access(req, fid)
     487        elif self.auth_type == 'abac':
     488            found, dyn, owners = self.lookup_abac_access(req, fid)
     489        else:
     490            raise service_error(service_error.internal,
     491                    'Unknown auth_type: %s' % self.auth_type)
    436492        ap = None
    437493
     
    466522        for o in owners:
    467523            self.auth.set_attribute(o, allocID)
     524        self.auth.save()
    468525        try:
    469526            f = open("%s/%s.pem" % (self.certdir, aid), "w")
  • fedd/federation/experiment_control.py

    r353db8c r6e63513  
    14311431            allocated[tb] = 1
    14321432
     1433    def get_abac_access_to_testbeds(self, testbeds, fid, allocated,
     1434            tbparams, masters, tbmap):
     1435        for tb in testbeds:
     1436            self.get_abac_access(tb, tbparams, fid, masters, tbmap)
     1437            allocated[tb] = 1
     1438
     1439    def get_abac_access(self, tb, tbparams,fid, masters, tbmap):
     1440        """
     1441        Get access to testbed through fedd and set the parameters for that tb
     1442        """
     1443        def get_export_project(svcs):
     1444            """
     1445            Look through for the list of federated_service for this testbed
     1446            objects for a project_export service, and extract the project
     1447            parameter.
     1448            """
     1449
     1450            pe = [s for s in svcs if s.name=='project_export']
     1451            if len(pe) == 1:
     1452                return pe[0].params.get('project', None)
     1453            elif len(pe) == 0:
     1454                return None
     1455            else:
     1456                raise service_error(service_error.req,
     1457                        "More than one project export is not supported")
     1458
     1459        uri = tbmap.get(testbed_base(tb), None)
     1460        if not uri:
     1461            raise service_error(service_error.server_config,
     1462                    "Unknown testbed: %s" % tb)
     1463
     1464        export_svcs = masters.get(tb,[])
     1465        import_svcs = [ s for m in masters.values() \
     1466                for s in m \
     1467                    if tb in s.importers ]
     1468
     1469        export_project = get_export_project(export_svcs)
     1470        # Compose the credential list so that IDs come before attributes
     1471        creds = set()
     1472        keys = set()
     1473        for c in self.auth.get_creds_for_principal(fid):
     1474            keys.add(c.issuer_cert())
     1475            creds.add(c.attribute_cert())
     1476        creds = list(keys) + list(creds)
     1477
     1478        # Request credentials
     1479        req = {
     1480                'abac_credential': creds,
     1481            }
     1482        # Make the service request from the services we're importing and
     1483        # exporting.  Keep track of the export request ids so we can
     1484        # collect the resulting info from the access response.
     1485        e_keys = { }
     1486        if import_svcs or export_svcs:
     1487            req['service'] = [ ]
     1488
     1489            for i, s in enumerate(import_svcs):
     1490                idx = 'import%d' % i
     1491                sr = {'id': idx, 'name': s.name, 'visibility': 'import' }
     1492                if s.params:
     1493                    sr['fedAttr'] = [ { 'attribute': k, 'value': v } \
     1494                            for k, v in s.params.items()]
     1495                req['service'].append(sr)
     1496
     1497            for i, s in enumerate(export_svcs):
     1498                idx = 'export%d' % i
     1499                e_keys[idx] = s
     1500                sr = {'id': idx, 'name': s.name, 'visibility': 'export' }
     1501                if s.params:
     1502                    sr['fedAttr'] = [ { 'attribute': k, 'value': v }
     1503                            for k, v in s.params.items()]
     1504                req['service'].append(sr)
     1505
     1506
     1507        if self.local_access.has_key(uri):
     1508            # Local access call
     1509            req = { 'RequestAccessRequestBody' : req }
     1510            r = self.local_access[uri].RequestAccess(req,
     1511                    fedid(file=self.cert_file))
     1512            r = { 'RequestAccessResponseBody' : r }
     1513        else:
     1514            r = self.call_RequestAccess(uri, req,
     1515                    self.cert_file, self.cert_pwd, self.trusted_certs)
     1516
     1517        tbparam[tb] = {
     1518                "allocID" : r['allocID'],
     1519                "uri": uri,
     1520                }
     1521
     1522        # Collect the responses corresponding to the services this testbed
     1523        # exports.  These will be the service requests that we will include in
     1524        # the start segment requests (with appropriate visibility values) to
     1525        # import and export the segments.
     1526        for s in r.get('service', []):
     1527            id = s.get('id', None)
     1528            if id and id in e_keys:
     1529                e_keys[id].reqs.append(s)
     1530
     1531        # Add attributes to parameter space.  We don't allow attributes to
     1532        # overlay any parameters already installed.
     1533        for a in r.get('fedAttr', []):
     1534            try:
     1535                if a['attribute'] and \
     1536                        isinstance(a['attribute'], basestring)\
     1537                        and not tbparam[tb].has_key(a['attribute'].lower()):
     1538                    tbparam[tb][a['attribute'].lower()] = a['value']
     1539            except KeyError:
     1540                self.log.error("Bad attribute in response: %s" % a)
     1541
     1542
    14331543    def split_topology(self, top, topo, testbeds):
    14341544        """
     
    16371747            raise service_error(service_error.req, "No request?")
    16381748
     1749        # Import information from the requester
     1750        if self.auth.import_credentials(data_list=req.get('credential', [])):
     1751            self.auth.save()
     1752
    16391753        self.check_experiment_access(fid, key)
    16401754
     
    17901904            connInfo = { }          # Connection information
    17911905
    1792             self.get_access_to_testbeds(testbeds, access_user, allocated,
    1793                     tbparams, masters, tbmap)
     1906            if self.auth_type == 'legacy':
     1907                self.get_access_to_testbeds(testbeds, access_user, allocated,
     1908                        tbparams, masters, tbmap)
     1909            elif self.auth_type == 'abac':
     1910                self.get_abac_access_to_testbeds(testbeds, fid, allocated,
     1911                        tbparams, masters, tbmap)
     1912            else:
     1913                raise service_error(service_error.internal,
     1914                        "Unknown auth_type %s" % self.auth_type)
    17941915
    17951916            self.split_topology(top, topo, testbeds)
  • fedd/federation/util.py

    r353db8c r6e63513  
    1010
    1111from socket import sslerror
     12from tempfile import mkstemp
    1213
    1314from M2Crypto import SSL
     
    267268    cert_re = re.compile('\s*-----BEGIN CERTIFICATE-----$')
    268269    type = None
     270    print cert
    269271    f = open(cert, 'r')
    270272    for line in f:
     
    290292        '''
    291293        Wraps up the reqular expression to start and end a diversion, as well as
    292         the open file that gets the lines.
     294        the open file that gets the lines.  If fd is passed in, use that system
     295        file (probably from a mkstemp.  Otherwise open the given filename.
    293296        '''
    294         def __init__(self, start, end, fn):
     297        def __init__(self, start, end, fn=None, fd=None):
    295298            self.start = re.compile(start)
    296299            self.end = re.compile(end)
    297             # Open the file securely with minimal permissions. NB file cannot
    298             # exist before this call.
    299             self.f = os.fdopen(os.open(fn,
    300                 (os.O_WRONLY | os.O_CREAT | os.O_TRUNC | os.O_EXCL), 0600),
    301                 'w')
     300
     301            if not fd:
     302                # Open the file securely with minimal permissions. NB file
     303                # cannot exist before this call.
     304                fd = os.open(fn,
     305                    (os.O_WRONLY | os.O_CREAT | os.O_TRUNC | os.O_EXCL), 0600)
     306
     307            self.f = os.fdopen(fd, 'w')
    302308
    303309        def close(self):
     
    305311
    306312    if not keyfile:
    307         f, keyfile = mkstemp(suffix=".pem")
    308         os.close(f);
     313        kf, rkeyfile = mkstemp(suffix=".pem")
     314    else:
     315        kf, rkeyfile = None, keyfile
     316
    309317    if not certfile:
    310         f, certfile = mkstemp(suffix=".pem")
    311         os.close(f);
     318        cf, rcertfile = mkstemp(suffix=".pem")
     319    else:
     320        cf, rcertfile = None, certfile
    312321
    313322    # Initialize the diversions
    314     divs = [diversion(s, e, fn) for s, e,fn in (
     323    divs = [diversion(s, e, fn=pfn, fd=pfd ) for s, e, pfn, pfd in (
    315324        ('\s*-----BEGIN RSA PRIVATE KEY-----$',
    316325            '\s*-----END RSA PRIVATE KEY-----$',
    317             keyfile),
     326            keyfile, kf),
    318327        ('\s*-----BEGIN CERTIFICATE-----$',
    319328            '\s*-----END CERTIFICATE-----$',
    320             certfile))]
     329            certfile, cf))]
    321330
    322331    # walk through the file, beginning a diversion when a start regexp
     
    340349    # This is probably unnecessary.  Close all the diversion files.
    341350    for d in divs: d.close()
    342     return keyfile, certfile
     351    return rkeyfile, rcertfile
    343352
    344353def find_pickle_problem(o, st=None):
Note: See TracChangeset for help on using the changeset viewer.