source: fedd/fedd_proj.py @ 4a6f04b

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since 4a6f04b was 4a6f04b, checked in by Ted Faber <faber@…>, 16 years ago

Don't poison the access database by assigning to objects in the DB (\!)

  • Property mode set to 100644
File size: 21.7 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4
5from BaseHTTPServer import BaseHTTPRequestHandler
6from ZSI import *
7from M2Crypto import SSL
8from M2Crypto.m2xmlrpclib import SSL_Transport
9from M2Crypto.SSL.SSLServer import SSLServer
10import M2Crypto.httpslib
11import xmlrpclib
12
13import re
14import string
15import subprocess
16import tempfile
17import copy
18
19from fedd_services import *
20from fedd_util import *
21from fedd_allocate_project import *
22import parse_detail
23from service_error import *
24
25class fedd_proj:
26    """
27    The implementation of access control based on mapping users to projects.
28
29    Users can be mapped to existing projects or have projects created
30    dynamically.  This implements both direct requests and proxies.
31    """
32    # Attributes that can be parsed from the configuration file
33    bool_attrs = ("dynamic_projects", "project_priority")
34    emulab_attrs = ("boss", "ops", "domain", "fileserver", "eventserver")
35    id_attrs = ("testbed", "cert_file", "cert_pwd", "trusted_certs", "proxy",
36            "proxy_cert_file", "proxy_cert_pwd", "proxy_trusted_certs",
37            "dynamic_projects_url", "dynamic_projects_cert_file", 
38            "dynamic_projects_cert_pwd", "dynamic_projects_trusted_certs")
39
40    # Used by the SOAP caller
41    soap_namespaces = ('http://www.isi.edu/faber/fedd.wsdl',
42            'http://www.isi.edu/faber/fedd_internal.wsdl')
43    soap_methods = { 'RequestAccess': 'soap_RequestAccess' }
44    xmlrpc_methods = { 'RequestAccess': 'xmlrpc_RequestAccess' }
45
46    class access_project:
47        """
48        A project description used to grant access to this testbed.
49
50        The description includes a name and a list of node types to which the
51        project will be granted access.
52        """
53        def __init__(self, name, nt):
54            self.name = name
55            self.node_types = list(nt)
56
57        def __repr__(self):
58            if len(self.node_types) > 0:
59                return "access_proj('%s', ['%s'])" % \
60                        (self.name, str("','").join(self.node_types))
61            else:
62                return "access_proj('%s', [])" % self.name
63
64    # Used to report errors parsing the configuration files, not in providing
65    # service
66    class parse_error(RuntimeError): pass
67
68
69    def __init__(self, config=None):
70        """
71        Initializer.  Parses a configuration if one is given.
72        """
73
74        # Create instance attributes from the static lists
75        for a in fedd_proj.bool_attrs:
76            setattr(self, a, False)
77
78        for a in fedd_proj.emulab_attrs + fedd_proj.id_attrs:
79            setattr(self, a, None)
80
81        # Other attributes
82        self.attrs = {}
83        self.access = {}
84        self.fedid_category = {}
85        self.fedid_default = "user"
86        self.restricted = []
87
88        # Delete these
89        self.wap = '/usr/testbed/sbin/wap'
90        self.newproj = '/usr/testbed/sbin/newproj'
91        self.mkproj = '/usr/testbed/sbin/mkproj'
92        self.grantnodetype = '/usr/testbed/sbin/grantnodetype'
93
94        # Read the configuration
95        if config != None: 
96            self.read_config(config)
97
98        # Certs are promoted from the generic to the specific, so without a
99        # specific proxy certificate, the main certificates are used for proxy
100        # interactions. If no dynamic project certificates, then proxy certs
101        # are used, and if none of those the main certs.
102
103        # init proxy certs
104        if self.proxy_cert_file == None:
105            self.proxy_cert_file = self.cert_file
106            self.proxy_cert_pwd = self.cert_pwd
107
108        if self.proxy_trusted_certs == None:
109            self.proxy_trusted_certs = self.trusted_certs
110
111        # init dynamic project certs
112        if self.dynamic_projects_cert_file == None:
113            self.dynamic_projects_cert_file = self.proxy_cert_file
114            self.dynamic_projects_cert_pwd = self.proxy_cert_pwd
115
116        if self.dynamic_projects_trusted_certs == None:
117            self.dynamic_projects_trusted_certs = self.proxy_trusted_certs
118
119        proj_certs = (self.dynamic_projects_cert_file, 
120                self.dynamic_projects_trusted_certs,
121                self.dynamic_projects_cert_pwd)
122
123        if self.dynamic_projects_url == None:
124            self.allocate_project = \
125                fedd_allocate_project_local(self.dynamic_projects, 
126                        self.dynamic_projects_url, proj_certs)
127            fedd_proj.soap_methods['AllocateProject'] = 'soap_AllocateProject'
128        else:
129            self.allocate_project = \
130                fedd_allocate_project_remote(self.dynamic_projects, 
131                        self.dynamic_projects_url, proj_certs)
132
133    def dump_state(self):
134        """
135        Dump the state read from a configuration file.  Mostly for debugging.
136        """
137        for a in fedd_proj.bool_attrs:
138            print "%s: %s" % (a, getattr(self, a ))
139        for a in fedd_proj.emulab_attrs + fedd_proj.id_attrs:
140            print "%s: %s" % (a, getattr(self, a))
141        for k, v in self.attrs.iteritems():
142            print "%s %s" % (k, v)
143        print "Access DB:"
144        for k, v in self.access.iteritems():
145            print "%s %s" % (k, v)
146        print "Trust DB:"
147        for k, v in self.fedid_category.iteritems():
148            print "%s %s" % (k, v)
149        print "Restricted: %s" % str(',').join(sorted(self.restricted))
150
151    def get_users(self, obj):
152        """
153        Return a list of the IDs of the users in dict
154        """
155        if obj.has_key('user'):
156            return [ unpack_id(u['userID']) \
157                    for u in obj['user'] if u.has_key('userID') ]
158        else:
159            return None
160
161    def strip_unicode(self, obj):
162        """Loosly de-unicode an object"""
163        if isinstance(obj, dict):
164            for k in obj.keys():
165                obj[k] = self.strip_unicode(obj[k])
166            return obj
167        elif isinstance(obj, basestring):
168            return str(obj)
169        elif getattr(obj, "__iter__", None):
170            return [ self.strip_unicode(x) for x in obj]
171        else:
172            return obj
173
174    def proxy_xmlrpc_request(self, dt, req):
175        """Send an XMLRPC proxy request.  Called if the SOAP RPC fails"""
176
177        # No retry loop here.  Proxy servers must correctly authenticate
178        # themselves without help
179        try:
180            ctx = fedd_ssl_context(self.proxy_cert_file, 
181                    self.proxy_trusted_certs, password=self.proxy_cert_pwd)
182        except SSL.SSLError:
183            raise service_error(service_error.server_config,
184                    "Server certificates misconfigured")
185
186        # Of all the dumbass things.  The XMLRPC library in use here won't
187        # properly encode unicode strings, so we make a copy of req with the
188        # unicode objects converted.  We also convert the destination testbed
189        # to a basic string if it isn't one already.
190        if isinstance(dt, str): url = dt
191        else: url = str(dt)
192
193        r = copy.deepcopy(req)
194        self.strip_unicode(r)
195       
196        transport = SSL_Transport(ctx)
197        port = xmlrpclib.ServerProxy(url, transport=transport)
198
199        # Reconstruct the full request message
200        try:
201            resp = port.RequestAccess(
202                    { "RequestAccessRequestBody": r})
203            resp, method = xmlrpclib.loads(resp)
204        except xmlrpclib.Fault, f:
205            se = service_error(None, f.faultString, f.faultCode)
206            raise se
207        except xmlrpclib.Error, e:
208            raise service_error(service_error.proxy, 
209                    "Remote XMLRPC Fault: %s" % e)
210       
211        if resp[0].has_key('RequestAccessResponseBody'):
212            return resp[0]['RequestAccessResponseBody']
213        else:
214            raise service_error(service_error.proxy, 
215                    "Bad proxy response")
216
217    def proxy_request(self, dt, req):
218        """
219        Send req on to the real destination in dt and return the response
220
221        Req is just the requestType object.  This function re-wraps it.  It
222        also rethrows any faults.
223        """
224        # No retry loop here.  Proxy servers must correctly authenticate
225        # themselves without help
226        try:
227            ctx = fedd_ssl_context(self.proxy_cert_file, 
228                    self.proxy_trusted_certs, password=self.proxy_cert_pwd)
229        except SSL.SSLError:
230            raise service_error(service_error.server_config, 
231                    "Server certificates misconfigured")
232
233        loc = feddServiceLocator();
234        port = loc.getfeddPortType(dt,
235                transport=M2Crypto.httpslib.HTTPSConnection, 
236                transdict={ 'ssl_context' : ctx })
237
238        # Reconstruct the full request message
239        msg = RequestAccessRequestMessage()
240        msg.set_element_RequestAccessRequestBody(
241                pack_soap(msg, "RequestAccessRequestBody", req))
242        try:
243            resp = port.RequestAccess(msg)
244        except ZSI.ParseException, e:
245            raise service_error(service_error.proxy,
246                    "Bad format message (XMLRPC??): %s" %
247                    str(e))
248        r = unpack_soap(resp)
249
250        if r.has_key('RequestAccessResponseBody'):
251            return r['RequestAccessResponseBody']
252        else:
253            raise service_error(service_error.proxy,
254                    "Bad proxy response")
255
256    def permute_wildcards(self, a, p):
257        """Return a copy of a with various fields wildcarded.
258
259        The bits of p control the wildcards.  A set bit is a wildcard
260        replacement with the lowest bit being user then project then testbed.
261        """
262        if p & 1: user = ["<any>"]
263        else: user = a[2]
264        if p & 2: proj = "<any>"
265        else: proj = a[1]
266        if p & 4: tb = "<any>"
267        else: tb = a[0]
268
269        return (tb, proj, user)
270
271    def find_access(self, search):
272        """
273        Search the access DB for a match on this tuple.  Return the matching
274        access tuple and the user that matched.
275       
276        NB, if the initial tuple fails to match we start inserting wildcards in
277        an order determined by self.project_priority.  Try the list of users in
278        order (when wildcarded, there's only one user in the list).
279        """
280        if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7)
281        else: perm = (0, 2, 1, 3, 4, 6, 5, 7)
282
283        for p in perm: 
284            s = self.permute_wildcards(search, p)
285            # s[2] is None on an anonymous, unwildcarded request
286            if s[2] != None:
287                for u in s[2]:
288                    if self.access.has_key((s[0], s[1], u)):
289                        return (self.access[(s[0], s[1], u)], u)
290            else:
291                if self.access.has_key(s):
292                    return (self.access[s], None)
293        return None
294
295    def lookup_access(self, req, fid):
296        """
297        Determine the allowed access for this request.  Return the access and
298        which fields are dynamic.
299
300        The fedid is needed to construct the request
301        """
302        # Search keys
303        tb = None
304        project = None
305        user = None
306        # Return values
307        rp = fedd_proj.access_project(None, ())
308        ru = None
309
310
311        principal_type = self.fedid_category.get(fid, self.fedid_default)
312
313        if principal_type == "testbed": tb = fid
314
315        if req.has_key('project'):
316            p = req['project']
317            if p.has_key('name'):
318                project = unpack_id(p['name'])
319            user = self.get_users(p)
320        else:
321            user = self.get_users(req)
322
323        # Now filter by prinicpal type
324        if principal_type == "user":
325            if user != None:
326                fedids = [ u for u in user if isinstance(u, type(fid))]
327                if len(fedids) > 1:
328                    raise service_error(service_error.req,
329                            "User asserting multiple fedids")
330                elif len(fedids) == 1 and fedids[0] != fid:
331                    raise service_error(service_error.req,
332                            "User asserting different fedid")
333            project = None
334            tb = None
335        elif principal_type == "project":
336            if isinstance(project, type(fid)) and fid != project:
337                raise service_error(service_error.req,
338                        "Project asserting different fedid")
339            tb = None
340
341        # Ready to look up access
342        found, user_match = self.find_access((tb, project, user))
343       
344        if found == None:
345            raise service_error(service_error.access,
346                    "Access denied")
347
348        # resolve <dynamic> and <same> in found
349        dyn_proj = False
350        dyn_user = False
351
352        if found[0].name == "<same>":
353            if project != None:
354                rp.name = project
355            else : 
356                raise service_error(\
357                        service_error.server_config,
358                        "Project matched <same> when no project given")
359        elif found[0].name == "<dynamic>":
360            rp.name = None
361            dyn_proj = True
362        else:
363            rp.name = found[0].name
364        rp.node_types = found[0].node_types;
365
366        if found[1] == "<same>":
367            if user_match == "<any>":
368                if user != None: ru = user[0]
369                else: raise service_error(\
370                        service_error.server_config,
371                        "Matched <same> on anonymous request")
372            else:
373                ru = user_match
374        elif found[1] == "<dynamic>":
375            ru = None
376            dyn_user = True
377       
378        return (rp, ru), (dyn_user, dyn_proj)
379
380    def build_response(self, alloc_id, ap):
381        """
382        Create the SOAP response.
383
384        Build the dictionary description of the response and use
385        fedd_utils.pack_soap to create the soap message.  NB that alloc_id is
386        a fedd_services_types.IDType_Holder pulled from the incoming message.
387        ap is the allocate project message returned from a remote project
388        allocation (even if that allocation was done locally).
389        """
390        # Because alloc_id is already a fedd_services_types.IDType_Holder,
391        # there's no need to repack it
392        msg = { 
393                'allocID': alloc_id,
394                'emulab': { 
395                    'domain': self.domain,
396                    'boss': self.boss,
397                    'ops': self.ops,
398                    'fileServer': self.fileserver,
399                    'eventServer': self.eventserver,
400                    'project': ap['project']
401                },
402            }
403        if len(self.attrs) > 0:
404            msg['emulab']['fedAttr'] = \
405                [ { 'attribute': x, 'value' : y } \
406                        for x,y in self.attrs.iteritems()]
407        return msg
408
409    def RequestAccess(self, req, fid):
410
411        if req.has_key('RequestAccessRequestBody'):
412            req = req['RequestAccessRequestBody']
413        else:
414            raise service_error(service_error.req, "No request!?")
415
416        if req.has_key('destinationTestbed'):
417            dt = unpack_id(req['destinationTestbed'])
418       
419        if dt == None or dt == self.testbed:
420            # Request for this fedd
421            found, dyn = self.lookup_access(req, fid)
422            restricted = None
423            ap = None
424
425            # Check for access to restricted nodes
426            if req.has_key('resources') and req['resources'].has_key('node'):
427                resources = req['resources']
428                restricted = [ t for n in resources['node'] \
429                                if n.has_key('hardware') \
430                                    for t in n['hardware'] \
431                                        if t in self.restricted ]
432                inaccessible = [ t for t in restricted \
433                                    if t not in found[0].node_types]
434                if len(inaccessible) > 0:
435                    raise service_error(service_error.access,
436                            "Access denied (nodetypes %s)" % \
437                            str(', ').join(inaccessible))
438
439            ssh = [ x['sshPubkey'] \
440                    for x in req['access'] if x.has_key('sshPubkey')]
441
442            if len(ssh) > 0: 
443                if dyn[1]: 
444                    # Compose the dynamic project request
445                    # (only dynamic, dynamic currently allowed)
446                    preq = { 'AllocateProjectRequestBody': \
447                                { 'project' : {\
448                                    'user': [ \
449                                    { 'access': [ { 'sshPubkey': s } ] } \
450                                        for s in ssh ] \
451                                    }\
452                                }\
453                            }
454                    if restricted != None and len(restricted) > 0:
455                        preq['AllocateProjectRequestBody']['resources'] = \
456                            [ {'node': { 'hardware' :  [ h ] } } \
457                                    for h in restricted ]
458                               
459                    ap = self.allocate_project.dynamic_project(preq)
460                else:
461                    # XXX ssh key additions
462                    ap = { 'project': \
463                            { 'name' : { 'username' : found[0] },\
464                              'user' : [ {\
465                                'userID': { 'username' : found[1] }, \
466                                'access': [ { 'sshPubkey': s } for s in ssh]}\
467                                ]\
468                            }\
469                    }
470            else:
471                raise service_error(service_error.req, 
472                        "SSH access parameters required")
473
474            resp = self.build_response(req['allocID'], ap)
475            return resp
476        else:
477            p_fault = None      # Any SOAP failure (sent unless XMLRPC works)
478            try:
479                # Proxy the request using SOAP
480                return self.proxy_request(dt, req)
481            except service_error, e:
482                if e.code == service_error.proxy: p_fault = None
483                else: raise
484            except ZSI.FaultException, f:
485                p_fault = f.fault.detail[0]
486                   
487
488            # If we could not get a valid SOAP response to the request above,
489            # try the same address using XMLRPC and let any faults flow back
490            # out.
491            if p_fault == None:
492                return self.proxy_xmlrpc_request(dt, req)
493            else:
494                # Build the fault
495                body = p_fault.get_element_RequestAccessFaultBody()
496                if body != None:
497                    raise service_error(body.get_element_code(),
498                                body.get_element_desc());
499                else:
500                    raise service_error(\
501                            service_error.proxy,
502                            "Undefined fault from proxy??");
503
504
505    def soap_AllocateProject(self, ps, fid):
506        req = ps.Parse(AllocateProjectRequestMessage.typecode)
507
508        msg = self.allocate_project.dynamic_project(unpack_soap(req), fedid)
509
510        resp = AllocateProjectResponseMessage()
511        resp.set_element_AllocateProjectResponseBody(
512                pack_soap(resp, "AllocateProjectResponseBody", msg))
513
514        return resp
515
516    def soap_RequestAccess(self, ps, fid):
517        req = ps.Parse(RequestAccessRequestMessage.typecode)
518
519        msg = self.RequestAccess(unpack_soap(req), fedid)
520
521        resp = RequestAccessResponseMessage()
522        resp.set_element_RequestAccessResponseBody(
523                pack_soap(resp, "RequestAccessResponseBody", msg))
524
525        return resp
526
527    def xmlrpc_RequestAccess(self, params, fid):
528        msg = self.RequestAccess(params[0], fedid)
529
530        if msg != None:
531            return xmlrpclib.dumps(({ "RequestAccessResponseBody": msg },))
532        else:
533            raise service_error(service_error.internal,
534                    "No response generated?!");
535
536    def read_trust(self, trust):
537        """
538        Read a trust file that splits fedids into testbeds, users or projects
539
540        Format is:
541
542        [type]
543        fedid
544        fedid
545        default: type
546        """
547        lineno = 0;
548        cat = None
549        cat_re = re.compile("\[(user|testbed|project)\]$", re.IGNORECASE)
550        fedid_re = re.compile("[" + string.hexdigits + "]+$")
551        default_re = re.compile("default:\s*(user|testbed|project)$", 
552                re.IGNORECASE)
553
554        f = open(trust, "r")
555        for line in f:
556            lineno += 1
557            line = line.strip()
558            if len(line) == 0 or line.startswith("#"):
559                continue
560            # Category line
561            m = cat_re.match(line)
562            if m != None:
563                cat = m.group(1).lower()
564                continue
565            # Fedid line
566            m = fedid_re.match(line)
567            if m != None:
568                if cat != None:
569                    self.fedid_category[fedid(hexstr=m.string)] = cat
570                else:
571                    raise fedd_proj.parse_error(\
572                            "Bad fedid in trust file (%s) line: %d" % \
573                            (trust, lineno))
574                continue
575            # default line
576            m = default_re.match(line)
577            if m != None:
578                self.fedid_default = m.group(1).lower()
579                continue
580            # Nothing matched - bad line, raise exception
581            f.close()
582            raise fedd_proj.parse_error(\
583                    "Unparsable line in trustfile %s line %d" % (trust, lineno))
584        f.close()
585
586    def read_config(self, config):
587        """
588        Read a configuration file and set internal parameters.
589
590        The format is more complex than one might hope.  The basic format is
591        attribute value pairs separated by colons(:) on a signle line.  The
592        attributes in bool_attrs, emulab_attrs and id_attrs can all be set
593        directly using the name: value syntax.  E.g.
594        boss: hostname
595        sets self.boss to hostname.  In addition, there are access lines of the
596        form (tb, proj, user) -> (aproj, auser) that map the first tuple of
597        names to the second for access purposes.  Names in the key (left side)
598        can include "<NONE> or <ANY>" to act as wildcards or to require the
599        fields to be empty.  Similarly aproj or auser can be <SAME> or
600        <DYNAMIC> indicating that either the matching key is to be used or a
601        dynamic user or project will be created.  These names can also be
602        federated IDs (fedid's) if prefixed with fedid:.  Finally, the aproj
603        can be followed with a colon-separated list of node types to which that
604        project has access (or will have access if dynamic).
605        Testbed attributes outside the forms above can be given using the
606        format attribute: name value: value.  The name is a single word and the
607        value continues to the end of the line.  Empty lines and lines startin
608        with a # are ignored.
609
610        Parsing errors result in a parse_error exception being raised.
611        """
612        lineno=0
613        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
614        fedid_expr = "fedid:[" + string.hexdigits + "]+"
615        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
616        access_proj = "(<DYNAMIC>(?::" + name_expr +")*|"+ \
617                "<SAME>" + "(?::" + name_expr + ")*|" + \
618                fedid_expr + "(?::" + name_expr + ")*|" + \
619                name_expr + "(?::" + name_expr + ")*)"
620        access_name = "(<DYNAMIC>|<SAME>|" + fedid_expr + "|"+ name_expr + ")"
621
622        bool_re = re.compile('(' + '|'.join(fedd_proj.bool_attrs) + 
623                '):\s+(true|false)', re.IGNORECASE)
624        string_re = re.compile( "(" + \
625                '|'.join(fedd_proj.emulab_attrs + fedd_proj.id_attrs) + \
626                '):\s*(.*)', re.IGNORECASE)
627        attr_re = re.compile('attribute:\s*([\._\-a-z0-9]+)\s+value:\s*(.*)',
628                re.IGNORECASE)
629        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
630                key_name+'\s*\)\s*->\s*\('+access_proj + '\s*,\s*' + 
631                access_name + '\s*\)', re.IGNORECASE)
632        trustfile_re = re.compile("trustfile:\s*(.*)", re.IGNORECASE)
633        restricted_re = re.compile("restricted:\s*(.*)", re.IGNORECASE)
634
635        def parse_name(n):
636            if n.startswith('fedid:'): return fedid(n[len('fedid:'):])
637            else: return n
638
639        f = open(config, "r");
640        for line in f:
641            lineno += 1
642            line = line.strip();
643            if len(line) == 0 or line.startswith('#'):
644                continue
645
646            # Boolean attribute line
647            m = bool_re.match(line);
648            if m != None:
649                attr, val = m.group(1,2)
650                setattr(self, attr.lower(), bool(val.lower() == "true"))
651                continue
652
653            # String attribute line
654            m = string_re.match(line)
655            if m != None:
656                attr, val = m.group(1,2)
657                setattr(self, attr.lower(), val)
658                continue
659
660            # Extended (attribute: x value: y) attribute line
661            m = attr_re.match(line)
662            if m != None:
663                attr, val = m.group(1,2)
664                self.attrs[attr] = val
665                continue
666
667            # Access line (t, p, u) -> (ap, au) line
668            m = access_re.match(line)
669            if m != None:
670                access_key = tuple([ parse_name(x) for x in m.group(1,2,3)])
671                aps = m.group(4).split(":");
672                if aps[0] == 'fedid:':
673                    del aps[0]
674                    aps[0] = fedid(hexstr=aps[0])
675
676                au = m.group(5)
677                if au.startswith("fedid:"):
678                    au = fedid(hexstr=aus[len("fedid:"):])
679
680                access_val = (fedd_proj.access_project(aps[0], aps[1:]), au)
681
682                self.access[access_key] = access_val
683                continue
684
685            # Trustfile inclusion
686            m = trustfile_re.match(line)
687            if m != None:
688                self.read_trust(m.group(1))
689                continue
690            # Restricted node types
691
692            m = restricted_re.match(line)
693            if m != None:
694                self.restricted.append(m.group(1))
695                continue
696
697            # Nothing matched to here: unknown line - raise exception
698            f.close()
699            raise fedd_proj.parse_error("Unknown statement at line %d of %s" % \
700                    (lineno, config))
701        f.close()
702
703    def soap_dispatch(self, method, req, fid):
704        if fedd_proj.soap_methods.has_key(method):
705            try:
706                return getattr(self, fedd_proj.soap_methods[method])(req, fid)
707            except service_error, e:
708                de = ns0.faultType_Def(
709                        (ns0.faultType_Def.schema,
710                            "RequestAccessFaultBody")).pyclass()
711                de._code=e.code
712                de._errstr=e.code_string()
713                de._desc=e.desc
714                if  e.is_server_error():
715                    raise Fault(Fault.Server, e.code_string(), detail=de)
716                else:
717                    raise Fault(Fault.Client, e.code_string(), detail=de)
718        else:
719            raise Fault(Fault.Client, "Unknown method: %s" % method)
720
721    def xmlrpc_dispatch(self, method, req, fid):
722        if fedd_proj.xmlrpc_methods.has_key(method):
723            try:
724                return getattr(self, fedd_proj.xmlrpc_methods[method])(req, fid)
725            except service_error, e:
726                raise xmlrpclib.Fault(e.code_string(), e.desc)
727        else:
728            raise xmlrpclib.Fault(100, "Unknown method: %s" % method)
729
730def new_feddservice(configfile):
731    return fedd_proj(configfile)
Note: See TracBrowser for help on using the repository browser.