Running an XML-RPC server with a basic HTTP authentication

Sometimes, you may need to implement authentication with an XML-RPC server. This recipe presents an example of a basic HTTP authentication with an XML-RPC server.

How to do it...

We can create a subclass of SimpleXMLRPCServer and override its request handler so that when a request comes, it is verified against a given login credentials.

Listing 8.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 -- Chapter – 8
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.

import argparse
import xmlrpclib
from base64 import b64decode
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 
ZZZ')
            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(':')
    username, password = secret[0], secret[1]
  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  upper 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 8_3a_xmlrpc_server_with_http_auth.py 
Running a HTTP auth enabled XMLRPC server on localhost:8000...
Client said: hello server.... So we echo that in uppercase: HELLO 
SERVER...
localhost - - [27/Sep/2013 12:08:57] "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 8.3b gives the code for the XML-RPC Client, as shown:

#!/usr/bin/env python
# Python Network Programming Cookbook -- Chapter – 8
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.

import argparse
import xmlrpclib

def run_client(host, port, username, password):
  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 8_3b_xmprpc_client.py 
Sending message to server: hello server...  
Got reply: HELLO SERVER...

How it works...

In the server script, the SecureXMLRPCServer subclass is created by inheriting from SimpleXMLRPCServer. In this subclass' initialization code, we created the VerifyingRequestHandler class that actually intercepts the request and does the basic authentication using the authenticate() method.

In the authenticate() method, the HTTP request is passed as an argument. This method checks the presence of the value of Authorization. If its value is set to Basic, it then decodes the encoded password with the b64decode() function from the base64 standard module. After extracting the username and password, it then checks that with the server's given credentials set up initially.

In the run_server() function, a simple echo() subfunction is defined and registered with the SecureXMLRPCServer instance.

In the client script, run_client() simply takes the server address and login credentials and passes them to the ServerProxy() instance. It then sends a single line message via the echo() method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.17.154