Calling function on another gateway?

I have a split frontend/backend gateway architecture with two gateways and I’m trying to set up a page which displays all devices and allow the user (engineers) to enable/disable the connections from there. However because of the split architecture, I can’t just call system.device.* functions as they don’t exist on the frontend gateway… How would I achieve this? And how would I return the return value from the gateway function call e.g. from listDevices()?

system.util.sendRequest takes an optional remoteServer argument you can use to send a request to a remote gateway.

In your case, a message handler on the backend could return a device list when requested.

4 Likes

Cool, thanks! I was sure I’d seen something before but never used it before.

I created a message handler that accepts a string path to the system function to call so I can use it generically for calling any system function on the other gateway:

	"""	
	Arguments:
		payload: A dictionary with the following structure:
		If the function accepts keyword args:
			{'function': 'subLibrary.functionName',
			 'args': {'arg1': 'val1', 'argN': valN'}
			}
		If the function doesn't accept keyword args:
			{'function': 'subLibrary.functionName',
			 'args': ['arg1Val', 'argNVal']
			}
		Note: The function string appended to "system." will be the resulting function call executed.
		      Passing in the full system.subLibrary.functionName will also work but system. is otherwise
		      assumed.
	"""
	import traceback
	try:
		functionPartialString = payload['function']
	except KeyError:
		return traceback.format_exc()
	
	# remove 'system.' from the start if it exists
	if functionPartialString.startswith('system.'):
		functionPartialString = functionPartialString[len('system.'):]
	
	function = system
	for part in functionPartialString.split('.'):
		function = getattr(function, part)
	
	args = payload.get('args', {})
	if isinstance(args, dict):
		ret = function(**args)
	else:
		ret = function(*args)
	
	return ret

and calling it on the other gateway:

project = 'dummy'
msgType = 'systemFunction'
payload = {'function': 'system.device.listDevices'}
remoteGWName = 'OtherGWName'

print system.util.sendRequest(project, msgType, payload, remoteGWName)
3 Likes

Oy! That makes me cringe.

1 Like

I knew it would make some people cringe, but hey, we use to have a single gateway and I could run any system function then as well… I did limit it to only running system functions, not user functions

yikes… free real estate for hackers
you should atleast prevent some function like system.util.execute and add/edit user/role… than atleast hackers can only mess with your application and not with the gateway, i think…

Like @victordcq you should probably make a whitelist of what functions you allow to be called. So something like

WHITELISTED_FUNCTIONS = ['listDevices', ...]
...
if payload['function'] in WIHTELISTED_FUNCTIONS:
    #proceed

yeah whitelisting probably is better than blacklisting xd

Oook, i’ll create a whitelist. Call me naive, but I still don’t see how it’s a security risk since:

  • they can only execute the function from a gateway part of the gateway network which is configured with a user with sufficient privileges. A user with these privileges will also have full reign to do anything they want.
  • if they have access to the frontend gateway, they have access to the backend gateway in which case they could just run the system functions from there anyway.

The only thing I can think of is if they could manipulate the function call in the client session - is this possible? And in this case, if you have a single-gateway system, can’t they just do the same thing?