98373ec3039105ecb8746aab232a41fb34bb8de2
[sfa.git] / sfa / util / componentserver.py
1 ##
2 # This module implements a general-purpose server layer for sfa.
3 # The same basic server should be usable on the registry, component, or
4 # other interfaces.
5 #
6 # TODO: investigate ways to combine this with existing PLC server?
7 ##
8
9 import sys
10 import traceback
11 import threading
12 import socket, os
13 import SocketServer
14 import BaseHTTPServer
15 import SimpleHTTPServer
16 import SimpleXMLRPCServer
17 from OpenSSL import SSL
18
19 from sfa.util.sfalogging import logger
20 from sfa.trust.certificate import Keypair, Certificate
21 from sfa.trust.credential import *
22 from sfa.util.faults import *
23 from sfa.plc.api import ComponentAPI 
24 from sfa.util.server import verify_callback, ThreadedServer 
25
26
27 ##
28 # taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler
29
30 class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
31     """Secure XML-RPC request handler class.
32
33     It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
34     """
35     def setup(self):
36         self.connection = self.request
37         self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
38         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
39
40     def do_POST(self):
41         """Handles the HTTPS POST request.
42
43         It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
44         """
45         try:
46             peer_cert = Certificate()
47             peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate())
48             self.api = ComponentAPI(peer_cert = peer_cert, 
49                            interface = self.server.interface, 
50                            key_file = self.server.key_file, 
51                            cert_file = self.server.cert_file)
52             # get arguments
53             request = self.rfile.read(int(self.headers["content-length"]))
54             # In previous versions of SimpleXMLRPCServer, _dispatch
55             # could be overridden in this class, instead of in
56             # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
57             # check to see if a subclass implements _dispatch and dispatch
58             # using that method if present.
59             #response = self.server._marshaled_dispatch(request, getattr(self, '_dispatch', None))
60             # XX TODO: Need to get the real remote address
61             remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
62             self.api.remote_addr = remote_addr
63             #remote_addr = (self.rfile.connection.remote_ip, remote_port)
64             #self.api.remote_addr = remote_addr
65             response = self.api.handle(remote_addr, request)
66
67         
68         except Exception, fault:
69             raise
70             # This should only happen if the module is buggy
71             # internal error, report as HTTP server error
72             self.send_response(500)
73             self.end_headers()
74             logger.log_exc("componentserver.SecureXMLRpcRequestHandler.do_POST")
75         else:
76             # got a valid XML RPC response
77             self.send_response(200)
78             self.send_header("Content-type", "text/xml")
79             self.send_header("Content-length", str(len(response)))
80             self.end_headers()
81             self.wfile.write(response)
82
83             # shut down the connection
84             self.wfile.flush()
85             self.connection.shutdown() # Modified here!
86
87 ##
88 # Implements an HTTPS XML-RPC server. Generally it is expected that SFA
89 # functions will take a credential string, which is passed to
90 # decode_authentication. Decode_authentication() will verify the validity of
91 # the credential, and verify that the user is using the key that matches the
92 # GID supplied in the credential.
93
94 class ComponentServer(threading.Thread):
95
96     ##
97     # Create a new SfaServer object.
98     #
99     # @param ip the ip address to listen on
100     # @param port the port to listen on
101     # @param key_file private key filename of registry
102     # @param cert_file certificate filename containing public key 
103     #   (could be a GID file)
104
105     def __init__(self, ip, port, key_file, cert_file, api=None):
106         threading.Thread.__init__(self)
107         self.key = Keypair(filename = key_file)
108         self.cert = Certificate(filename = cert_file)
109         self.server = ThreadedServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
110         self.trusted_cert_list = None
111         self.register_functions()
112
113
114     ##
115     # Register functions that will be served by the XMLRPC server. This
116     # function should be overrided by each descendant class.
117
118     def register_functions(self):
119         self.server.register_function(self.noop)
120
121     ##
122     # Sample no-op server function. The no-op function decodes the credential
123     # that was passed to it.
124
125     def noop(self, cred, anything):
126         self.decode_authentication(cred)
127
128         return anything
129
130     ##
131     # Execute the server, serving requests forever. 
132
133     def run(self):
134         self.server.serve_forever()
135
136