check in latest updates to documentation
[sfa.git] / util / geniserver.py
index c4cd60a..b8cc047 100644 (file)
@@ -1,14 +1,15 @@
-# geniserver.py
-#
-# geniwrapper server
-#
-# implements a general-purpose server layer for geni. This should be usable on
-# the registry, component, or other interfaces.
+##
+# This module implements a general-purpose server layer for geni.
+# The same basic server should be usable on the registry, component, or
+# other interfaces.
 #
 # TODO: investigate ways to combine this with existing PLC server?
+##
 
 import SimpleXMLRPCServer
 
+import sys
+import traceback
 import SocketServer
 import BaseHTTPServer\r
 import SimpleHTTPServer\r
@@ -21,9 +22,8 @@ from credential import *
 import socket, os\r
 from OpenSSL import SSL\r
 \r
-# verify_callback\r
-#\r
-# verification callback for pyOpenSSL. We do our own checking of keys because\r
+##\r
+# Verification callback for pyOpenSSL. We do our own checking of keys because\r
 # we have our own authentication spec. Thus we disable several of the normal\r
 # prohibitions that OpenSSL places on certificates\r
 \r
@@ -72,9 +72,8 @@ def verify_callback(conn, x509, err, depth, preverify):
 
     return 0\r
 \r
-# SecureXMLServer\r
-#\r
-# taken from the web (XXX find reference). Implements an HTTPS xmlrpc server\r
+##\r
+# Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server\r
 
 class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):\r
     def __init__(self, server_address, HandlerClass, key_file, cert_file, logRequests=True):\r
@@ -84,7 +83,7 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
         """\r
         self.logRequests = logRequests\r
 \r
-        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, None, None)\r
+        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, True, None)\r
         SocketServer.BaseServer.__init__(self, server_address, HandlerClass)\r
         ctx = SSL.Context(SSL.SSLv23_METHOD)\r
         ctx.use_privatekey_file(key_file)\r
@@ -96,8 +95,21 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
         self.server_bind()\r
         self.server_activate()\r
 \r
-# SecureXMLRpcRequestHandler\r
-#\r
+    # _dispatch\r
+    #
+    # Convert an exception on the server to a full stack trace and send it to
+    # the client.
+
+    def _dispatch(self, method, params):\r
+        try:\r
+            return SimpleXMLRPCServer.SimpleXMLRPCDispatcher._dispatch(self, method, params)\r
+        except:\r
+            # can't use format_exc() as it is not available in jython yet (even\r
+            # in trunk).\r
+            type, value, tb = sys.exc_info()\r
+            raise xmlrpclib.Fault(1,''.join(traceback.format_exception(type, value, tb)))\r
+\r
+##\r
 # taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler\r
 \r
 class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):\r
@@ -130,6 +142,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
         except: # This should only happen if the module is buggy\r
             # internal error, report as HTTP server error\r
             self.send_response(500)\r
+\r
             self.end_headers()\r
         else:\r
             # got a valid XML RPC response\r
@@ -143,10 +156,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
             self.wfile.flush()\r
             self.connection.shutdown() # Modified here!\r
 
-# GeniServer
-#
-# Class for a general purpose geni server.
-#
+##
 # Implements an HTTPS XML-RPC server. Generally it is expected that GENI
 # functions will take a credential string, which is passed to
 # decode_authentication. Decode_authentication() will verify the validity of
@@ -154,13 +164,29 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
 # GID supplied in the credential.
 
 class GeniServer():
+
+    ##
+    # Create a new GeniServer object.
+    #
+    # @param ip the ip address to listen on
+    # @param port the port to listen on
+    # @param key_file private key filename of registry
+    # @param cert_file certificate filename containing public key (could be a GID file)
+
     def __init__(self, ip, port, key_file, cert_file):
         self.key = Keypair(filename = key_file)
         self.cert = Certificate(filename = cert_file)
         self.server = SecureXMLRPCServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
+        self.trusted_cert_list = None
         self.register_functions()
 
-    def decode_authentication(self, cred_string):
+    ##
+    # Decode the credential string that was submitted by the caller. Several
+    # checks are performed to ensure that the credential is valid, and that the
+    # callerGID included in the credential matches the caller that is
+    # connected to the HTTPS connection.
+
+    def decode_authentication(self, cred_string, operation):
         self.client_cred = Credential(string = cred_string)
         self.client_gid = self.client_cred.get_gid_caller()
         self.object_gid = self.client_cred.get_gid_object()
@@ -174,15 +200,36 @@ class GeniServer():
         if not peer_cert.is_pubkey(self.client_gid.get_pubkey()):
             raise ConnectionKeyGIDMismatch(self.client_gid.get_subject())
 
-    # register_functions override this to add more functions
+        # make sure the client is allowed to perform the operation
+        if not self.client_cred.can_perform(operation):
+            raise InsufficientRights(operation)
+
+        if self.trusted_cert_list:
+            self.client_cred.verify_chain(self.trusted_cert_list)
+            if self.client_gid:
+                self.client_gid.verify_chain(self.trusted_cert_list)
+            if self.object_gid:
+                self.object_gid.verify_chain(self.trusted_cert_list)
+
+    ##
+    # Register functions that will be served by the XMLRPC server. This
+    # function should be overrided by each descendant class.
+
     def register_functions(self):
         self.server.register_function(self.noop)
 
+    ##
+    # Sample no-op server function. The no-op function decodes the credential
+    # that was passed to it.
+
     def noop(self, cred, anything):
         self.decode_authentication(cred)
 
         return anything
 
+    ##
+    # Execute the server, serving requests forever. 
+
     def run(self):
         self.server.serve_forever()