From 215fbcba6872cefe7923f0a7a508b2a9125022cb Mon Sep 17 00:00:00 2001 From: Arinerron Date: Sun, 29 Jul 2018 17:37:12 -0700 Subject: [PATCH] Add web api callbacks --- onionr/api.py | 71 +++++++++++++++++++ onionr/communicator2.py | 4 +- onionr/onionr.py | 6 +- onionr/onionrblockapi.py | 19 +++-- onionr/onionrpluginapi.py | 20 ++++++ .../static-data/ui/{readme.txt => README.md} | 0 6 files changed, 107 insertions(+), 13 deletions(-) rename onionr/static-data/ui/{readme.txt => README.md} (100%) diff --git a/onionr/api.py b/onionr/api.py index 43e7cabc..256f55f9 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -30,6 +30,9 @@ class API: ''' Main HTTP API (Flask) ''' + + callbacks = {'public' : {}, 'private' : {}, 'ui' : {}} + def validateToken(self, token): ''' Validate that the client token matches the given token @@ -164,6 +167,44 @@ class API: self.mimeType = 'text/html' response = siteData.split(b'-', 2)[-1] resp = Response(response) + elif action == "insertBlock": + response = {'success' : False, 'reason' : 'An unknown error occurred'} + + try: + decoded = json.loads(data) + + block = Block() + + sign = False + + for key in decoded: + val = decoded[key] + + key = key.lower() + + if key == 'type': + block.setType(val) + elif key in ['body', 'content']: + block.setContent(val) + elif key == 'parent': + block.setParent(val) + elif key == 'sign': + sign = (str(val).lower() == 'true') + + hash = block.save(sign = sign) + + if not hash is False: + response['success'] = true + response['hash'] = hash + response['reason'] = 'Successfully wrote block to file' + else: + response['reason'] = 'Faield to save the block' + except Exception as e: + logger.debug('insertBlock api request failed', error = e) + + resp = Response(json.dumps(response)) + elif action in callbacks['private']: + resp = Response(str(getCallback(action, scope = 'private')(request))) else: resp = Response('(O_o) Dude what? (invalid command)') endTime = math.floor(time.time()) @@ -258,6 +299,8 @@ class API: peers = self._core.listPeers(getPow=True) response = ','.join(peers) resp = Response(response) + elif action in callbacks['public']: + resp = Response(str(getCallback(action, scope = 'public')(request))) else: resp = Response("") @@ -328,3 +371,31 @@ class API: # we exit rather than abort to avoid fingerprinting logger.debug('Avoiding fingerprinting, exiting...') sys.exit(1) + + def setCallback(action, callback, scope = 'public'): + if not scope in callbacks: + return False + + callbacks[scope][action] = callback + + return True + + def removeCallback(action, scope = 'public'): + if (not scope in callbacks) or (not action in callbacks[scope]): + return False + + del callbacks[scope][action] + + return True + + def getCallback(action, scope = 'public'): + if (not scope in callbacks) or (not action in callbacks[scope]): + return None + + return callbacks[scope][action] + + def getCallbacks(scope = None): + if (not scope is None) and (scope in callbacks): + return callbacks[scope] + + return callbacks diff --git a/onionr/communicator2.py b/onionr/communicator2.py index b3734cbc..b420fcba 100755 --- a/onionr/communicator2.py +++ b/onionr/communicator2.py @@ -36,7 +36,7 @@ class OnionrCommunicatorDaemon: # intalize NIST beacon salt and time self.nistSaltTimestamp = 0 self.powSalt = 0 - + self.blockToUpload = '' # loop time.sleep delay in seconds @@ -309,7 +309,7 @@ class OnionrCommunicatorDaemon: logger.info(i) def peerAction(self, peer, action, data=''): - '''Perform a get request to a peer''' + '''Perform a get request to a peer''' if len(peer) == 0: return False logger.info('Performing ' + action + ' with ' + peer + ' on port ' + str(self.proxyPort)) diff --git a/onionr/onionr.py b/onionr/onionr.py index 11db4351..ec3ec468 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -91,8 +91,6 @@ class Onionr: self.onionrCore = core.Core() self.onionrUtils = OnionrUtils(self.onionrCore) - self.userOS = platform.system() - # Handle commands self.debug = False # Whole application debugging @@ -258,7 +256,7 @@ class Onionr: def getWebPassword(self): return config.get('client.hmac') - + def printWebPassword(self): print(self.getWebPassword()) @@ -542,7 +540,7 @@ class Onionr: subprocess.Popen([communicatorDaemon, "run", str(net.socksPort)]) logger.debug('Started communicator') events.event('daemon_start', onionr = self) - api.API(self.debug) + self.api = api.API(self.debug) return diff --git a/onionr/onionrblockapi.py b/onionr/onionrblockapi.py index 695d41ab..a49210d3 100644 --- a/onionr/onionrblockapi.py +++ b/onionr/onionrblockapi.py @@ -38,7 +38,6 @@ class Block: self.btype = type self.bcontent = content - # initialize variables self.valid = True self.raw = None @@ -71,8 +70,10 @@ class Block: # logic - def decrypt(self, anonymous=True, encodedData=True): - '''Decrypt a block, loading decrypted data into their vars''' + def decrypt(self, anonymous = True, encodedData = True): + ''' + Decrypt a block, loading decrypted data into their vars + ''' if self.decrypted: return True retData = False @@ -100,9 +101,11 @@ class Block: else: logger.warn('symmetric decryption is not yet supported by this API') return retData - + def verifySig(self): - '''Verify if a block's signature is signed by its claimed signer''' + ''' + Verify if a block's signature is signed by its claimed signer + ''' core = self.getCore() if core._crypto.edVerify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True): @@ -227,12 +230,14 @@ class Block: else: self.hash = self.getCore().insertBlock(self.getContent(), header = self.getType(), sign = sign) self.update() + return self.getHash() else: logger.warn('Not writing block; it is invalid.') except Exception as e: logger.error('Failed to save block.', error = e, timestamp = False) - return False + + return False # getters @@ -533,7 +538,7 @@ class Block: if relevant: relevant_blocks.append(block) - + if bool(reverse): relevant_blocks.reverse() diff --git a/onionr/onionrpluginapi.py b/onionr/onionrpluginapi.py index bfaf73e8..0120dad7 100644 --- a/onionr/onionrpluginapi.py +++ b/onionr/onionrpluginapi.py @@ -130,6 +130,22 @@ class CommandAPI: def get_commands(self): return self.pluginapi.get_onionr().getCommands() +class WebAPI: + def __init__(self, pluginapi): + self.pluginapi = pluginapi + + def register_callback(self, action, callback, scope = 'public'): + return self.pluginapi.get_onionr().api.setCallback(action, callback, scope = scope) + + def unregister_callback(self, action, scope = 'public'): + return self.pluginapi.get_onionr().api.removeCallback(action, scope = scope) + + def get_callback(self, action, scope = 'public'): + return self.pluginapi.get_onionr().api.getCallback(action, scope= scope) + + def get_callbacks(self, scope = None): + return self.pluginapi.get_onionr().api.getCallbacks(scope = scope) + class pluginapi: def __init__(self, onionr, data): self.onionr = onionr @@ -142,6 +158,7 @@ class pluginapi: self.daemon = DaemonAPI(self) self.plugins = PluginAPI(self) self.commands = CommandAPI(self) + self.web = WebAPI(self) def get_onionr(self): return self.onionr @@ -167,5 +184,8 @@ class pluginapi: def get_commandapi(self): return self.commands + def get_webapi(self): + return self.web + def is_development_mode(self): return self.get_onionr()._developmentMode diff --git a/onionr/static-data/ui/readme.txt b/onionr/static-data/ui/README.md similarity index 100% rename from onionr/static-data/ui/readme.txt rename to onionr/static-data/ui/README.md