We can create a subclass of SimpleXMLRPCServer and override its request handler so that when a request comes, it is verified against given login credentials.
Listing 7.3a gives the code for running an XML-RPC server with a basic HTTP authentication, as shown:
#!/usr/bin/env python # Python Network Programming Cookbook, Second Edition -- Chapter - 7 # This program is optimized for Python 3.5.2. # To make it work with Python 2.7.12: # Follow through the code inline for some changes. # It may run on any other version with/without modifications. import argparse import xmlrpc # Comment out the above line and uncomment the below line for Python 2.x. #import xmlrpclib from base64 import b64decode from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler # Comment out the above line and uncomment the below line for Python 2.x. #from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler class SecureXMLRPCServer(SimpleXMLRPCServer): def __init__(self, host, port, username, password, *args, **kargs): self.username = username self.password = password # authenticate method is called from inner class class VerifyingRequestHandler(SimpleXMLRPCRequestHandler): # method to override def parse_request(request): if SimpleXMLRPCRequestHandler.
parse_request(request): # authenticate if self.authenticate(request.headers): return True else: # if authentication fails return 401 request.send_error(401, 'Authentication
failed, Try agin.') return False # initialize SimpleXMLRPCServer.__init__(self, (host, port),
requestHandler=VerifyingRequestHandler, *args, **kargs) def authenticate(self, headers): headers = headers.get('Authorization').split() basic, encoded = headers[0], headers[1] if basic != 'Basic': print ('Only basic authentication supported') return False secret = b64decode(encoded).split(b':') username, password = secret[0].decode("utf-8"),
secret[1].decode("utf-8") return True if (username == self.username and
password == self.password) else False def run_server(host, port, username, password): server = SecureXMLRPCServer(host, port, username, password) # simple test function def echo(msg): """Reply client in uppser case """ reply = msg.upper() print ("Client said: %s. So we echo that in uppercase: %s"
%(msg, reply)) return reply server.register_function(echo, 'echo') print ("Running a HTTP auth enabled XMLRPC server
on %s:%s..." %(host, port)) server.serve_forever() if __name__ == '__main__': parser = argparse.ArgumentParser(description='Multithreaded
multicall XMLRPC Server/Proxy') parser.add_argument('--host', action="store", dest="host",
default='localhost') parser.add_argument('--port', action="store", dest="port",
default=8000, type=int) parser.add_argument('--username', action="store",
dest="username", default='user') parser.add_argument('--password', action="store",
dest="password", default='pass') # parse arguments given_args = parser.parse_args() host, port = given_args.host, given_args.port username, password = given_args.username, given_args.password run_server(host, port, username, password)
If this server is run, then the following output can be seen by default:
$ python 17_3a_xmlrpc_server_with_http_auth.py --port=8000 Running a HTTP auth enabled XMLRPC server on localhost:8000... Client said: hello server.... So we echo that in uppercase: HELLO SERVER... 127.0.0.1 - - [13/Jun/2017 23:32:14] "POST /RPC2 HTTP/1.1" 200 -
Now, let us create a simple client proxy and use the same login credentials as used with the server.
Listing 7.3b gives the code for the XML-RPC client, as shown:
#!/usr/bin/env python # Python Network Programming Cookbook, Second Edition -- Chapter - 7 # This program is optimized for Python 3.5.2. # To make it work with Python 2.7.12: # Follow through the code inline for some changes. # It may run on any other version with/without modifications. import argparse import xmlrpc # Comment out the above line and uncomment the below line for Python 2.x. #import xmlrpclib from xmlrpc.server import SimpleXMLRPCServer # Comment out the above line for Python 2.x. def run_client(host, port, username, password): server = xmlrpc.client.ServerProxy('http://%s:%s@%s:%s'
%(username, password, host, port, )) # Comment out the above line and uncomment the
below line for Python 2.x. #server = xmlrpclib.ServerProxy('http://%s:%s@%s:%s'
%(username, password, host, port, )) msg = "hello server..." print ("Sending message to server: %s " %msg) print ("Got reply: %s" %server.echo(msg)) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Multithreaded
multicall XMLRPC Server/Proxy') parser.add_argument('--host', action="store", dest="host",
default='localhost') parser.add_argument('--port', action="store", dest="port",
default=8000, type=int) parser.add_argument('--username', action="store",
dest="username", default='user') parser.add_argument('--password', action="store",
dest="password", default='pass') # parse arguments given_args = parser.parse_args() host, port = given_args.host, given_args.port username, password = given_args.username, given_args.password run_client(host, port, username, password)
If you run the client, then it shows the following output:
$ python 17_3b_xmprpc_client.py --port=8000 Sending message to server: hello server... Got reply: HELLO SERVER...
The following screenshot shows the server and client: