Source code for PyFoam.Infrastructure.ServerBase

#  ICE Revision: $Id$
"""Basis for the XMLRPC-Servers in PyFoam

Based on 15.5 in "Python Cookbook" for faster restarting

SSL-handling lifted from
http://blogs.blumetech.com/blumetechs-tech-blog/2011/06/python-xmlrpc-server-with-ssl-and-authentication.html

"""

from PyFoam.ThirdParty.six import PY3
from PyFoam.Error import warning
from PyFoam import configuration as config
import PyFoam.Infrastructure.Authentication as auth
from PyFoam.FoamInformation import getUserName

if PY3:
    try:
        import pydoc
    except ModuleNotFoundError:
        # Workaround for this issue
        # https://discourse.paraview.org/t/issue-with-pv-5-10-with-the-python-shell-and-help/8859
        pass
    from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCDispatcher, SimpleXMLRPCRequestHandler
    from xmlrpc.client import ServerProxy
else:
    from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCDispatcher, SimpleXMLRPCRequestHandler
    from xmlrpclib import ServerProxy

import socket
from os import path

[docs]class ServerBase(SimpleXMLRPCServer): """The Base class for the servers""" def __init__(self,addr,useSSL=False,logRequests=False, allow_none=False, encoding=None): """:param addr: the (server address,port)-tuple) :param logRequests: patched thru to the base class""" certfile=config().get("Network","personalSSLCertificate") keyfile=config().get("Network","privateSSLKey") self.useSSL=useSSL if self.useSSL and not path.exists(certfile): warning("No certficate file",certfile, "exists. Therefor no SSL-connection for the FoamServer possible\n", "To generate a private key:\n", (" openssl genrsa -out %s 2048" % keyfile), "\nThen generate the cerificate that is valid for 3 years with \n", (" openssl req -new -x509 -key %s -out %s -days 1095" % (keyfile,certfile))) self.useSSL=False self.authOK=True if self.useSSL: try: import ssl if PY3: import socketserver else: import SocketServer as socketserver import socket except ImportError: warning("Problem with the imports. Dropping SSL-support") self.useSSL=False if self.useSSL: self.logRequests = logRequests SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) class VerifyingRequestHandler(SimpleXMLRPCRequestHandler): ''' Request Handler that verifies username and security token to XML RPC server in HTTP URL sent by client. ''' # this is the method we must override def parse_request(self): # first, call the original implementation which returns # True if all OK so far if SimpleXMLRPCRequestHandler.parse_request(self): # next we authenticate if self.authenticate(self.headers): return True else: # if authentication fails, tell the client self.send_error(401, 'Authentication failed') return False def authenticate(self, headers): from base64 import b64decode # Confirm that Authorization header is set to Basic authHeader=headers.get('Authorization') if authHeader is None: return True (basic, _, encoded) = authHeader.partition(' ') assert basic == 'Basic', 'Only basic authentication supported' # Encoded portion of the header is a string # Need to convert to bytestring encodedByteString = encoded.encode() # Decode Base64 byte String to a decoded Byte String decodedBytes = b64decode(encodedByteString) # Convert from byte string to a regular String decodedString = decodedBytes.decode() # Get the username and password from the string (username, _, password) = decodedString.partition(':') # Check that username and password match internal global dictionary self.server.authOK=auth.checkAuthentication(username,password) return True # Override the normal socket methods with an SSL socket socketserver.BaseServer.__init__(self, addr, VerifyingRequestHandler) try: self.socket = ssl.wrap_socket( socket.socket(self.address_family, self.socket_type), server_side=True, keyfile=keyfile, certfile=certfile, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23, ) self.server_bind() self.server_activate() except socket.error as e: warning("Socket error",e) raise e else: SimpleXMLRPCServer.__init__(self,addr,logRequests=logRequests)
[docs] def server_bind(self): """Should allow a fast restart after the server was killed""" self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) SimpleXMLRPCServer.server_bind(self)
[docs] def verify_request(self,request,client_addr): """To be overriden later""" return True
[docs]def getServerProxy(host,port,useSSL=None): """Get a proxy to a server. If the useSSL-parameter is unset then it tries to automatically get the most secure connection""" if useSSL is None: useSSL=[True,False] else: useSSL=[useSSL] for s in useSSL: if s: try: import ssl import socket import uuid context=None if config().getboolean("Network","allowSelfSignedSSL"): try: context=ssl._create_unverified_context() except AttributeError: pass try: user=getUserName() # user="nix" challenge=auth.createChallengeString(str(uuid.uuid4())) try: server=ServerProxy("https://%s:%s@%s:%d" % (user,challenge, host,port), context=context) except TypeError: server=ServerProxy("https://%s:%s@%s:%d" % (user,challenge, host,port)) server.system.listMethods() # provoke a socket-error return server except socket.error as reason: if hasattr(reason,"reason"): if reason.reason=="CERTIFICATE_VERIFY_FAILED" and context is None: warning("Can't verify certificate. Set setting 'network'/'allowSelfSignedSSL' to True. This may be insecure") raise reason except ImportError: warning("Problem with the imports. Dropping SSL-support") else: return ServerProxy("http://%s:%d" % (host,port)) warning("Could not connect to",host,"at",port) return None
# Should work with Python3 and Python2