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)
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: ''' 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)