#!/usr/local/bin/python import sys from BaseHTTPServer import BaseHTTPRequestHandler from ZSI import Fault, ParseException, FaultFromNotUnderstood, \ FaultFromZSIException, FaultFromException, ParsedSoap, SoapWriter from M2Crypto import SSL from M2Crypto.SSL.SSLServer import SSLServer from optparse import OptionParser from fedd_util import fedd_ssl_context, fedid from fedd_proj import new_feddservice # The SSL server here is based on the implementation described at # http://www.xml.com/pub/a/ws/2004/01/20/salz.html # Turn off the matching of hostname to certificate ID SSL.Connection.clientPostConnectionCheck = None class fedd_server(SSLServer): def __init__(self, ME, handler, ssl_ctx, impl): SSLServer.__init__(self, ME, handler, ssl_ctx) self.impl = impl class fedd_handler(BaseHTTPRequestHandler): server_version = "ZSI/2.0 fedd/0.1 " + BaseHTTPRequestHandler.server_version namespace = 'http://www.isi.edu/faber/fedd.wsdl' methods = { 'RequestAccess': 'soap_RequestAccess', } def send_xml(self, text, code=200): """Send an XML document as reply""" self.send_response(code) self.send_header('Content-type', 'text/xml; charset="utf-8"') self.send_header('Content-Length', str(len(text))) self.end_headers() self.wfile.write(text) self.wfile.flush() def send_fault(self, f, code=500): """Send a SOAP encoded fault as reply""" self.send_xml(f.AsSOAP(), code) def check_headers(self, ps): """Send a fault for any required envelope headers""" for (uri, localname) in ps.WhatMustIUnderstand(): self.send_fault(FaultFromNotUnderstood(uri, lname, 'fedd')) return False return True def check_method(self, ps): """Confirm that this class implements the namespace and SOAP method""" root = ps.body_root if root.namespaceURI != fedd_handler.namespace: self.send_fault(Fault(Fault.Client, 'Unknown namespace "%s"' % root.namespaceURI)) return False if not fedd_handler.methods.has_key(root.localName): self.send_fault(Fault(Fault.Client, 'Unknown operation "%s"' % \ root.localName)) return False return True def do_POST(self): """Treat an HTTP POST request as a SOAP service call""" try: cl = int(self.headers['content-length']) data = self.rfile.read(cl) ps = ParsedSoap(data) except ParseException, e: self.send_fault(FaultFromZSIException(e)) return except Exception, e: self.send_fault(FaultFromException(e, 0, sys.exc_info()[2])) return if not self.check_headers(ps): return if not self.check_method(ps): return try: # We know this will work because check_method passed above method = getattr(self.server.impl, fedd_handler.methods[ps.body_root.localName]) resp = method(ps, fedid(cert=self.request.get_peer_cert())) except Fault, f: self.send_fault(f) resp = None if resp != None: sw = SoapWriter() sw.serialize(resp) self.send_xml(str(sw)) class fedd_opts(OptionParser): """Encapsulate option processing in this class, rather than in main""" def __init__(self): OptionParser.__init__(self, usage="%prog [opts] (--help for details)", version="0.1") self.set_defaults(host="localhost", port=23235, debug=0) self.add_option("-d", "--debug", action="count", dest="debug", help="Set debug. Repeat for more information") self.add_option("-f", "--configfile", action="store", dest="configfile", help="Configuration file (required)") self.add_option("-H", "--host", action="store", type="string", dest="host", help="Hostname to listen on (default %default)") self.add_option("-p", "--port", action="store", type="int", dest="port", help="Port to listen on (default %default)") self.add_option("--trace", action="store_const", dest="tracefile", const=sys.stderr, help="Print SOAP exchange to stderr") opts, args = fedd_opts().parse_args() if opts.configfile != None: try: impl = new_feddservice(opts.configfile) except RuntimeError, e: sys.exit("Error configuring fedd: %s" % e.message) else: sys.exit("--configfile is required") SOAP_port = (opts.host, opts.port) if impl.cert_file == None: sys.exit("Must supply certificate file (probably in config)") ctx = None while ctx == None: try: ctx = fedd_ssl_context(impl.cert_file, impl.trusted_certs, password=impl.cert_pwd) except SSL.SSLError, e: if str(e) != "bad decrypt" or impl.cert_pwd != None: raise s = fedd_server(SOAP_port, fedd_handler, ctx, impl) s.serve_forever()