extras-buildsys/common FileDownloader.py, 1.3, 1.4 HTTPSURLopener.py, 1.2, 1.3 SSLCommon.py, 1.1, 1.2 SSLXMLRPCServerProxy.py, 1.2, 1.3 SimpleHTTPSServer.py, 1.3, 1.4 SimpleSSLXMLRPCServer.py, 1.2, 1.3

Daniel Williams (dcbw) fedora-extras-commits at redhat.com
Tue Jun 14 01:16:32 UTC 2005


Author: dcbw

Update of /cvs/fedora/extras-buildsys/common
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv21732/common

Modified Files:
	FileDownloader.py HTTPSURLopener.py SSLCommon.py 
	SSLXMLRPCServerProxy.py SimpleHTTPSServer.py 
	SimpleSSLXMLRPCServer.py 
Log Message:
2005-06-13  Dan Williams <dcbw at redhat.com>

    * client/buildclient.py
      common/FileDownloader.py
      common/HTTPSURLopener.py
      common/SSLCommon.py
      common/SSLXMLRPCServerProxy.py
      common/SimpleHTTPSServer.py
      common/SimpleSSLXMLRPCServer.py
      server/client_manager.py
        - Clean up cert handling by stuffing cert file paths into a dict

    * common/SSLCommon.py
        - QuietSSLServer: new class to quiet certain SSL errors we don't care about

    * common/SimpleSSLXMLRPCServer.py
        - Add an authorization framework.  The actual request gets authorized
            in SimpleSSLXMLRPCServer, but since we need to pass the authorization
            object back up to the Instance class, we need to override a bunch
            of functions in superclasses just to pass the object through.  Subclasses
            of SimpleSSLXMLRPCServer don't need to use authorization, they just don't
            set an auth callback.  If authorization is in use, the subclass is asked
            to authorize the connection, and passes back an arbitrary authorization
            object, which gets passed to each Handler method.

    * server/buildmaster.py
      server/buildserver.py
        - Change buildmaster_db -> jobdb

    * server/buildserver.py
        - XMLRPCBuildMaster -> UserInterface
        - Attach authorization to all public XMLRPC methods
        - VerifiableSSLXMLRPCServer: new class to handle request authorization
        - Add "guest" user support.  If the user's cert checks out but they are not
            in the user database, they have read-only permission to the server

    * server/User.py
        - New User object: encapsulates all permissions that a user might
            have.
        - New UserAuthentication object: looks up user authentication info in
            a user database and creates User objects

    * utils/user-manager.py
        - Local user administration tool

    * utils/package-builder.py
        - New CLI front-end to the build system that support XMLRPC over SSL

    * utils/certs/fedora-upload-ca.cert
        - Fedora CVS Upload CA certificate, required to validate users




Index: FileDownloader.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/common/FileDownloader.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- FileDownloader.py	10 Jun 2005 01:35:40 -0000	1.3
+++ FileDownloader.py	14 Jun 2005 01:16:30 -0000	1.4
@@ -72,7 +72,7 @@
 
 class FileDownloader(threading.Thread):
 
-    def __init__(self, callback, cb_data, url, target_dir, legal_exts, certfile, keyfile, ca_certfile):
+    def __init__(self, callback, cb_data, url, target_dir, legal_exts, certs):
         self._callback = callback
         self._cb_data = cb_data
         self._url = url
@@ -80,7 +80,7 @@
         self._filename = get_base_filename_from_url(self._url, legal_exts)
         if not self._filename:
             print "Couldn't get base filename from url!!  target_dir=%s, url=%s" % (target_dir, url)
-        self._opener = HTTPSURLopener.HTTPSURLopener(certfile, keyfile, ca_certfile)
+        self._opener = HTTPSURLopener.HTTPSURLopener(certs)
         threading.Thread.__init__(self)
 
     def run(self):


Index: HTTPSURLopener.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/common/HTTPSURLopener.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- HTTPSURLopener.py	13 Jun 2005 05:12:09 -0000	1.2
+++ HTTPSURLopener.py	14 Jun 2005 01:16:30 -0000	1.3
@@ -23,8 +23,8 @@
 import SSLCommon
 
 class HTTPSURLopener(urllib.URLopener):
-    def __init__(self, certfile, keyfile, ca_certfile):
-        self.ctx = SSLCommon.getSSLContext(certfile, keyfile, ca_certfile)
+    def __init__(self, certs):
+        self.ctx = SSLCommon.getSSLContext(certs)
         urllib.URLopener.__init__(self)
 
     def open_https(self, url, data=None):


Index: SSLCommon.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/common/SSLCommon.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SSLCommon.py	13 Jun 2005 05:12:09 -0000	1.1
+++ SSLCommon.py	14 Jun 2005 01:16:30 -0000	1.2
@@ -24,19 +24,23 @@
     """
     return
 
-def getSSLContext(certfile, keyfile, ca_certfile, session_id='ssl_session'):
+def getSSLContext(certs, session_id='ssl_session'):
     """
     Helper method for m2crypto's SSL libraries.
     """
-    for f in certfile, keyfile, ca_certfile:
-        if not os.access(f, os.R_OK):
+    cert = certs['cert']
+    key = certs['key']
+    ca_cert = certs['ca_cert']
+    peer_ca_cert = certs['peer_ca_cert']
+    for f in cert, key, ca_cert, peer_ca_cert:
+        if f and not os.access(f, os.R_OK):
             print "%s does not exist or is not readable." % f
             os._exit(1)
 
-    ctx = SSL.Context('sslv3')
-    ctx.load_cert(certfile, keyfile)
-    ctx.load_client_ca(ca_certfile)
-    ctx.load_verify_info(ca_certfile)
+    ctx = SSL.Context('sslv3')   # SSLv3 only
+    ctx.load_cert(cert, key)
+    ctx.load_client_ca(ca_cert)
+    ctx.load_verify_info(peer_ca_cert)
     ctx.set_allow_unknown_ca(False)
     verify = SSL.verify_peer | SSL.verify_fail_if_no_peer_cert
     ctx.set_verify(verify, 10)
@@ -44,3 +48,19 @@
     ctx.set_info_callback(quietCallback)
     return ctx
     
+
+class QuietSSLServer(SSL.SSLServer):
+    def handle_request(self):
+        request = None
+        client_address = None
+        try:
+            request, client_address = self.get_request()
+            if self.verify_request(request, client_address):
+                self.process_request(request, client_address)
+        except SSL.SSLError, e:
+            if e.args[0] == "no certificate returned":
+                pass
+            elif e.args[0] == "wrong version number":
+                pass
+            else:
+                self.handle_error(request, client_address)


Index: SSLXMLRPCServerProxy.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/common/SSLXMLRPCServerProxy.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SSLXMLRPCServerProxy.py	13 Jun 2005 05:12:09 -0000	1.2
+++ SSLXMLRPCServerProxy.py	14 Jun 2005 01:16:30 -0000	1.3
@@ -21,7 +21,7 @@
 import SSLCommon
 
 class SSLXMLRPCServerProxy(Server):
-    def __init__(self, certfile, keyfile, ca_certfile, uri):
-        self.ctx = SSLCommon.getSSLContext(certfile, keyfile, ca_certfile)
+    def __init__(self, certs, uri):
+        self.ctx = SSLCommon.getSSLContext(certs)
         Server.__init__(self, uri, SSL_Transport(ssl_context=self.ctx))
 


Index: SimpleHTTPSServer.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/common/SimpleHTTPSServer.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- SimpleHTTPSServer.py	13 Jun 2005 05:12:09 -0000	1.3
+++ SimpleHTTPSServer.py	14 Jun 2005 01:16:30 -0000	1.4
@@ -73,11 +73,11 @@
 
 class ThreadingHTTPSServer(ThreadingSSLServer):
 
-    def __init__(self, certfile, keyfile, ca_certfile, server_addr, http_dir):
+    def __init__(self, certs, server_addr, http_dir):
         self.allow_reuse_address = 1
         self.http_dir = http_dir
 
-        self.ctx = SSLCommon.getSSLContext(certfile, keyfile, ca_certfile)
+        self.ctx = SSLCommon.getSSLContext(certs)
         ThreadingSSLServer.__init__(self, server_addr, HttpRequestHandler, self.ctx)
 
         self.server_name = server_addr[0]
@@ -91,8 +91,8 @@
 
 class SimpleHTTPSServer(threading.Thread):
 
-    def __init__(self, certfile, keyfile, ca_certfile, addr, http_dir):
-        self._server = ThreadingHTTPSServer(certfile, keyfile, ca_certfile, addr, http_dir)
+    def __init__(self, certs, addr, http_dir):
+        self._server = ThreadingHTTPSServer(certs, addr, http_dir)
         threading.Thread.__init__(self)
 
     def run(self):


Index: SimpleSSLXMLRPCServer.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/common/SimpleSSLXMLRPCServer.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SimpleSSLXMLRPCServer.py	13 Jun 2005 05:12:09 -0000	1.2
+++ SimpleSSLXMLRPCServer.py	14 Jun 2005 01:16:30 -0000	1.3
@@ -17,34 +17,141 @@
 
 import os, sys
 from M2Crypto import SSL
-from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler, SimpleXMLRPCDispatcher
+import xmlrpclib
+import SimpleXMLRPCServer
 import SSLCommon
 
 
-class VerifiableSimpleXMLRPCRequestHander(SimpleXMLRPCRequestHandler):
+class AuthedSimpleXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
     def do_POST(self):
         """
-        Override request handling to provide the server a chance to verify the client
+        See documentation on SimpleXMLRPCRequestHandler.  Modifications
+        include the ability to pass through local request-specific data,
+        and removal of code to deal with historical _dispatch methods.
         """
-        accept = True
-        if self.server.verify_callback:
-            accept = self.server.verify_callback(self.request)
-        if accept:
-            SimpleXMLRPCRequestHandler.do_POST(self)
+
+        if 0:
+            # ONLY FOR DEBUGGING, so we can see exceptions on the console
+            # get arguments
+            data = self.rfile.read(int(self.headers["content-length"]))
+            authinfo = self.server.get_authinfo(self.request, self.client_address)
+            response = self.server._marshaled_dispatch(data, authinfo)
+
+            # got a valid XML RPC response
+            self.send_response(200)
+            self.send_header("Content-type", "text/xml")
+            self.send_header("Content-length", str(len(response)))
+            self.end_headers()
+            self.wfile.write(response)
+
+            # shut down the connection
+            self.wfile.flush()
+            self.connection.shutdown(1)
         else:
-            self.server.send_error(403, 'You are not authorized to access this resource.')
+            try:
+                # get arguments
+                data = self.rfile.read(int(self.headers["content-length"]))
+                authinfo = self.server.get_authinfo(self.request, self.client_address)
+                response = self.server._marshaled_dispatch(data, authinfo)
+            except: # This should only happen if the module is buggy
+                # internal error, report as HTTP server error
+                self.send_response(500)
+                self.end_headers()
+            else:
+                # got a valid XML RPC response
+                self.send_response(200)
+                self.send_header("Content-type", "text/xml")
+                self.send_header("Content-length", str(len(response)))
+                self.end_headers()
+                self.wfile.write(response)
+
+                # shut down the connection
+                self.wfile.flush()
+                self.connection.shutdown(1)
 
 
-class SimpleSSLXMLRPCServer(SSL.SSLServer, SimpleXMLRPCServer):
+class SimpleSSLXMLRPCServer(SSLCommon.QuietSSLServer, SimpleXMLRPCServer.SimpleXMLRPCServer):
     """
     An extension of SimpleXMLRPCServer that allows SSL handling.
     """
-    def __init__(self, certfile, keyfile, ca_certfile, address, verify_callback=None):
+    def __init__(self, certs, address, authinfo_callback=None):
         self.allow_reuse_address = 1
         self.logRequests = 0
-        self.verify_callback = verify_callback
+        self.authinfo_callback = authinfo_callback
+
+        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self)
+
+        ctx = SSLCommon.getSSLContext(certs)
+        SSLCommon.QuietSSLServer.__init__(self, address, AuthedSimpleXMLRPCRequestHandler, ctx) 
+
+    def get_authinfo(self, request, client_address):
+        if self.authinfo_callback:
+            return self.authinfo_callback(request, client_address)
+        return None
 
-        ctx = SSLCommon.getSSLContext(certfile, keyfile, ca_certfile)
-        SSL.SSLServer.__init__(self, address, VerifiableSimpleXMLRPCRequestHander, ctx) 
+    def verify_request(self, request, client_address):
+        """
+        Allow ourselves a chance to verify the client
+        """
+        if self.authinfo_callback:
+            authinfo = self.get_authinfo(request, client_address)
+            if authinfo:
+                return True
+            return False
 
-        SimpleXMLRPCDispatcher.__init__(self)
+        # Allow all requests if there's no authinfo_callback
+        return True
+
+    def _marshaled_dispatch(self, data, authinfo):
+        """
+        Allow the ability to pass request-dependent objects into
+        the handler instance.
+        """
+        params, method = xmlrpclib.loads(data)
+
+        # generate response
+        try:
+            response = self._dispatch(authinfo, method, params)
+            # wrap response in a singleton tuple
+            response = (response,)
+            response = xmlrpclib.dumps(response, methodresponse=1)
+        except xmlrpclib.Fault, fault:
+            response = xmlrpclib.dumps(fault)
+        except:
+            # report exception back to server
+            response = xmlrpclib.dumps(
+                xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value))
+                )
+
+        return response
+
+    def _dispatch(self, authinfo, method, params):
+        """
+        Allow the ability to pass request-dependent objects into
+        the handler instance.
+        """
+        func = None
+        try:
+            # check to see if a matching function has been registered
+            func = self.funcs[method]
+        except KeyError:
+            if self.instance is not None:
+                # call instance method directly
+                try:
+                    func = SimpleXMLRPCServer.resolve_dotted_attribute(
+                        self.instance,
+                        method,
+                        self.allow_dotted_names
+                        )
+                except AttributeError:
+                    pass
+
+        if func is not None:
+            # Only pass authinfo into Instance if we have are set
+            # up to get authinfo
+            if self.authinfo_callback is not None:
+                return func(authinfo, *params)
+            else:
+                return func(*params)
+        else:
+            raise Exception('method "%s" is not supported' % method)




More information about the fedora-extras-commits mailing list