Python 2 SSL xmlrpc server with session handling -


i searched quite long time find implementation mentioned in title, not find anything. habe implemented myself. post code here other people might find usefull.

if finds mistakes willing fix them. here code:

#!/usr/bin/env python # -*- coding: utf-8 -*-  import os  hashlib import sha256 import hmac import uuid import time datetime import datetime  import ssl import socket import socketserver import basehttpserver import simplehttpserver import simplexmlrpcserver xmlrpclib import fault   # configure below listen_host = '127.0.0.1' # should not use '' here, unless have real fqdn. listen_port = 2048  keyfile  = os.path.join('certs', 'server.key')  # replace pem formatted key file certfile = os.path.join('certs', 'server.crt')  # replace pem formatted certificate file  # 2011/01/01 in utc epoch = 1293840000  def require_login(decorated_function):     """     decorator prevents access action if not logged in.      if login check failed xmlrpclib.fault exception raised     """      def wrapper(self, session_id, *args, **kwargs):         """ decorated methods must have self , session_id """          # check if valid session available         if not self.sessions.has_key(session_id):             self._clear_expired_sessions() # clean session dict             raise fault("session id invalid", "call login(user, pass) aquire valid session")          last_visit = self.sessions[session_id]["last_visit"]          # check if timestamp valid         if is_timestamp_expired(last_visit):             self._clear_expired_sessions() # clean session dict             raise fault("session id expired", "call login(user, pass) aquire valid session")          self.sessions[session_id]["last_visit"] = get_timestamp()         return decorated_function(self, session_id, *args, **kwargs)      return wrapper  def timestamp_to_datetime(timestamp):     """     convert timestamp 'get_timestamp' datetime object      args:         ts: integer timestamp      returns:         datetime object     """      return datetime.utcfromtimestamp(timestamp + epoch)  def get_timestamp():     """     returns seconds since 1/1/2011.      returns:         integer timestamp     """      return int(time.time() - epoch)  def is_timestamp_expired(timestamp, max_age = 2700): # maxage in seconds (here: 2700 = 45 min)     """     checks if given timestamp expired      args:         timestamp: integer timestamp         max_age  : maximal allowd age of timestamp in seconds      returns:         true if timestamp expired or false if timestamp valid     """      age = get_timestamp() - timestamp     if age > max_age:         return true     return false   class securexmlrpcserver(basehttpserver.httpserver,simplexmlrpcserver.simplexmlrpcdispatcher):     def __init__(self, server_address, handlerclass, logrequests=true, allow_none=false):         """         secure xml-rpc server.         it similar simplexmlrpcserver uses https transporting xml data.         """         self.logrequests = logrequests         self.allow_none  = true          simplexmlrpcserver.simplexmlrpcdispatcher.__init__(self, self.allow_none, none)         socketserver.baseserver.__init__(self, server_address, handlerclass)          self.socket = ssl.wrap_socket(socket.socket(), server_side=true, certfile=certfile,                             keyfile=keyfile, ssl_version=ssl.protocol_sslv23)          self.server_bind()         self.server_activate()  class securexmlrpcrequesthandler(simplexmlrpcserver.simplexmlrpcrequesthandler):     """     secure xml-rpc request handler class.     it similar simplexmlrpcrequesthandler uses https transporting xml data.     """      def setup(self):         self.connection = self.request         self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)      def do_post(self):         """handles https post request.          copied out simplexmlrpcserver.py , modified shutdown socket cleanly.         """          try:             # arguments             data = self.rfile.read(int(self.headers["content-length"]))             # in previous versions of simplexmlrpcserver, _dispatch             # overridden in class, instead of in             # simplexmlrpcdispatcher. maintain backwards compatibility,             # check see if subclass implements _dispatch , dispatch             # using method if present.             response = self.server._marshaled_dispatch(                     data, getattr(self, '_dispatch', none)                 )         except exception e: # should happen if module buggy             # internal error, report http server error             self.send_response(500)             self.end_headers()         else:             # got 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 connection             self.wfile.flush()              #modified of http://docs.python.org/library/ssl.html             self.connection.shutdown(socket.shut_rdwr)             self.connection.close()  class xmlrpchandler:     """     example implementation login handling     """      def __init__(self):         self.users       = {"test": "test", "foo": "bar"} # replace own authentication         self.sessions    = dict()         self.session_key = os.urandom(32)      def _find_session_by_username(self, username):         """         try find valid session username.          args:             username: username search          returns:             if session found returned otherwise none returned         """          session in self.sessions.itervalues():             if session["username"] == username:                 return session      def _invalidate_session_id(self, session_id):         """         remove session.          args:             session_id: session should removed         """          try:             del self.sessions[session_id]         except keyerror:             pass      def _clear_expired_sessions(self):         """         clear expired sessions         """          session_id in self.sessions.keys():             last_visit = self.sessions[session_id]["last_visit"]             if is_timestamp_expired(last_visit):                 self._invalidate_session_id(session_id)      def _generate_session_id(self, username):         """         generates new session id          returns:             new unique session_id         """          return hmac.new(self.session_key, username + str(uuid.uuid4()), sha256).hexdigest()      def login(self, username, password):         """         handle login procedure. if login successfull session id returned         otherwise xmlrpclib.fault exception raised.          args:             username: username             password: password          returns:             valid session id          raises:             xmlrpclib.fault exception raised         """         # check username , password         if self.users.has_key(username):             if self.users[username] == password:                  # check if session username exists                 session = self._find_session_by_username(username)                 if session:                     if is_timestamp_expired(session["last_visit"]):                         self._invalidate_session_id(session["session_id"])                     else:                         return session["session_id"]                  # generate session id , save                 session_id = self._generate_session_id(username)                 self.sessions[session_id] = {"username"  : username,                                              "session_id": session_id,                                              "last_visit": get_timestamp()}                  return session_id          raise fault("unknown username or password", "please check username , password")      @require_login     def hello(self, session_id, name):         """         example method requires login         """         if not name:             raise fault("unknown recipient", "i need greet!")         return "hello, %s!" % name  def test():     server_address = (listen_host, listen_port)     server = securexmlrpcserver(server_address, securexmlrpcrequesthandler)     server.register_introspection_functions()     server.register_instance(xmlrpchandler())      sa = server.socket.getsockname()     print "serving https on", sa[0], "port", sa[1]     server.serve_forever()  if __name__ == "__main__":     test()      """ testcode example client """     import time     def continue_xmlrpc_call(func, *args):         try:             ret = func(*args)             print ret             return ret         except xmlrpclib.fault e:             print e      server = xmlrpclib.serverproxy("https://localhost:2048")      print server     print server.system.listmethods()     sid = continue_xmlrpc_call(server.login, "foo", "bar")     sid = continue_xmlrpc_call(server.login, "foo", "bar")     continue_xmlrpc_call(server.hello, sid, "world")     time.sleep(2)     continue_xmlrpc_call(server.hello, sid, "invalid")     continue_xmlrpc_call(server.hello, "193", "") 

@philipp nice setup. find loginfunction bit weird. seems knows username logged in can session id, , stuff. looks better if move session code existing session id returned if correct username , password entered.

    # check username , password     if self.users.has_key(username):         if self.users[username] == password:             # check if session username exists             session = self._find_session_by_username(username)             if session:                 if is_timestamp_expired(session["last_visit"]):                     self._invalidate_session_id(session["session_id"])                 else:                     return session["session_id"]              # generate session id , save             session_id = self._generate_session_id(username)             self.sessions[session_id] = {"username"  : username,                                          "session_id": session_id,                                          "last_visit": get_timestamp()}              return session_id 

Comments

Popular posts from this blog

javascript - DIV "hiding" when changing dropdown value -

Does Firefox offer AppleScript support to get URL of windows? -

android - How to install packaged app on Firefox for mobile? -