source: fedd/federation/access.py @ c573278

axis_examplecompt_changesinfo-ops
Last change on this file since c573278 was 6e63513, checked in by Ted Faber <faber@…>, 14 years ago

Checkpoint

  • Property mode set to 100644
File size: 24.3 KB
RevLine 
[f771e2f]1#!/usr/local/bin/python
2
3import os,sys
4import stat # for chmod constants
5import re
6import random
7import string
8import copy
9import pickle
10import logging
11import subprocess
12
13from threading import *
14from M2Crypto.SSL import SSLError
15
16from util import *
17from allocate_project import allocate_project_local, allocate_project_remote
18from access_project import access_project
19from fedid import fedid, generate_fedid
20from authorizer import authorizer
21from service_error import service_error
22from remote_service import xmlrpc_handler, soap_handler, service_caller
23
24import httplib
25import tempfile
26from urlparse import urlparse
27
28import topdl
29import list_log
30import proxy_emulab_segment
31import local_emulab_segment
32
33
34# Make log messages disappear if noone configures a fedd logger
35class nullHandler(logging.Handler):
36    def emit(self, record): pass
37
38fl = logging.getLogger("fedd.access")
39fl.addHandler(nullHandler())
40
41class access_base:
42    """
43    The implementation of access control based on mapping users to projects.
44
45    Users can be mapped to existing projects or have projects created
46    dynamically.  This implements both direct requests and proxies.
47    """
48
49    class parse_error(RuntimeError): pass
50
51    def __init__(self, config=None, auth=None):
52        """
53        Initializer.  Pulls parameters out of the ConfigParser's access section.
54        """
55
56        # Make sure that the configuration is in place
57        if not config: 
58            raise RunTimeError("No config to fedd.access")
59
60        self.project_priority = config.getboolean("access", "project_priority")
61
62        self.certdir = config.get("access","certdir")
63        self.create_debug = config.getboolean("access", "create_debug")
64        self.cleanup = not config.getboolean("access", "leave_tmpfiles")
65        self.access_type = config.get("access", "type")
66        self.log = logging.getLogger("fedd.access")
67        set_log_level(config, "access", self.log)
68        self.state_lock = Lock()
69        self.state = { }
[623a2c9]70        # subclasses fill with what and how they export.
71        self.exports = { }
[f771e2f]72        # XXX: Configurable
73        self.imports = set(('SMB', 'seer', 'userconfig', 'seer_master',
74            'hide_hosts'))
75
76        if auth: self.auth = auth
77        else:
78            self.log.error(\
79                    "[access]: No authorizer initialized, creating local one.")
80            auth = authorizer()
81
82        self.state_filename = config.get("access", "access_state")
83        self.read_state()
84
85        # Keep cert_file and cert_pwd coming from the same place
86        self.cert_file = config.get("access", "cert_file")
87        if self.cert_file:
[1027cf7]88            self.cert_pwd = config.get("access", "cert_pw")
[f771e2f]89        else:
90            self.cert_file = config.get("globals", "cert_file")
91            self.sert_pwd = config.get("globals", "cert_pw")
92
93        self.trusted_certs = config.get("access", "trusted_certs") or \
94                config.get("globals", "trusted_certs")
95
96
97    @staticmethod
98    def software_list(v):
99        """
100        From a string containing a sequence of space separated pairs, return a
101        list of tuples with pairs of location and file.
102        """
103        l = [ ]
104        if v:
105            ps = v.split(" ")
106            while len(ps):
107                loc, file = ps[0:2]
108                del ps[0:2]
109                l.append((loc, file))
110        return l
111
112    @staticmethod
113    def add_kit(e, kit):
114        """
115        Add a Software object created from the list of (install, location)
116        tuples passed as kit  to the software attribute of an object e.  We
117        do this enough to break out the code, but it's kind of a hack to
118        avoid changing the old tuple rep.
119        """
120
121        s = [ topdl.Software(install=i, location=l) for i, l in kit]
122
123        if isinstance(e.software, list): e.software.extend(s)
124        else: e.software = s
125
126
127    def read_access(self, config, access_obj=None):
128        """
129        Read an access DB with filename config  of the form:
[2f6820c]130            (id, id, id) -> attribute, something
131        where the ids can be fedids, strings, or <any> or <none>, attribute is
132        the attribute to assign , and something is any set of charcters.  The
133        hash self.access is populated with mappings from those triples to the
134        results of access_obj being called on the remainder of the line (if
135        present).  If access_obj is not given, the string itself is entered in
136        the hash.  Additionally, a triple with <any> and <none> mapped to None
137        is entered in self.auth with the attribute given.
[f771e2f]138
139        Parsing errors result in a self.parse_error exception being raised.
140        access_obj should throw that as well.
141        """
142        lineno=0
143        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
144        fedid_expr = "fedid:[" + string.hexdigits + "]+"
145        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
146        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
[2f6820c]147                key_name+'\s*\)\s*->\s*([^,]+)\s*(.*)', re.IGNORECASE)
[f771e2f]148
149        def parse_name(n):
150            if n.startswith('fedid:'): return fedid(hexstr=n[len('fedid:'):])
151            else: return n
152       
153        def auth_name(n):
154            if isinstance(n, basestring):
155                if n =='<any>' or n =='<none>': return None
156                else: return unicode(n)
157            else:
158                return n
[2f6820c]159        def strip_comma(s):
160            s = s.strip()
161            if s.startswith(','):
162                s = s[1:].strip()
163            return s
[f771e2f]164
165        if access_obj is None:
166            access_obj = lambda(x): "%s" % x
167
168        f = open(config, "r");
169        try:
170            for line in f:
171                lineno += 1
172                line = line.strip();
173                if len(line) == 0 or line.startswith('#'):
174                    continue
175
176                # Access line (t, p, u) -> anything
177                m = access_re.match(line)
178                if m != None:
179                    access_key = tuple([ parse_name(x) \
180                            for x in m.group(1,2,3)])
[2f6820c]181                    attribute = m.group(4)
[f771e2f]182                    auth_key = tuple([ auth_name(x) for x in access_key])
[2f6820c]183                    self.auth.set_attribute(auth_key, attribute)
184                    if len(m.group(5)) > 0:
185                        access_val = access_obj(strip_comma(m.group(5)))
186                        self.access[access_key] = access_val
[f771e2f]187                    continue
188
189                # Nothing matched to here: unknown line - raise exception
190                f.close()
191                raise self.parse_error(
192                        "Unknown statement at line %d of %s" % \
193                        (lineno, config))
194        finally:
195            if f: f.close()
196
[6e63513]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
232
[f771e2f]233    def get_users(self, obj):
234        """
235        Return a list of the IDs of the users in dict
236        """
237        if obj.has_key('user'):
238            return [ unpack_id(u['userID']) \
239                    for u in obj['user'] if u.has_key('userID') ]
240        else:
241            return None
242
243    def write_state(self):
244        if self.state_filename:
245            try:
246                f = open(self.state_filename, 'w')
247                pickle.dump(self.state, f)
[06cc65b]248                self.log.debug("Wrote state to %s" % self.state_filename)
[f771e2f]249            except EnvironmentError, e:
250                self.log.error("Can't write file %s: %s" % \
251                        (self.state_filename, e))
252            except pickle.PicklingError, e:
253                self.log.error("Pickling problem: %s" % e)
254            except TypeError, e:
255                self.log.error("Pickling problem (TypeError): %s" % e)
256
257
258    def read_state(self):
259        """
260        Read a new copy of access state.  Old state is overwritten.
261
262        State format is a simple pickling of the state dictionary.
263        """
264        if self.state_filename:
265            try:
266                f = open(self.state_filename, "r")
267                self.state = pickle.load(f)
[06cc65b]268                self.log.debug("[read_state]: Read state from %s" % \
269                        self.state_filename)
[f771e2f]270            except EnvironmentError, e:
271                self.log.warning(("[read_state]: No saved state: " +\
272                        "Can't open %s: %s") % (self.state_filename, e))
273            except EOFError, e:
274                self.log.warning(("[read_state]: " +\
275                        "Empty or damaged state file: %s:") % \
276                        self.state_filename)
277            except pickle.UnpicklingError, e:
278                self.log.warning(("[read_state]: No saved state: " + \
279                        "Unpickling failed: %s") % e)
280
281
282
283    def permute_wildcards(self, a, p):
284        """Return a copy of a with various fields wildcarded.
285
286        The bits of p control the wildcards.  A set bit is a wildcard
287        replacement with the lowest bit being user then project then testbed.
288        """
289        if p & 1: user = ["<any>"]
290        else: user = a[2]
291        if p & 2: proj = "<any>"
292        else: proj = a[1]
293        if p & 4: tb = "<any>"
294        else: tb = a[0]
295
296        return (tb, proj, user)
297
298    def find_access(self, search):
299        """
300        Search the access DB for a match on this tuple.  Return the matching
301        access tuple and the user that matched.
302       
303        NB, if the initial tuple fails to match we start inserting wildcards in
304        an order determined by self.project_priority.  Try the list of users in
305        order (when wildcarded, there's only one user in the list).
306        """
307        if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7)
308        else: perm = (0, 2, 1, 3, 4, 6, 5, 7)
309
310        for p in perm: 
311            s = self.permute_wildcards(search, p)
312            # s[2] is None on an anonymous, unwildcarded request
313            if s[2] != None:
314                for u in s[2]:
315                    if self.access.has_key((s[0], s[1], u)):
316                        return (self.access[(s[0], s[1], u)], u)
317            else:
318                if self.access.has_key(s):
319                    return (self.access[s], None)
320        return None, None
321
322    def lookup_access_base(self, req, fid):
323        """
324        Determine the allowed access for this request.  Return the access and
325        which fields are dynamic.
326
327        The fedid is needed to construct the request
328        """
329        user_re = re.compile("user:\s(.*)")
330        project_re = re.compile("project:\s(.*)")
331
332        # Search keys
333        tb = fid
334        user = [ user_re.findall(x)[0] for x in req.get('credential', []) \
335                if user_re.match(x)]
336        project = [ project_re.findall(x)[0] \
337                for x in req.get('credential', []) \
338                    if project_re.match(x)]
339
340        if len(project) == 1: project = project[0]
341        elif len(project) == 0: project = None
342        else: 
343            raise service_error(service_error.req, 
344                    "More than one project credential")
345
346        # Confirm authorization
347        for u in user:
348            self.log.debug("[lookup_access] Checking access for %s" % \
349                    ((tb, project, u),))
350            if self.auth.check_attribute((tb, project, u), 'access'):
351                self.log.debug("[lookup_access] Access granted")
352                break
353            else:
354                self.log.debug("[lookup_access] Access Denied")
355        else:
356            raise service_error(service_error.access, "Access denied")
357
358        # This maps a valid user to the Emulab projects and users to use
359        found, user_match = self.find_access((tb, project, user))
360
361        return (found, (tb, project, user_match))
362       
363
364    def get_handler(self, path, fid):
365        self.log.info("Get handler %s %s" % (path, fid))
366        if self.auth.check_attribute(fid, path) and self.userconfdir:
367            return ("%s/%s" % (self.userconfdir, path), "application/binary")
368        else:
369            return (None, None)
370
371    def export_userconf(self, project):
372        dev_null = None
373        confid, confcert = generate_fedid("test", dir=self.userconfdir, 
374                log=self.log)
375        conffilename = "%s/%s" % (self.userconfdir, str(confid))
376        cf = None
377        try:
378            cf = open(conffilename, "w")
379            os.chmod(conffilename, stat.S_IRUSR | stat.S_IWUSR)
380        except EnvironmentError, e:
381            raise service_error(service_error.internal, 
382                    "Cannot create user configuration data")
383
384        try:
385            dev_null = open("/dev/null", "a")
386        except EnvironmentError, e:
387            self.log.error("export_userconf: can't open /dev/null: %s" % e)
388
389        cmd = "%s %s" % (self.userconfcmd, project)
390        conf = subprocess.call(cmd.split(" "),
391                stdout=cf, stderr=dev_null, close_fds=True)
392
393        self.auth.set_attribute(confid, "/%s" % str(confid))
394
395        return confid, confcert
396
397    def export_SMB(self, id, state, project, user, attrs):
[623a2c9]398        if project and user:
399            return [{ 
400                    'id': id,
401                    'name': 'SMB',
402                    'visibility': 'export',
403                    'server': 'http://fs:139',
404                    'fedAttr': [
405                            { 'attribute': 'SMBSHARE', 'value': 'USERS' },
406                            { 'attribute': 'SMBUSER', 'value': user },
407                            { 'attribute': 'SMBPROJ', 'value': project },
408                        ]
409                    }]
410        else:
411            self.log.warn("Cannot export SMB w/o user and project")
412            return [ ]
[f771e2f]413
414    def export_seer(self, id, state, project, user, attrs):
[623a2c9]415        return [{ 
[f771e2f]416                'id': id,
417                'name': 'seer',
418                'visibility': 'export',
419                'server': 'http://control:16606',
[623a2c9]420                }]
[f771e2f]421
422    def export_local_seer(self, id, state, project, user, attrs):
[623a2c9]423        return [{ 
[f771e2f]424                'id': id,
425                'name': 'local_seer_control',
426                'visibility': 'export',
427                'server': 'http://control:16606',
[623a2c9]428                }]
[f771e2f]429
430    def export_seer_master(self, id, state, project, user, attrs):
[623a2c9]431        return [{ 
[f771e2f]432                'id': id,
433                'name': 'seer_master',
434                'visibility': 'export',
435                'server': 'http://seer-master:17707',
[623a2c9]436                }]
[f771e2f]437
438    def export_tmcd(self, id, state, project, user, attrs):
[623a2c9]439        return [{ 
[f771e2f]440                'id': id,
441                'name': 'seer',
442                'visibility': 'export',
443                'server': 'http://boss:7777',
[623a2c9]444                }]
[f771e2f]445
446    def export_userconfig(self, id, state, project, user, attrs):
447        if self.userconfdir and self.userconfcmd \
448                and self.userconfurl:
449            cid, cert = self.export_userconf(project)
450            state['userconfig'] = unicode(cid)
[623a2c9]451            return [{
[f771e2f]452                    'id': id,
453                    'name': 'userconfig',
454                    'visibility': 'export',
455                    'server': "%s/%s" % (self.userconfurl, str(cid)),
456                    'fedAttr': [
457                        { 'attribute': 'cert', 'value': cert },
458                    ]
[623a2c9]459                    }]
[f771e2f]460        else:
[623a2c9]461            return [ ]
[f771e2f]462
463    def export_hide_hosts(self, id, state, project, user, attrs):
[623a2c9]464        return [{
[f771e2f]465                'id': id, 
466                'name': 'hide_hosts',
467                'visibility': 'export',
468                'fedAttr': [ x for x in attrs \
469                        if x.get('attribute', "") == 'hosts'],
[623a2c9]470                }]
[f771e2f]471
[623a2c9]472    def export_project_export(self, id, state, project, user, attrs):
473        rv = [ ]
474        rv.extend(self.export_SMB(id, state, project, user, attrs))
475        rv.extend(self.export_userconfig(id, state, project, user, attrs))
476        return rv
477
478    def export_services(self, sreq, project=None, user=None):
[f771e2f]479        exp = [ ]
480        state = { }
481        for s in sreq:
482            sname = s.get('name', '')
483            svis = s.get('visibility', '')
484            sattrs = s.get('fedAttr', [])
485            if svis == 'export':
486                if sname in self.exports:
487                    id = s.get('id', 'no_id')
[623a2c9]488                    exp.extend(self.exports[sname](id, state, project, user,
[f771e2f]489                            sattrs))
[623a2c9]490
[f771e2f]491        return (exp, state)
492
493    def build_access_response(self, alloc_id, ap, services):
494        """
495        Create the SOAP response.
496
497        Build the dictionary description of the response and use
498        fedd_utils.pack_soap to create the soap message.  ap is the allocate
499        project message returned from a remote project allocation (even if that
500        allocation was done locally).
501        """
502        # Because alloc_id is already a fedd_services_types.IDType_Holder,
503        # there's no need to repack it
504        msg = { 
505                'allocID': alloc_id,
506                'fedAttr': [
507                    { 'attribute': 'domain', 'value': self.domain } , 
508                    { 'attribute': 'project', 'value': 
509                        ap['project'].get('name', {}).get('localname', "???") },
510                ]
511            }
512
513        if self.dragon_endpoint:
514            msg['fedAttr'].append({'attribute': 'dragon',
515                'value': self.dragon_endpoint})
516        if self.deter_internal:
517            msg['fedAttr'].append({'attribute': 'deter_internal',
518                'value': self.deter_internal})
519        #XXX: ??
520        if self.dragon_vlans:
521            msg['fedAttr'].append({'attribute': 'vlans',
522                'value': self.dragon_vlans})
523
524        if services:
525            msg['service'] = services
526        return msg
527
528    def generate_portal_configs(self, topo, pubkey_base, secretkey_base, 
[d6990a4]529            tmpdir, lproj, leid, connInfo, services):
[f771e2f]530
531        def conninfo_to_dict(key, info):
532            """
533            Make a cpoy of the connection information about key, and flatten it
534            into a single dict by parsing out any feddAttrs.
535            """
536
537            rv = None
538            for i in info:
539                if key == i.get('portal', "") or \
540                        key in [e.get('element', "") \
541                        for e in i.get('member', [])]:
542                    rv = i.copy()
543                    break
544
545            else:
546                return rv
547
548            if 'fedAttr' in rv:
549                for a in rv['fedAttr']:
550                    attr = a.get('attribute', "")
551                    val = a.get('value', "")
552                    if attr and attr not in rv:
553                        rv[attr] = val
554                del rv['fedAttr']
555            return rv
556
557        # XXX: un hardcode this
558        def client_null(f, s):
559            print >>f, "Service: %s" % s['name']
560
561        def client_seer_master(f, s):
[703859f]562            print >>f, 'PortalAlias: seer-master'
[f771e2f]563
564        def client_smb(f, s):
565            print >>f, "Service: %s" % s['name']
566            smbshare = None
567            smbuser = None
568            smbproj = None
569            for a in s.get('fedAttr', []):
570                if a.get('attribute', '') == 'SMBSHARE':
571                    smbshare = a.get('value', None)
572                elif a.get('attribute', '') == 'SMBUSER':
573                    smbuser = a.get('value', None)
574                elif a.get('attribute', '') == 'SMBPROJ':
575                    smbproj = a.get('value', None)
576
577            if all((smbshare, smbuser, smbproj)):
578                print >>f, "SMBshare: %s" % smbshare
579                print >>f, "ProjectUser: %s" % smbuser
580                print >>f, "ProjectName: %s" % smbproj
581
582        def client_hide_hosts(f, s):
583            for a in s.get('fedAttr', [ ]):
584                if a.get('attribute', "") == 'hosts':
585                    print >>f, "Hide: %s" % a.get('value', "")
586
587        client_service_out = {
588                'SMB': client_smb,
589                'tmcd': client_null,
590                'seer': client_null,
591                'userconfig': client_null,
592                'project_export': client_null,
593                'seer_master': client_seer_master,
594                'hide_hosts': client_hide_hosts,
595            }
596
597        def client_seer_master_export(f, s):
598            print >>f, "AddedNode: seer-master"
599
600        def client_seer_local_export(f, s):
601            print >>f, "AddedNode: control"
602
603        client_export_service_out = {
604                'seer_master': client_seer_master_export,
605                'local_seer_control': client_seer_local_export,
606            }
607
608        def server_port(f, s):
609            p = urlparse(s.get('server', 'http://localhost'))
610            print >>f, 'port: remote:%s:%s:%s' % (p.port, p.hostname, p.port) 
611
612        def server_null(f,s): pass
613
614        def server_seer(f, s):
615            print >>f, 'seer: True'
616
617        server_service_out = {
618                'SMB': server_port,
619                'tmcd': server_port,
620                'userconfig': server_null,
621                'project_export': server_null,
622                'seer': server_seer,
623                'seer_master': server_port,
624                'hide_hosts': server_null,
625            }
626        # XXX: end un hardcode this
627
628
629        seer_out = False
630        client_out = False
631        mproj = None
632        mexp = None
633        control_gw = None
634        testbed = ""
635        # Create configuration files for the portals
636        for e in [ e for e in topo.elements \
637                if isinstance(e, topdl.Computer) and e.get_attribute('portal')]:
638            myname = e.name
639            type = e.get_attribute('portal_type')
640
641            info = conninfo_to_dict(myname, connInfo)
642
643            if not info:
644                raise service_error(service_error.req,
645                        "No connectivity info for %s" % myname)
646
647            peer = info.get('peer', "")
[703859f]648            ldomain = self.domain
[f771e2f]649            ssh_port = info.get('ssh_port', 22)
650
651            # Collect this for the client.conf file
652            if 'masterexperiment' in info:
653                mproj, meid = info['masterexperiment'].split("/", 1)
654
655            if type in ('control', 'both'):
656                testbed = e.get_attribute('testbed')
657                control_gw = myname
658
659            active = info.get('active', 'False')
660
661            cfn = "%s/%s.gw.conf" % (tmpdir, myname.lower())
662            tunnelconfig = self.tunnel_config
663            try:
664                f = open(cfn, "w")
665                if active == 'True':
666                    print >>f, "active: True"
667                    print >>f, "ssh_port: %s" % ssh_port
668                    if type in ('control', 'both'):
669                        for s in [s for s in services \
670                                if s.get('name', "") in self.imports]:
671                            server_service_out[s['name']](f, s)
672
673                if tunnelconfig:
674                    print >>f, "tunnelip: %s" % tunnelconfig
675                print >>f, "peer: %s" % peer.lower()
676                print >>f, "ssh_pubkey: /proj/%s/exp/%s/tmp/%s" % \
677                        (lproj, leid, pubkey_base)
678                print >>f, "ssh_privkey: /proj/%s/exp/%s/tmp/%s" % \
679                        (lproj, leid, secretkey_base)
680                f.close()
681            except EnvironmentError, e:
682                raise service_error(service_error.internal,
683                        "Can't write protal config %s: %s" % (cfn, e))
684
685        # Done with portals, write the client config file.
686        try:
687            f = open("%s/client.conf" % tmpdir, "w")
688            if control_gw:
689                print >>f, "ControlGateway: %s.%s.%s%s" % \
690                    (myname.lower(), leid.lower(), lproj.lower(),
691                            ldomain.lower())
692            for s in services:
693                if s.get('name',"") in self.imports and \
694                        s.get('visibility','') == 'import':
695                    client_service_out[s['name']](f, s)
696                if s.get('name', '') in self.exports and \
697                        s.get('visibility', '') == 'export' and \
698                        s['name'] in client_export_service_out:
699                    client_export_service_out[s['name']](f, s)
700            # Seer uses this.
701            if mproj and meid:
702                print >>f, "ExperimentID: %s/%s" % (mproj, meid)
703            f.close()
704        except EnvironmentError, e:
705            raise service_error(service_error.internal,
706                    "Cannot write client.conf: %s" %s)
707
[623a2c9]708    def configure_userconf(self, services, tmpdir):
709        """
710        If the userconf service was imported, collect the configuration data.
711        """
712        for s in services:
713            s_name = s.get('name', '')
714            s_vis = s.get('visibility','')
715            if s_name  == 'userconfig' and s_vis == 'import':
716                # Collect ther server and certificate info.
717                u = s.get('server', None)
718                for a in s.get('fedAttr', []):
719                    if a.get('attribute',"") == 'cert':
720                        cert = a.get('value', None)
721                        break
722                else:
723                    cert = None
724
725                if cert:
726                    # Make a temporary certificate file for get_url.  The
727                    # finally clause removes it whether something goes
728                    # wrong (including an exception from get_url) or not.
729                    try:
730                        tfos, tn = tempfile.mkstemp(suffix=".pem")
731                        tf = os.fdopen(tfos, 'w')
732                        print >>tf, cert
733                        tf.close()
734                        self.log.debug("Getting userconf info: %s" % u)
735                        get_url(u, tn, tmpdir, "userconf")
736                        self.log.debug("Got userconf info: %s" % u)
737                    except EnvironmentError, e:
738                        raise service_error(service.error.internal, 
739                                "Cannot create temp file for " + 
740                                "userconfig certificates: %s" % e)
741                    except:
742                        t, v, st = sys.exc_info()
743                        raise service_error(service_error.internal,
744                                "Error retrieving %s: %s" % (u, v))
745                    finally:
746                        if tn: os.remove(tn)
747                else:
748                    raise service_error(service_error.req,
749                            "No certificate for retreiving userconfig")
750                break
751
[f771e2f]752    def import_store_info(self, cf, connInfo):
753        """
754        Pull any import parameters in connInfo in.  We translate them either
755        into known member names or fedAddrs.
756        """
757
758        for c in connInfo:
759            for p in [ p for p in c.get('parameter', []) \
760                    if p.get('type', '') == 'input']:
761                name = p.get('name', None)
762                key = p.get('key', None)
763                store = p.get('store', None)
764
765                if name and key and store :
766                    req = { 'name': key, 'wait': True }
767                    self.log.debug("Waiting for %s (%s) from %s" % \
768                            (name, key, store))
769                    r = self.call_GetValue(store, req, cf)
770                    r = r.get('GetValueResponseBody', None)
771                    if r :
772                        if r.get('name', '') == key:
773                            v = r.get('value', None)
774                            if v is not None:
775                                if name == 'peer':
776                                    self.log.debug("Got peer %s" % v)
777                                    c['peer'] = v
778                                else:
779                                    self.log.debug("Got %s %s" % (name, v))
780                                    if c.has_key('fedAttr'):
781                                        c['fedAttr'].append({
782                                            'attribute': name, 'value': v})
783                                    else:
784                                        c['fedAttr']= [{
785                                            'attribute': name, 'value': v}]
786                            else:
787                                raise service_error(service_error.internal, 
788                                        'None value exported for %s'  % key)
789                        else:
790                            raise service_error(service_error.internal, 
791                                    'Different name returned for %s: %s' \
792                                            % (key, r.get('name','')))
793                    else:
794                        raise service_error(service_error.internal, 
795                            'Badly formatted response: no GetValueResponseBody')
796                else:
797                    raise service_error(service_error.internal, 
798                        'Bad Services missing info for import %s' % c)
[623a2c9]799
800    def remove_dirs(self, dir):
801        """
802        Remove the directory tree and all files rooted at dir.  Log any errors,
803        but continue.
804        """
805        self.log.debug("[removedirs]: removing %s" % dir)
806        try:
807            for path, dirs, files in os.walk(dir, topdown=False):
808                for f in files:
809                    os.remove(os.path.join(path, f))
810                for d in dirs:
811                    os.rmdir(os.path.join(path, d))
812            os.rmdir(dir)
813        except EnvironmentError, e:
814            self.log.error("Error deleting directory tree in %s" % e);
Note: See TracBrowser for help on using the repository browser.