def __init__(self, url_name='root', restrict_introspection=False): self.url_name = url_name self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher() self.xmlrpcdispatcher = XMLRPCDispatcher() if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe)
def __init__(self, url='', apps=[], restrict_introspection=False): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher() self.xmlrpcdispatcher = XMLRPCDispatcher() if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) self.register_rpcmethods(apps)
def __init__(self, url='', apps=[], restrict_introspection=False, restrict_ootb_auth=True, json_encoder=None): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher(json_encoder) if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) if not restrict_ootb_auth: self.register_method(self.system_login) self.register_method(self.system_logout) self.register_rpcmethods(apps)
def __init__(self, url="", apps=[], restrict_introspection=False): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher() if int(version[0]) < 3 and int(version[1]) < 5: # this is for python 2.4 and below self.xmlrpcdispatcher = SimpleXMLRPCDispatcher() else: # python 2.5+ requires different parameters self.xmlrpcdispatcher = SimpleXMLRPCDispatcher(allow_none=False, encoding=None) if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) self.register_rpcmethods(apps)
class RPCDispatcher: ''' Dispatches method calls to either the xmlrpc or jsonrpc dispatcher ''' def __init__(self, url='', apps=[], restrict_introspection=False): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher() self.xmlrpcdispatcher = XMLRPCDispatcher() if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) self.register_rpcmethods(apps) @rpcmethod(name='system.describe', signature=['struct']) def system_describe(self): ''' Returns a simple method description of the methods supported ''' description = {} description['serviceType'] = 'RPC4Django JSONRPC+XMLRPC' description['serviceURL'] = self.url, description['methods'] = [{'name': method.name, 'summary': method.help, 'params': method.get_params(), 'return': method.get_returnvalue()} \ for method in self.rpcmethods] return description @rpcmethod(name='system.listMethods', signature=['array']) def system_listmethods(self): ''' Returns a list of supported methods ''' methods = [method.name for method in self.rpcmethods] methods.sort() return methods @rpcmethod(name='system.methodHelp', signature=['string', 'string']) def system_methodhelp(self, method_name): ''' Returns documentation for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.help # this differs from what implementation in SimpleXMLRPCServer does # this will report via a fault or error while SimpleXMLRPCServer # just returns an empty string raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) @rpcmethod(name='system.methodSignature', signature=['array', 'string']) def system_methodsignature(self, method_name): ''' Returns the signature for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.signature raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) def register_rpcmethods(self, apps): ''' Scans the installed apps for methods with the rpcmethod decorator Adds these methods to the list of methods callable via RPC ''' for appname in apps: # check each app for any rpcmethods app = __import__(appname, globals(), locals(), ['*']) for obj in dir(app): method = getattr(app, obj) if callable(method) and \ hasattr(method, 'is_rpcmethod'): # if this method is callable and it has the rpcmethod # decorator, add it to the dispatcher self.register_method(method, method.external_name) elif isinstance(method, types.ModuleType): # if this is not a method and instead a sub-module, # scan the module for methods with @rpcmethod try: self.register_rpcmethods(["%s.%s" % (appname, obj)]) except ImportError: pass def jsondispatch(self, raw_post_data, **kwargs): ''' Sends the post data to a jsonrpc processor ''' return self.jsonrpcdispatcher.dispatch(raw_post_data, **kwargs) def xmldispatch(self, raw_post_data, **kwargs): ''' Sends the post data to an xmlrpc processor ''' return self.xmlrpcdispatcher.dispatch(raw_post_data, **kwargs) def get_method_name(self, raw_post_data, request_format='xml'): ''' Gets the name of the method to be called given the post data and the format of the data ''' if request_format == 'xml': # xmlrpclib.loads could throw an exception, but this is fine # since _marshaled_dispatch would throw the same thing try: params, method = xmlrpclib.loads(raw_post_data) return method except Fault: return None else: try: # attempt to do a json decode on the data jsondict = json.loads(raw_post_data) if not isinstance(jsondict, dict) or 'method' not in jsondict: return None else: return jsondict['method'] except ValueError: return None def list_methods(self): ''' Returns a list of RPCMethod objects supported by the server ''' return self.rpcmethods def register_method(self, method, name=None, signature=None, helpmsg=None): ''' Registers a method with the rpc server ''' meth = RPCMethod(method, name, signature, helpmsg) if meth.name not in self.system_listmethods(): self.xmlrpcdispatcher.register_function(method, meth.name) self.jsonrpcdispatcher.register_function(method, meth.name) self.rpcmethods.append(meth)
class RPCDispatcher: ''' Keeps track of the methods available to be called and then dispatches method calls to either the :class:`XMLRPCDispatcher <rpc4django.xmlrpcdispatcher.XMLRPCDispatcher>` or the :class:`JSONRPCDispatcher <rpc4django.jsonrpcdispatcher.JSONRPCDispatcher>` Disables RPC introspection methods (eg. ``system.list_methods()`` if ``restrict_introspection`` is set to ``True``. Disables out of the box authentication if ``restrict_ootb_auth`` is ``True``. **Attributes** ``url`` The URL that handles RPC requests (eg. ``/RPC2``) This is needed by ``system.describe``. ``rpcmethods`` A list of :class:`RPCMethod<rpc4django.rpcdispatcher.RPCMethod>` instances available to be called by the dispatcher ``xmlrpcdispatcher`` An instance of :class:`XMLRPCDispatcher <rpc4django.xmlrpcdispatcher.XMLRPCDispatcher>` where XMLRPC calls are dispatched to using :meth:`xmldispatch` ``jsonrpcdispatcher`` An instance of :class:`JSONRPCDispatcher <rpc4django.jsonrpcdispatcher.JSONRPCDispatcher>` where JSONRPC calls are dispatched to using :meth:`jsondispatch` ''' def __init__(self, url='', apps=[], restrict_introspection=False, restrict_ootb_auth=True, json_encoder=None): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher(json_encoder) self.xmlrpcdispatcher = XMLRPCDispatcher() if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) if not restrict_ootb_auth: self.register_method(self.system_login) self.register_method(self.system_logout) self.register_rpcmethods(apps) @rpcmethod(name='system.describe', signature=['struct']) def system_describe(self): ''' Returns a simple method description of the methods supported ''' description = {} description['serviceType'] = 'RPC4Django JSONRPC+XMLRPC' description['serviceURL'] = self.url, description['methods'] = [{'name': method.name, 'summary': method.help, 'params': method.get_params(), 'return': method.get_returnvalue()} \ for method in self.rpcmethods] return description @rpcmethod(name='system.listMethods', signature=['array']) def system_listmethods(self): ''' Returns a list of supported methods ''' methods = [method.name for method in self.rpcmethods] methods.sort() return methods @rpcmethod(name='system.methodHelp', signature=['string', 'string']) def system_methodhelp(self, method_name): ''' Returns documentation for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.help # this differs from what implementation in SimpleXMLRPCServer does # this will report via a fault or error while SimpleXMLRPCServer # just returns an empty string raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) @rpcmethod(name='system.methodSignature', signature=['array', 'string']) def system_methodsignature(self, method_name): ''' Returns the signature for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.signature raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) @rpcmethod(name='system.login', signature=['boolean', 'string', 'string']) def system_login(self, username, password, **kwargs): ''' Authorizes a user to enable sending protected RPC requests ''' request = kwargs.get('request', None) user = authenticate(username=username, password=password) if user is not None and request is not None: if user.is_active: login(request, user) return True return False @rpcmethod(name='system.logout', signature=['boolean']) def system_logout(self, **kwargs): ''' Deauthorizes a user ''' request = kwargs.get('request', None) if request is not None: logout(request) return True return False def register_rpcmethods(self, apps): ''' Scans the installed apps for methods with the rpcmethod decorator Adds these methods to the list of methods callable via RPC ''' for appname in apps: # check each app for any rpcmethods try: app = __import__(appname, globals(), locals(), ['*']) except (ImportError, ValueError): # import throws ValueError on empty "name" continue for obj in dir(app): method = getattr(app, obj) if isinstance(method, xmlrpclib.ServerProxy): continue if callable(method) and \ hasattr(method, 'is_rpcmethod') and \ method.is_rpcmethod == True: # if this method is callable and it has the rpcmethod # decorator, add it to the dispatcher self.register_method(method, method.external_name) elif isinstance(method, types.ModuleType): # if this is not a method and instead a sub-module, # scan the module for methods with @rpcmethod self.register_rpcmethods(["%s.%s" % (appname, obj)]) def jsondispatch(self, raw_post_data, **kwargs): ''' Sends the post data to :meth:`rpc4django.jsonrpcdispatcher.JSONRPCDispatcher.dispatch` ''' return self.jsonrpcdispatcher.dispatch(raw_post_data, **kwargs) def xmldispatch(self, raw_post_data, **kwargs): ''' Sends the post data to :meth:`rpc4django.xmlrpcdispatcher.XMLRPCDispatcher.dispatch` ''' return self.xmlrpcdispatcher.dispatch(raw_post_data, **kwargs) def get_method_name(self, raw_post_data, request_format='xml'): ''' Gets the name of the method to be called given the post data and the format of the data ''' if request_format == 'xml': # xmlrpclib.loads could throw an exception, but this is fine # since _marshaled_dispatch would throw the same thing try: params, method = xmlrpclib.loads(raw_post_data) return method except Exception: return None else: try: # attempt to do a json decode on the data jsondict = json.loads(raw_post_data) if not isinstance(jsondict, dict) or 'method' not in jsondict: return None else: return jsondict['method'] except ValueError: return None def list_methods(self): ''' Returns a list of RPCMethod objects supported by the server ''' return self.rpcmethods def register_method(self, method, name=None, signature=None, helpmsg=None): ''' Instantiates an RPCMethod object and adds it to ``rpcmethods`` so that it can be called by RPC requests **Parameters** ``method`` A callable Python method that the dispatcher will delegate to when requested via RPC ``name`` The name to make the method availabe. ``None`` signifies to use the method's actual name ``signature`` The signature of the method. See :meth:`rpc4django.rpcdispatcher.rpcmethod` ``helpmsg`` The "help" message displayed by introspection functions asking about the method ''' meth = RPCMethod(method, name, signature, helpmsg) if meth.name not in self.system_listmethods(): self.xmlrpcdispatcher.register_function(method, meth.name) self.jsonrpcdispatcher.register_function(method, meth.name) self.rpcmethods.append(meth)
class RPCDispatcher: """ Dispatches method calls to either the xmlrpc or jsonrpc dispatcher """ def __init__(self, url="", apps=[], restrict_introspection=False): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher() if int(version[0]) < 3 and int(version[1]) < 5: # this is for python 2.4 and below self.xmlrpcdispatcher = SimpleXMLRPCDispatcher() else: # python 2.5+ requires different parameters self.xmlrpcdispatcher = SimpleXMLRPCDispatcher(allow_none=False, encoding=None) if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) self.register_rpcmethods(apps) @rpcmethod(name="system.describe", signature=["struct"]) def system_describe(self): """ Returns a simple method description of the methods supported """ description = {} description["serviceType"] = "RPC4Django JSONRPC+XMLRPC" description["serviceURL"] = (self.url,) description["methods"] = [ { "name": method.name, "summary": method.help, "params": method.get_params(), "return": method.get_returnvalue(), } for method in self.rpcmethods ] return description @rpcmethod(name="system.listMethods", signature=["array"]) def system_listmethods(self): """ Returns a list of supported methods """ methods = [method.name for method in self.rpcmethods] methods.sort() return methods @rpcmethod(name="system.methodHelp", signature=["string", "string"]) def system_methodhelp(self, method_name): """ Returns documentation for a specified method """ for method in self.rpcmethods: if method.name == method_name: return method.help # this differs from what implementation in SimpleXMLRPCServer does # this will report via a fault or error while SimpleXMLRPCServer # just returns an empty string raise Fault(APPLICATION_ERROR, "No method found with name: " + str(method_name)) @rpcmethod(name="system.methodSignature", signature=["array", "string"]) def system_methodsignature(self, method_name): """ Returns the signature for a specified method """ for method in self.rpcmethods: if method.name == method_name: return method.signature raise Fault(APPLICATION_ERROR, "No method found with name: " + str(method_name)) def register_rpcmethods(self, apps): """ Scans the installed apps for methods with the rpcmethod decorator Adds these methods to the list of methods callable via RPC """ for appname in apps: # check each app for any rpcmethods app = __import__(appname, globals(), locals(), ["*"]) for obj in dir(app): method = getattr(app, obj) if callable(method) and getattr(method, "is_rpcmethod", False): # if this method is callable and it has the rpcmethod # decorator, add it to the dispatcher self.register_method(method, method.external_name) def jsondispatch(self, raw_post_data): """ Sends the post data to a jsonrpc processor """ return self.jsonrpcdispatcher.dispatch(raw_post_data) def xmldispatch(self, raw_post_data): """ Sends the post data to an xmlrpc processor """ return self.xmlrpcdispatcher._marshaled_dispatch(raw_post_data) def get_method_name(self, raw_post_data, request_format="xml"): """ Gets the name of the method to be called given the post data and the format of the data """ if request_format == "xml": # xmlrpclib.loads could throw an exception, but this is fine # since _marshaled_dispatch would throw the same thing try: params, method = xmlrpclib.loads(raw_post_data) return method except Fault: return None else: try: # attempt to do a json decode on the data jsondict = json.loads(raw_post_data) if not isinstance(jsondict, dict) or "method" not in jsondict: return None else: return jsondict["method"] except ValueError: return None def list_methods(self): """ Returns a list of RPCMethod objects supported by the server """ return self.rpcmethods def register_method(self, method, name=None, signature=None, helpmsg=None): """ Registers a method with the rpc server """ meth = RPCMethod(method, name, signature, helpmsg) self.xmlrpcdispatcher.register_function(method, meth.name) self.jsonrpcdispatcher.register_function(method, meth.name) self.rpcmethods.append(meth)
class RPCDispatcher: ''' Keeps track of the methods available to be called and then dispatches method calls to :class:`JSONRPCDispatcher <rpc4django.jsonrpcdispatcher.JSONRPCDispatcher>` Disables RPC introspection methods (eg. ``system.list_methods()`` if ``restrict_introspection`` is set to ``True``. Disables out of the box authentication if ``restrict_ootb_auth`` is ``True``. **Attributes** ``url`` The URL that handles RPC requests (eg. ``/RPC2``) This is needed by ``system.describe``. ``rpcmethods`` A list of :class:`RPCMethod<rpc4django.rpcdispatcher.RPCMethod>` instances available to be called by the dispatcher ``jsonrpcdispatcher`` An instance of :class:`JSONRPCDispatcher <rpc4django.jsonrpcdispatcher.JSONRPCDispatcher>` where JSONRPC calls are dispatched to using :meth:`jsondispatch` ''' def __init__(self, url='', apps=[], restrict_introspection=False, restrict_ootb_auth=True, json_encoder=None): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher(json_encoder) if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) if not restrict_ootb_auth: self.register_method(self.system_login) self.register_method(self.system_logout) self.register_rpcmethods(apps) def check_request_permission(self, request): ''' Checks whether this user has permission to call a particular method This method does not check method call validity. That is done later **Parameters** - ``request`` - a django HttpRequest object Returns ``False`` if permission is denied and ``True`` otherwise ''' methods = self.list_methods() method_name = self.get_method_name(request.raw_post_data) # TODO: put it to json dispatcher for method in methods: if method.name != method_name: continue if method.authentication: method.authentication(request) if method.authorization: method.authorization(request) return True # TODO raise wrong method @rpcmethod(name='system.describe', signature=['struct']) def system_describe(self, **kwargs): ''' Returns a simple method description of the methods supported ''' description = {} description['serviceType'] = 'RPC4Django JSONRPC' description['serviceURL'] = self.url, description['methods'] = [{'name': method.name, 'summary': method.help, 'params': method.get_params(), 'return': method.get_returnvalue()} \ for method in self.rpcmethods] return description @rpcmethod(name='system.listMethods', signature=['array']) def system_listmethods(self, **kwargs): ''' Returns a list of supported methods ''' methods = [method.name for method in self.rpcmethods] methods.sort() return methods @rpcmethod(name='system.methodHelp', signature=['string', 'string']) def system_methodhelp(self, method_name, **kwargs): ''' Returns documentation for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.help raise BadMethodException('Method %s not registered here' % method_name) @rpcmethod(name='system.methodSignature', signature=['array', 'string']) def system_methodsignature(self, method_name, **kwargs): ''' Returns the signature for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.signature raise BadMethodException('Method %s not registered here' % method_name) @rpcmethod(name='system.login', signature=['boolean', 'string', 'string']) def system_login(self, username, password, **kwargs): ''' Authorizes a user to enable sending protected RPC requests ''' request = kwargs.get('request', None) user = authenticate(username=username, password=password) if user is not None and request is not None: if user.is_active: login(request, user) return True return False @rpcmethod(name='system.logout', signature=['boolean']) def system_logout(self, **kwargs): ''' Deauthorizes a user ''' request = kwargs.get('request', None) if request is not None: logout(request) return True return False def register_rpcmethods(self, apps): ''' Scans the installed apps for methods with the rpcmethod decorator Adds these methods to the list of methods callable via RPC ''' for appname in apps: # check each app for any rpcmethods try: app = __import__(appname, globals(), locals(), ['*']) except (ImportError, ValueError): # import throws ValueError on empty "name" continue for obj in dir(app): method = getattr(app, obj) if callable(method) and \ hasattr(method, 'is_rpcmethod') and \ method.is_rpcmethod == True: # if this method is callable and it has the rpcmethod # decorator, add it to the dispatcher self.register_method(method, method.external_name) elif isinstance(method, types.ModuleType): # if this is not a method and instead a sub-module, # scan the module for methods with @rpcmethod self.register_rpcmethods(["%s.%s" % (appname, obj)]) def get_method_name(self, raw_post_data): ''' Gets the name of the method to be called given the post data and the format of the data ''' try: # attempt to do a json decode on the data jsondict = json.loads(raw_post_data) if not isinstance(jsondict, dict) or 'method' not in jsondict: return None else: return jsondict['method'] except ValueError: return None def list_methods(self): ''' Returns a list of RPCMethod objects supported by the server ''' return self.rpcmethods def register_method(self, method, name=None, signature=None, helpmsg=None): ''' Instantiates an RPCMethod object and adds it to ``rpcmethods`` so that it can be called by RPC requests **Parameters** ``method`` A callable Python method that the dispatcher will delegate to when requested via RPC ``name`` The name to make the method availabe. ``None`` signifies to use the method's actual name ``signature`` The signature of the method. See :meth:`rpc4django.rpcdispatcher.rpcmethod` ``helpmsg`` The "help" message displayed by introspection functions asking about the method ''' meth = RPCMethod(method, name, signature, helpmsg) if meth.name not in self.system_listmethods(): self.jsonrpcdispatcher.register_function(method, meth.name) self.rpcmethods.append(meth)
class RPCDispatcher: ''' Keeps track of the methods available to be called and then dispatches method calls to either the :class:`XMLRPCDispatcher <rpc4django.xmlrpcdispatcher.XMLRPCDispatcher>` or the :class:`JSONRPCDispatcher <rpc4django.jsonrpcdispatcher.JSONRPCDispatcher>` Disables RPC introspection methods (eg. ``system.list_methods()`` if ``restrict_introspection`` is set to ``True``. Disables out of the box authentication if ``restrict_ootb_auth`` is ``True``. **Attributes** ``url`` The URL that handles RPC requests (eg. ``/RPC2``) This is needed by ``system.describe``. ``rpcmethods`` A list of :class:`RPCMethod<rpc4django.rpcdispatcher.RPCMethod>` instances available to be called by the dispatcher ``xmlrpcdispatcher`` An instance of :class:`XMLRPCDispatcher <rpc4django.xmlrpcdispatcher.XMLRPCDispatcher>` where XMLRPC calls are dispatched to using :meth:`xmldispatch` ``jsonrpcdispatcher`` An instance of :class:`JSONRPCDispatcher <rpc4django.jsonrpcdispatcher.JSONRPCDispatcher>` where JSONRPC calls are dispatched to using :meth:`jsondispatch` ''' def __init__(self, url='', apps=[], restrict_introspection=False, restrict_ootb_auth=True): version = platform.python_version_tuple() self.url = url self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher() self.xmlrpcdispatcher = XMLRPCDispatcher() if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) if not restrict_ootb_auth: self.register_method(self.system_login) self.register_method(self.system_logout) self.register_rpcmethods(apps) @rpcmethod(name='system.describe', signature=['struct']) def system_describe(self): ''' Returns a simple method description of the methods supported ''' description = {} description['serviceType'] = 'RPC4Django JSONRPC+XMLRPC' description['serviceURL'] = self.url, description['methods'] = [{'name': method.name, 'summary': method.help, 'params': method.get_params(), 'return': method.get_returnvalue()} \ for method in self.rpcmethods] return description @rpcmethod(name='system.listMethods', signature=['array']) def system_listmethods(self): ''' Returns a list of supported methods ''' methods = [method.name for method in self.rpcmethods] methods.sort() return methods @rpcmethod(name='system.methodHelp', signature=['string', 'string']) def system_methodhelp(self, method_name): ''' Returns documentation for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.help # this differs from what implementation in SimpleXMLRPCServer does # this will report via a fault or error while SimpleXMLRPCServer # just returns an empty string raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) @rpcmethod(name='system.methodSignature', signature=['array', 'string']) def system_methodsignature(self, method_name): ''' Returns the signature for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.signature raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) @rpcmethod(name='system.login', signature=['boolean', 'string', 'string']) def system_login(self, username, password, **kwargs): ''' Authorizes a user to enable sending protected RPC requests ''' request = kwargs.get('request', None) user = authenticate(username=username, password=password) if user is not None and request is not None: if user.is_active: login(request, user) return True return False @rpcmethod(name='system.logout', signature=['boolean']) def system_logout(self, **kwargs): ''' Deauthorizes a user ''' request = kwargs.get('request', None) if request is not None: logout(request) return True return False def register_rpcmethods(self, apps): ''' Scans the installed apps for methods with the rpcmethod decorator Adds these methods to the list of methods callable via RPC ''' for appname in apps: # check each app for any rpcmethods try: app = __import__(appname, globals(), locals(), ['*']) except (ImportError, ValueError): # import throws ValueError on empty "name" continue for obj in dir(app): method = getattr(app, obj) if isinstance(method, xmlrpclib.ServerProxy): continue if callable(method) and \ hasattr(method, 'is_rpcmethod') and \ method.is_rpcmethod == True: # if this method is callable and it has the rpcmethod # decorator, add it to the dispatcher self.register_method(method, method.external_name) elif isinstance(method, types.ModuleType): # if this is not a method and instead a sub-module, # scan the module for methods with @rpcmethod self.register_rpcmethods(["%s.%s" % (appname, obj)]) def jsondispatch(self, raw_post_data, **kwargs): ''' Sends the post data to :meth:`rpc4django.jsonrpcdispatcher.JSONRPCDispatcher.dispatch` ''' return self.jsonrpcdispatcher.dispatch(raw_post_data, **kwargs) def xmldispatch(self, raw_post_data, **kwargs): ''' Sends the post data to :meth:`rpc4django.xmlrpcdispatcher.XMLRPCDispatcher.dispatch` ''' return self.xmlrpcdispatcher.dispatch(raw_post_data, **kwargs) def get_method_name(self, raw_post_data, request_format='xml'): ''' Gets the name of the method to be called given the post data and the format of the data ''' if request_format == 'xml': # xmlrpclib.loads could throw an exception, but this is fine # since _marshaled_dispatch would throw the same thing try: params, method = xmlrpclib.loads(raw_post_data) return method except Exception: return None else: try: # attempt to do a json decode on the data jsondict = json.loads(raw_post_data) if not isinstance(jsondict, dict) or 'method' not in jsondict: return None else: return jsondict['method'] except ValueError: return None def list_methods(self): ''' Returns a list of RPCMethod objects supported by the server ''' return self.rpcmethods def register_method(self, method, name=None, signature=None, helpmsg=None): ''' Instantiates an RPCMethod object and adds it to ``rpcmethods`` so that it can be called by RPC requests **Parameters** ``method`` A callable Python method that the dispatcher will delegate to when requested via RPC ``name`` The name to make the method availabe. ``None`` signifies to use the method's actual name ``signature`` The signature of the method. See :meth:`rpc4django.rpcdispatcher.rpcmethod` ``helpmsg`` The "help" message displayed by introspection functions asking about the method ''' meth = RPCMethod(method, name, signature, helpmsg) if meth.name not in self.system_listmethods(): self.xmlrpcdispatcher.register_function(method, meth.name) self.jsonrpcdispatcher.register_function(method, meth.name) self.rpcmethods.append(meth)
class RPCDispatcher: ''' Dispatches method calls to either the xmlrpc or jsonrpc dispatcher ''' def __init__(self, url_name='root', restrict_introspection=False): self.url_name = url_name self.rpcmethods = [] # a list of RPCMethod objects self.jsonrpcdispatcher = JSONRPCDispatcher() self.xmlrpcdispatcher = XMLRPCDispatcher() if not restrict_introspection: self.register_method(self.system_listmethods) self.register_method(self.system_methodhelp) self.register_method(self.system_methodsignature) self.register_method(self.system_describe) @rpcmethod(name='system.describe', signature=['struct']) def system_describe(self): ''' Returns a simple method description of the methods supported ''' description = {} description['serviceType'] = 'RPC4Django JSONRPC+XMLRPC' description['methods'] = [{'name': method.name, 'summary': method.help, 'params': method.get_params(), 'return': method.get_returnvalue()} \ for method in self.rpcmethods] return description @rpcmethod(name='system.listMethods', signature=['array']) def system_listmethods(self): ''' Returns a list of supported methods ''' methods = [method.name for method in self.rpcmethods] methods.sort() return methods @rpcmethod(name='system.methodHelp', signature=['string', 'string']) def system_methodhelp(self, method_name): ''' Returns documentation for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.help # this differs from what implementation in SimpleXMLRPCServer does # this will report via a fault or error while SimpleXMLRPCServer # just returns an empty string raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) @rpcmethod(name='system.methodSignature', signature=['array', 'string']) def system_methodsignature(self, method_name): ''' Returns the signature for a specified method ''' for method in self.rpcmethods: if method.name == method_name: return method.signature raise Fault(APPLICATION_ERROR, 'No method found with name: ' + \ str(method_name)) def jsondispatch(self, raw_post_data, **kwargs): ''' Sends the post data to a jsonrpc processor ''' return self.jsonrpcdispatcher.dispatch(raw_post_data, **kwargs) def xmldispatch(self, raw_post_data, **kwargs): ''' Sends the post data to an xmlrpc processor ''' return self.xmlrpcdispatcher.dispatch(raw_post_data, **kwargs) def get_method_name(self, raw_post_data, request_format='xml'): ''' Gets the name of the method to be called given the post data and the format of the data ''' if request_format == 'xml': # xmlrpclib.loads could throw an exception, but this is fine # since _marshaled_dispatch would throw the same thing try: params, method = xmlrpclib.loads(raw_post_data) return method except Fault: return None else: try: # attempt to do a json decode on the data jsondict = json.loads(raw_post_data) if not isinstance(jsondict, dict) or 'method' not in jsondict: return None else: return jsondict['method'] except ValueError: return None def list_methods(self): ''' Returns a list of RPCMethod objects supported by the server ''' return self.rpcmethods def register_method(self, method, name=None, signature=None, helpmsg=None): ''' Registers a method with the rpc server ''' meth = RPCMethod(method, name, signature, helpmsg) if meth.name not in self.system_listmethods(): self.xmlrpcdispatcher.register_function(method, meth.name) self.jsonrpcdispatcher.register_function(method, meth.name) self.rpcmethods.append(meth)