source: fedd/fedd.py @ 3e293e4

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

Reimplementation of fedd as a start in merging the codebases

  • Property mode set to 100755
File size: 4.5 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4
5from BaseHTTPServer import BaseHTTPRequestHandler
6
7from ZSI import Fault, ParseException, FaultFromNotUnderstood, \
8    FaultFromZSIException, FaultFromException, ParsedSoap, SoapWriter
9
10from M2Crypto import SSL
11from M2Crypto.SSL.SSLServer import SSLServer
12
13from optparse import OptionParser
14
15from fedd_util import fedd_ssl_context, fedid
16from fedd_proj import new_feddservice
17
18# The SSL server here is based on the implementation described at
19# http://www.xml.com/pub/a/ws/2004/01/20/salz.html
20
21# Turn off the matching of hostname to certificate ID
22SSL.Connection.clientPostConnectionCheck = None
23
24class fedd_server(SSLServer):
25    def __init__(self, ME, handler, ssl_ctx, impl):
26        SSLServer.__init__(self, ME, handler, ssl_ctx)
27        self.impl = impl
28
29class fedd_handler(BaseHTTPRequestHandler):
30    server_version = "ZSI/2.0 fedd/0.1 " + BaseHTTPRequestHandler.server_version
31
32    namespace = 'http://www.isi.edu/faber/fedd.wsdl'
33    methods = {
34            'RequestAccess': 'soap_RequestAccess',
35            }
36
37
38    def send_xml(self, text, code=200):
39        """Send an XML document as reply"""
40        self.send_response(code)
41        self.send_header('Content-type', 'text/xml; charset="utf-8"')
42        self.send_header('Content-Length', str(len(text)))
43        self.end_headers()
44        self.wfile.write(text)
45        self.wfile.flush()
46
47    def send_fault(self, f, code=500):
48        """Send a SOAP encoded fault as reply"""
49        self.send_xml(f.AsSOAP(), code)
50
51    def check_headers(self, ps):
52        """Send a fault for any required envelope headers"""
53        for (uri, localname) in ps.WhatMustIUnderstand():
54            self.send_fault(FaultFromNotUnderstood(uri, lname, 'fedd'))
55            return False
56        return  True
57
58    def check_method(self, ps):
59        """Confirm that this class implements the namespace and SOAP method"""
60        root = ps.body_root
61        if root.namespaceURI != fedd_handler.namespace:
62            self.send_fault(Fault(Fault.Client, 
63                'Unknown namespace "%s"' % root.namespaceURI))
64            return False
65        if not fedd_handler.methods.has_key(root.localName):
66            self.send_fault(Fault(Fault.Client, 'Unknown operation "%s"' % \
67                    root.localName))
68            return False
69        return True
70
71    def do_POST(self):
72        """Treat an HTTP POST request as a SOAP service call"""
73        try:
74            cl = int(self.headers['content-length'])
75            data = self.rfile.read(cl)
76            ps = ParsedSoap(data)
77        except ParseException, e:
78            self.send_fault(FaultFromZSIException(e))
79            return
80        except Exception, e:
81            self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
82            return
83        if not self.check_headers(ps): return
84        if not self.check_method(ps): return
85        try:
86            # We know this will work because check_method passed above
87            method = getattr(self.server.impl,
88                    fedd_handler.methods[ps.body_root.localName])
89            resp = method(ps, fedid(cert=self.request.get_peer_cert()))
90        except Fault, f:
91            self.send_fault(f)
92            resp = None
93       
94        if resp != None:
95            sw = SoapWriter()
96            sw.serialize(resp)
97            self.send_xml(str(sw))
98
99class fedd_opts(OptionParser):
100    """Encapsulate option processing in this class, rather than in main"""
101    def __init__(self):
102        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
103                version="0.1")
104
105        self.set_defaults(host="localhost", port=23235, debug=0)
106
107        self.add_option("-d", "--debug", action="count", dest="debug", 
108                help="Set debug.  Repeat for more information")
109        self.add_option("-f", "--configfile", action="store",
110                dest="configfile", help="Configuration file (required)")
111        self.add_option("-H", "--host", action="store", type="string",
112                dest="host", help="Hostname to listen on (default %default)")
113        self.add_option("-p", "--port", action="store", type="int",
114                dest="port", help="Port to listen on (default %default)")
115        self.add_option("--trace", action="store_const", dest="tracefile", 
116                const=sys.stderr, help="Print SOAP exchange to stderr")
117
118opts, args = fedd_opts().parse_args()
119
120if opts.configfile != None: 
121    try:
122        impl = new_feddservice(opts.configfile)
123    except RuntimeError, e:
124        sys.exit("Error configuring fedd: %s" % e.message)
125else: 
126    sys.exit("--configfile is required")
127
128SOAP_port = (opts.host, opts.port)
129
130if impl.cert_file == None:
131    sys.exit("Must supply certificate file (probably in config)")
132
133bad_password = True
134while bad_password:
135    try:
136        ctx = fedd_ssl_context(impl.cert_file, impl.trusted_certs, 
137                password=impl.cert_pwd)
138        bad_password = False
139    except SSL.SSLError, e:
140        if e.message == "bad decrypt" and impl.cert_pwd == None:
141            bad_password = True
142        else:
143            raise
144
145s = fedd_server(SOAP_port, fedd_handler, ctx, impl)
146s.serve_forever()
Note: See TracBrowser for help on using the repository browser.