work on wot cli

This commit is contained in:
Kevin F 2022-11-22 00:57:14 -05:00
parent c2db671a85
commit e84ad93de7
12 changed files with 74 additions and 137 deletions

View File

@ -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'

View File

@ -2,6 +2,8 @@
ORIG_ONIONR_RUN_DIR=`pwd` ORIG_ONIONR_RUN_DIR=`pwd`
export ORIG_ONIONR_RUN_DIR export ORIG_ONIONR_RUN_DIR
export PYTHONDONTWRITEBYTECODE=1 export PYTHONDONTWRITEBYTECODE=1
export PYTHONUNBUFFERED=1
export PYTHONOPTIMIZE="true"
cd "$(dirname "$0")" cd "$(dirname "$0")"
cd src cd src
./__init__.py "$@" ./__init__.py "$@"

View File

@ -112,6 +112,7 @@ def onionr_main():
Entrypoint for daemon is commands/daemonlaunch/__init__.py Entrypoint for daemon is commands/daemonlaunch/__init__.py
""" """
events.event('beforecmdparsing', threaded=False)
parser.register() parser.register()

View File

@ -8,8 +8,6 @@ if TYPE_CHECKING:
from onionrplugins import onionrevents from onionrplugins import onionrevents
from logger import log as logging from logger import log as logging
from socks import GeneralProxyError
from ..peer import Peer from ..peer import Peer
from ..commands import GossipCommands, command_to_byte from ..commands import GossipCommands, command_to_byte
from ..constants import PEER_AMOUNT_TO_ASK, TRANSPORT_SIZE_BYTES from ..constants import PEER_AMOUNT_TO_ASK, TRANSPORT_SIZE_BYTES

View File

@ -17,7 +17,8 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
import os, re, importlib import os, re
import importlib.util
import traceback import traceback
from . import onionrevents as events from . import onionrevents as events

View File

@ -42,13 +42,18 @@ socket_file_path = identifyhome.identify_home() + 'rpc.sock'
from jsonrpc import JSONRPCResponseManager, dispatcher from jsonrpc import JSONRPCResponseManager, dispatcher
import jsonrpc import jsonrpc
import ujson import ujson
import requests_unixsocket
import requests
jsonrpc.manager.json = ujson jsonrpc.manager.json = ujson
from onionrplugins import plugin_apis
# RPC modules map Onionr APIs to the RPC dispacher # RPC modules map Onionr APIs to the RPC dispacher
from rpc import blocks, pluginrpcmethods from rpc import blocks, pluginrpcmethods
from rpc.addmodule import add_module_to_api from rpc.addmodule import add_module_to_api
plugin_apis['rpc.add_module_to_api'] = add_module_to_api plugin_apis['rpc.add_module_to_api'] = add_module_to_api
class OnionrRPC(object): class OnionrRPC(object):
@ -62,6 +67,20 @@ class OnionrRPC(object):
return response.json 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 on_afterinit(api, data=None):
def ping(): def ping():
return "pong" return "pong"

View File

@ -2,10 +2,30 @@ import tty
import sys import sys
import subprocess import subprocess
import requests
import requests_unixsocket
from logger import log as logging
import onionrplugins.pluginapis
def do_quit(): raise KeyboardInterrupt def do_quit(): raise KeyboardInterrupt
rpc_payload = {
"method": "echo",
"params": ["example"],
"jsonrpc": "2.0",
"id": 0,
}
def list_idens(): def list_idens():
print('Listing identities') 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 = { main_menu = {
@ -14,18 +34,22 @@ main_menu = {
} }
def main_ui(): 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: while True:
# move cursor to the beginning # move cursor to the beginning
print('\r', end='') print('\r', end='')
key = sys.stdin.read(1) key = sys.stdin.read(1)
try: try:
main_menu[key][1]() main_menu[key][0]()
except KeyError: except KeyError:
pass pass
except KeyboardInterrupt: except KeyboardInterrupt:
break break
subprocess.Popen(['reset'], stdout=subprocess.PIPE) #subprocess.Popen(['reset'], stdout=subprocess.PIPE)

View File

@ -61,17 +61,23 @@ def on_init(api, data=None):
# load active identity, from there load our trust graph # load active identity, from there load our trust graph
active_identity = config.get('wot.active_identity_name', '') active_identity = config.get('wot.active_identity_name', '')
if active_identity: if not active_identity:
try: try:
script = sys.argv[0] + ' ' script = sys.argv[0] + ' '
except IndexError: except IndexError:
script = '' script = ''
logging.info( logging.info(
"Generate a web of trust identity with '{script}wot new" + f"Generate a web of trust identity with '{script}wot new" +
"<name>' and restart Onionr") "<name>' and restart Onionr")
return return
if config.get('wot.use_system_keyring', True): if config.get('wot.use_system_keyring', True):
try:
iden = wotkeyring.get_identity_by_name(active_identity) 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: else:
# load from file # load from file
iden = load_identity_from_config(active_identity) iden = load_identity_from_config(active_identity)

View File

@ -6,3 +6,4 @@ from .identity import Identity
from .getbykey import get_identity_by_key from .getbykey import get_identity_by_key
from .identity import identities from .identity import identities
from .identity.identityset import serialize_identity_set from .identity.identityset import serialize_identity_set

View File

@ -11,6 +11,7 @@ from wot.identity import Identity, identities
from wot.exceptions import IdentitySerializationError from wot.exceptions import IdentitySerializationError
from wot.getbykey import get_identity_by_key from wot.getbykey import get_identity_by_key
from wot.identityprocessing import processtrustsignature from wot.identityprocessing import processtrustsignature
import wot.wotcommand
def load_identity_from_block(block) -> Identity: 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: 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: try:
# If good signature, # If good signature,
# it adds the signature to the signed identity's trust set # it adds the signature to the signed identity's trust set

View File

@ -1,7 +1,15 @@
from enum import IntEnum, auto from enum import IntEnum, auto
from types import SimpleNamespace
class WotCommand(IntEnum): class WotCommand(IntEnum):
TRUST = 1 TRUST = 1
REVOKE_TRUST = auto() REVOKE_TRUST = auto()
ANNOUNCE = auto() ANNOUNCE = auto()
REVOKE = auto() REVOKE = auto()
_block_type_map = {
'trust': b'wots'
}
block_type_map = SimpleNamespace(**_block_type_map)

View File

@ -1,13 +1,13 @@
import keyring import keyring
from identity import Identity import wot.identity
def get_identity_by_name(name: str) -> 'Identity': def get_identity_by_name(name: str) -> 'Identity':
iden_key = keyring.get_credential('onionr.wot', name) iden_key = keyring.get_credential('onionr.wot', name)
if not iden_key: if not iden_key:
raise KeyError('Identity not found') 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: def set_identity_by_name(identity: 'Identity', name: str) -> None: