From e84ad93de7ad4e27e085d6e500daa46d2cef30e4 Mon Sep 17 00:00:00 2001 From: Kevin F Date: Tue, 22 Nov 2022 00:57:14 -0500 Subject: [PATCH] work on wot cli --- docs/dev/http-api.md | 124 ------------------ onionr.sh | 2 + src/__init__.py | 1 + src/gossip/client/peerexchange.py | 2 - src/onionrplugins/__init__.py | 3 +- static-data/official-plugins/rpc/main.py | 19 +++ .../official-plugins/wot/cli/__init__.py | 32 ++++- static-data/official-plugins/wot/main.py | 12 +- .../official-plugins/wot/wot/__init__.py | 1 + .../wot/wot/loadfromblocks.py | 3 +- .../official-plugins/wot/wot/wotcommand.py | 8 ++ .../wot/wot/wotkeyring/__init__.py | 4 +- 12 files changed, 74 insertions(+), 137 deletions(-) delete mode 100755 docs/dev/http-api.md diff --git a/docs/dev/http-api.md b/docs/dev/http-api.md deleted file mode 100755 index 6c189fd8..00000000 --- a/docs/dev/http-api.md +++ /dev/null @@ -1,124 +0,0 @@ -# Onionr HTTP API - -All HTTP interfaces in the Onionr reference client use the [Flask](http://flask.pocoo.org/) web framework with the [gevent](http://www.gevent.org/) WSGI server. - -## Client & Public difference - -The client API server is a locked down interface intended for authenticated local communication. - -The public API server is available only remotely from Tor & I2P. It is the interface in which peers use to communicate with one another. - -# Client API - -Please note: endpoints that simply provide static web app files are not documented here. - -* /serviceactive/pubkey - - Methods: GET - - Returns true or false based on if a given public key has an active direct connection service. -* /queueResponseAdd/key (DEPRECATED) - - Methods: POST - - Accepts form key 'data' to set queue response information from a plugin - - Returns success if no error occurs -* /queueResponse/key (DEPRECATED) - - Methods: GET - - Returns the queue response for a key. Returns failure with a 404 code if a code is not set. -* /ping - - Methods: GET - - Returns "pong!" -* /getblocksbytype/type - - Methods: GET - - Returns a list of stored blocks by a given type -* /getblockbody/hash - - Methods: GET - - Returns the main data section of a block -* /getblockdata/hash - - Methods: GET - - Returns the entire data contents of a block, including metadata. -* /getblockheader/hash - - Methods: GET - - Returns the header (metadata section) of a block. -* /gethidden/ - - Methods: GET - - Returns line separated list of hidden blocks -* /hitcount - - Methods: GET - - Return the amount of requests the public api server has received this session -* /lastconnect - - Methods: GET - - Returns the epoch timestamp of when the last incoming connection to the public API server was logged -* /site/hash - - Methods: GET - - Returns HTML content out of a block -* /waitforshare/hash - - Methods: POST - - Prevents the public API server from listing or sharing a block until it has been uploaded to at least 1 peer. -* /shutdown - - Methods: GET - - Shutdown Onionr. You should probably use /shutdownclean instead. -* /shutdownclean - - Methods: GET - - Tells the communicator daemon to shutdown Onionr. Slower but cleaner. -* /getstats - - Methods: GET - - Returns some JSON serialized statistics -* /getuptime - - Methods: GET - - Returns uptime in seconds -* /getActivePubkey - - Methods: GET - - Returns the current active public key in base32 format -* /getHumanReadable/pubkey - - Methods: GET - - Echos the specified public key in mnemonic format -* /insertblock - - Methods: POST - - Accepts JSON data for creating a new block. 'message' contains the block data, 'to' specifies the peer's public key to encrypt the data to, 'sign' is a boolean for signing the message. -* /torready - - Methods: POST - - Returns boolean if Tor is started or not - -# Public API - -v0 - -* / - - Methods: GET - - Returns a basic HTML informational banner describing Onionr. -* /getblocklist - - Methods: GET - - URI Parameters: - - date: unix epoch timestamp for offset - - Returns a list of block hashes stored on the node since an offset (all blocks if no timestamp is specified) -* /getdata/block-hash - - Methods: GET - - Returns data for a block based on a provided hash -* /www/file-path - - Methods: GET - - Returns file data. Intended for manually sharing file data directly from an Onionr node. -* /ping - - Methods: GET - - Returns 'pong!' -* /pex - - Methods: GET - - Returns a list of peer addresses reached within recent time -* /announce - - Methods: POST - - Accepts form data for 'node' (valid node address) and 'random' which is a nonce when hashed (blake2b_256) in the format `hash(peerAddress+serverAddress+nonce)`, begins with at least 5 zeros. - - Returns 200 with 'Success' if no error occurs. If the post is invalid, 'failure' with code 406 is returned. -* /upload - - Methods: POST - - Accepts form data for 'block' as a 'file' upload. - - Returns 200 with 'success' if no error occurs. If the block cannot be accepted, 'failure' with 400 is returned. - -# Direct Connection API - -These are constant endpoints available on direct connection servers. Plugin endpoints for direct connections are not documented here. - -* /ping - - Methods: GET - - Returns 200 with 'pong!' - -* /close - - Methods: GET - - Kills the direct connection server, destroying the onion address. - - Returns 200 with 'goodbye' \ No newline at end of file diff --git a/onionr.sh b/onionr.sh index 3d1f3e72..2212f901 100755 --- a/onionr.sh +++ b/onionr.sh @@ -2,6 +2,8 @@ ORIG_ONIONR_RUN_DIR=`pwd` export ORIG_ONIONR_RUN_DIR export PYTHONDONTWRITEBYTECODE=1 +export PYTHONUNBUFFERED=1 +export PYTHONOPTIMIZE="true" cd "$(dirname "$0")" cd src ./__init__.py "$@" \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py index 18eccb16..e2737a6d 100755 --- a/src/__init__.py +++ b/src/__init__.py @@ -112,6 +112,7 @@ def onionr_main(): Entrypoint for daemon is commands/daemonlaunch/__init__.py """ + events.event('beforecmdparsing', threaded=False) parser.register() diff --git a/src/gossip/client/peerexchange.py b/src/gossip/client/peerexchange.py index 099db1b9..25b55fdd 100644 --- a/src/gossip/client/peerexchange.py +++ b/src/gossip/client/peerexchange.py @@ -8,8 +8,6 @@ if TYPE_CHECKING: from onionrplugins import onionrevents from logger import log as logging -from socks import GeneralProxyError - from ..peer import Peer from ..commands import GossipCommands, command_to_byte from ..constants import PEER_AMOUNT_TO_ASK, TRANSPORT_SIZE_BYTES diff --git a/src/onionrplugins/__init__.py b/src/onionrplugins/__init__.py index 5fdb8536..6edb0b87 100755 --- a/src/onionrplugins/__init__.py +++ b/src/onionrplugins/__init__.py @@ -17,7 +17,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import os, re, importlib +import os, re +import importlib.util import traceback from . import onionrevents as events diff --git a/static-data/official-plugins/rpc/main.py b/static-data/official-plugins/rpc/main.py index 7a4f61c8..0ac9952e 100644 --- a/static-data/official-plugins/rpc/main.py +++ b/static-data/official-plugins/rpc/main.py @@ -42,13 +42,18 @@ socket_file_path = identifyhome.identify_home() + 'rpc.sock' from jsonrpc import JSONRPCResponseManager, dispatcher import jsonrpc import ujson +import requests_unixsocket +import requests jsonrpc.manager.json = ujson +from onionrplugins import plugin_apis + # RPC modules map Onionr APIs to the RPC dispacher from rpc import blocks, pluginrpcmethods from rpc.addmodule import add_module_to_api + plugin_apis['rpc.add_module_to_api'] = add_module_to_api class OnionrRPC(object): @@ -62,6 +67,20 @@ class OnionrRPC(object): return response.json +def rpc_client(*args, **kwargs): + if config.get('rpc.use_sock_file', True): + session = requests_unixsocket.Session() + return session.post( + 'http+unix://' + config.get('rpc.sock_file_path', socket_file_path).replace('/', '%2F') + '/rpc', + *args, **kwargs) + else: + return requests.post( + f'http://{config.get("rpc.bind_host")}/rpc:{config.get("rpc.bind_port")}', + *args, **kwargs) + +def on_beforecmdparsing(api, data=None): + plugin_apis['rpc.rpc_client'] = rpc_client + def on_afterinit(api, data=None): def ping(): return "pong" diff --git a/static-data/official-plugins/wot/cli/__init__.py b/static-data/official-plugins/wot/cli/__init__.py index f36a59ca..0bbd12a4 100644 --- a/static-data/official-plugins/wot/cli/__init__.py +++ b/static-data/official-plugins/wot/cli/__init__.py @@ -2,10 +2,30 @@ import tty import sys import subprocess +import requests +import requests_unixsocket + +from logger import log as logging +import onionrplugins.pluginapis + def do_quit(): raise KeyboardInterrupt + +rpc_payload = { + "method": "echo", + "params": ["example"], + "jsonrpc": "2.0", + "id": 0, +} + + def list_idens(): print('Listing identities') + payload = dict(rpc_payload) + payload['method'] = 'wot.serialize_identity_set' + payload['params'].clear() + print(onionrplugins.pluginapis.plugin_apis['rpc.rpc_client'](payload).text) + main_menu = { @@ -14,18 +34,22 @@ main_menu = { } def main_ui(): - tty.setraw(sys.stdin) - + #tty.setraw(sys.stdin) + try: + onionrplugins.pluginapis.plugin_apis['rpc.rpc_client'] + except KeyError: + logging.error("Web of trust CLI requires RPC plugin to be enabled") + return while True: # move cursor to the beginning print('\r', end='') key = sys.stdin.read(1) try: - main_menu[key][1]() + main_menu[key][0]() except KeyError: pass except KeyboardInterrupt: break - subprocess.Popen(['reset'], stdout=subprocess.PIPE) + #subprocess.Popen(['reset'], stdout=subprocess.PIPE) diff --git a/static-data/official-plugins/wot/main.py b/static-data/official-plugins/wot/main.py index d43a961a..15527d1d 100644 --- a/static-data/official-plugins/wot/main.py +++ b/static-data/official-plugins/wot/main.py @@ -61,17 +61,23 @@ def on_init(api, data=None): # load active identity, from there load our trust graph active_identity = config.get('wot.active_identity_name', '') - if active_identity: + if not active_identity: try: script = sys.argv[0] + ' ' except IndexError: script = '' logging.info( - "Generate a web of trust identity with '{script}wot new" + + f"Generate a web of trust identity with '{script}wot new" + "' and restart Onionr") return + if config.get('wot.use_system_keyring', True): - iden = wotkeyring.get_identity_by_name(active_identity) + try: + iden = wotkeyring.get_identity_by_name(active_identity) + except KeyError: + logging.error( + f"Could not load identity {active_identity} " + + "from keyring despite configuration choice to do so") else: # load from file iden = load_identity_from_config(active_identity) diff --git a/static-data/official-plugins/wot/wot/__init__.py b/static-data/official-plugins/wot/wot/__init__.py index 2dcd124c..724d7384 100644 --- a/static-data/official-plugins/wot/wot/__init__.py +++ b/static-data/official-plugins/wot/wot/__init__.py @@ -6,3 +6,4 @@ from .identity import Identity from .getbykey import get_identity_by_key from .identity import identities from .identity.identityset import serialize_identity_set + diff --git a/static-data/official-plugins/wot/wot/loadfromblocks.py b/static-data/official-plugins/wot/wot/loadfromblocks.py index db7d7d49..1f699485 100644 --- a/static-data/official-plugins/wot/wot/loadfromblocks.py +++ b/static-data/official-plugins/wot/wot/loadfromblocks.py @@ -11,6 +11,7 @@ from wot.identity import Identity, identities from wot.exceptions import IdentitySerializationError from wot.getbykey import get_identity_by_key from wot.identityprocessing import processtrustsignature +import wot.wotcommand def load_identity_from_block(block) -> Identity: @@ -26,7 +27,7 @@ def load_identities_from_blocks() -> Generator[Identity, None, None]: def load_signatures_from_blocks() -> None: - for block in blockdb.get_blocks_by_type(b'wots'): + for block in blockdb.get_blocks_by_type(wot.wotcommand.block_type_map.trust): try: # If good signature, # it adds the signature to the signed identity's trust set diff --git a/static-data/official-plugins/wot/wot/wotcommand.py b/static-data/official-plugins/wot/wot/wotcommand.py index a131c7a5..8a757f05 100644 --- a/static-data/official-plugins/wot/wot/wotcommand.py +++ b/static-data/official-plugins/wot/wot/wotcommand.py @@ -1,7 +1,15 @@ from enum import IntEnum, auto +from types import SimpleNamespace class WotCommand(IntEnum): TRUST = 1 REVOKE_TRUST = auto() ANNOUNCE = auto() REVOKE = auto() + + +_block_type_map = { + 'trust': b'wots' +} + +block_type_map = SimpleNamespace(**_block_type_map) \ No newline at end of file diff --git a/static-data/official-plugins/wot/wot/wotkeyring/__init__.py b/static-data/official-plugins/wot/wot/wotkeyring/__init__.py index 144f71c9..5bea6e62 100644 --- a/static-data/official-plugins/wot/wot/wotkeyring/__init__.py +++ b/static-data/official-plugins/wot/wot/wotkeyring/__init__.py @@ -1,13 +1,13 @@ import keyring -from identity import Identity +import wot.identity def get_identity_by_name(name: str) -> 'Identity': iden_key = keyring.get_credential('onionr.wot', name) if not iden_key: raise KeyError('Identity not found') - return Identity(iden_key, name) + return wot.identity.Identity(iden_key, name) def set_identity_by_name(identity: 'Identity', name: str) -> None: