Removed webUI and unused god objects

Remove defunct requirements
Removed more defunct code
Prepare onionrvalues for new release
disable unixtransport by default
newtransport
Kevin F 2 months ago
parent 228d3cbe35
commit c880b6fa7a
  1. 7
      requirements.in
  2. 41
      requirements.txt
  3. 8
      src/__init__.py
  4. 9
      src/apiservers/README.md
  5. 9
      src/apiservers/__init__.py
  6. 8
      src/apiservers/private/README.md
  7. 121
      src/apiservers/private/__init__.py
  8. 46
      src/apiservers/private/register_private_blueprints.py
  9. 1
      src/filepaths/__init__.py
  10. 13
      src/httpapi/README.md
  11. 36
      src/httpapi/__init__.py
  12. 45
      src/httpapi/addblock/__init__.py
  13. 1
      src/httpapi/apiutils/__init__.py
  14. 42
      src/httpapi/apiutils/setbindip.py
  15. 30
      src/httpapi/apiutils/shutdown.py
  16. 66
      src/httpapi/configapi/__init__.py
  17. 16
      src/httpapi/fdsafehandler.py
  18. 30
      src/httpapi/fileoffsetreader/__init__.py
  19. 75
      src/httpapi/friendsapi/__init__.py
  20. 35
      src/httpapi/httpheaders.py
  21. 1
      src/httpapi/miscclientapi/__init__.py
  22. 111
      src/httpapi/miscclientapi/endpoints.py
  23. 80
      src/httpapi/miscclientapi/staticfiles.py
  24. 1
      src/httpapi/security/__init__.py
  25. 102
      src/httpapi/security/client.py
  26. 67
      src/httpapi/security/lan.py
  27. 33
      src/httpapi/security/pluginwhitelist.py
  28. 6
      src/httpapi/sse/README.md
  29. 18
      src/httpapi/sse/__init__.py
  30. 41
      src/httpapi/sse/private/__init__.py
  31. 34
      src/httpapi/sse/wrapper.py
  32. 46
      src/httpapi/themeapi/__init__.py
  33. 55
      src/notifier/__init__.py
  34. 54
      src/onionrcommands/daemonlaunch/__init__.py
  35. 31
      src/onionrcommands/daemonlaunch/killdaemon.py
  36. 59
      src/onionrcommands/openwebinterface.py
  37. 12
      src/onionrcommands/parser/arguments.py
  38. 78
      src/onionrcommands/restartonionr.py
  39. 37
      src/onionrcommands/runtimetestcmd.py
  40. 58
      src/onionrcommands/softreset.py
  41. 3
      src/onionrcrypto/cryptoutils/__init__.py
  42. 3
      src/onionrcrypto/cryptoutils/replayvalidation.py
  43. 5
      src/onionrcrypto/cryptoutils/safecompare.py
  44. 38
      src/onionrcrypto/cryptoutils/verifypow.py
  45. 2
      src/onionrplugins/onionrpluginapi.py
  46. 21
      src/onionrprocess/__init__.py
  47. 12
      src/onionrsetup/setupconfig.py
  48. 1
      src/onionrutils/cleanup/__init__.py
  49. 1
      src/onionrutils/dependencycheck.py
  50. 37
      src/onionrutils/getclientapiserver.py
  51. 29
      src/onionrutils/getopenport.py
  52. 103
      src/onionrutils/localcommand.py
  53. 71
      src/onionrutils/stringvalidators.py
  54. 7
      src/onionrutils/updater/__init__.py
  55. 127
      src/onionrutils/validatemetadata.py
  56. 34
      src/onionrvalues.py
  57. 77
      src/runtests/__init__.py
  58. 46
      src/runtests/dnsrebindingtest.py
  59. 8
      src/runtests/osver.py
  60. 9
      src/runtests/uicheck.py
  61. 11
      src/runtests/webpasstest.py
  62. 34
      src/setupkvvars/__init__.py
  63. 30
      src/utils/bettersleep.py
  64. 17
      src/utils/readoffset.py
  65. 51
      src/utils/reconstructhash.py
  66. 2
      start-ram.sh
  67. 1
      static-data/connect-check.txt
  68. 7
      static-data/default-plugins/example/main.py
  69. 5
      static-data/default-plugins/tor/bootstrap.py
  70. 23
      static-data/default-plugins/tor/torpeer.py
  71. 2
      static-data/default_config.json
  72. BIN
      static-data/sounds/notification1.mp3
  73. 96
      static-data/www/friends/friends.js
  74. 160
      static-data/www/friends/index.html
  75. 4
      static-data/www/friends/style.css
  76. 24
      static-data/www/onboarding/consentskip.js
  77. 13
      static-data/www/onboarding/donate-modal.css
  78. 17
      static-data/www/onboarding/donate-modal.html
  79. 45
      static-data/www/onboarding/donate.js
  80. 291
      static-data/www/onboarding/index.html
  81. 48
      static-data/www/onboarding/onboarding.css
  82. 86
      static-data/www/onboarding/onboarding.js
  83. BIN
      static-data/www/private/images/made-with-bulma--dark.png
  84. BIN
      static-data/www/private/images/python-powered.png
  85. 373
      static-data/www/private/index.html
  86. 2
      static-data/www/private/js/console.js
  87. 13
      static-data/www/private/js/motd.js
  88. 63
      static-data/www/private/main.css
  89. 38
      static-data/www/shared/about.html
  90. 469
      static-data/www/shared/base32.js
  91. 436
      static-data/www/shared/bulma-quickview.js
  92. 24
      static-data/www/shared/bulma-quickview.min.css
  93. 24
      static-data/www/shared/bulma-steps.min.css
  94. 618
      static-data/www/shared/bulma-steps.min.js
  95. 24
      static-data/www/shared/bulma-tooltip.min.css
  96. 56
      static-data/www/shared/configeditor.js
  97. 61
      static-data/www/shared/direct-connections.js
  98. 1028
      static-data/www/shared/eventsource.js
  99. 34
      static-data/www/shared/fontawesome-free-5.10.2/LICENSE.txt
  100. 5
      static-data/www/shared/fontawesome-free-5.10.2/css/all.min.css
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,17 +1,10 @@
urllib3==1.26.7
requests==2.28.1
PyNaCl==1.5.0
gevent==21.12.0
Flask==2.1.3
PySocks==1.7.1
stem==1.8.0
deadsimplekv==0.3.2
unpaddedbase32==0.2.0
toomanyobjs==1.1.0
niceware==0.2.1
psutil==5.9.1
filenuke==0.0.0
watchdog==2.1.9
ujson==5.4.0
cffi==1.15.1
onionrblocks==7.0.0

@ -4,36 +4,14 @@
#
# pip-compile
#
certifi==2018.11.29
# via requests
cffi==1.15.1
# via
# -r requirements.in
# pynacl
charset-normalizer==2.0.9
# via requests
click==8.0.3
# via flask
deadsimplekv==0.3.2
# via -r requirements.in
filenuke==0.0.0
# via -r requirements.in
flask==2.1.3
# via -r requirements.in
gevent==21.12.0
# via -r requirements.in
greenlet==1.1.2
# via gevent
idna==2.7
# via requests
itsdangerous==2.0.1
# via flask
jinja2==3.0.3
# via flask
kasten==3.0.0
# via onionrblocks
markupsafe==2.0.1
# via jinja2
mimcvdf==1.2.1
# via kasten
msgpack==1.0.3
@ -54,28 +32,9 @@ pynacl==1.5.0
# onionrblocks
pysocks==1.7.1
# via -r requirements.in
requests==2.28.1
# via -r requirements.in
stem==1.8.0
# via -r requirements.in
toomanyobjs==1.1.0
# via -r requirements.in
ujson==5.4.0
# via -r requirements.in
unpaddedbase32==0.2.0
# via -r requirements.in
urllib3==1.26.7
# via
# -r requirements.in
# requests
watchdog==2.1.9
# via -r requirements.in
werkzeug==2.0.2
# via flask
zope-event==4.4
# via gevent
zope-interface==5.1.0
# via gevent
# The following packages are considered to be unsafe in a requirements file:
# setuptools

@ -46,14 +46,6 @@ locale.setlocale(locale.LC_ALL, '') # noqa
ran_as_script = False
if __name__ == "__main__": ran_as_script = True
# Import standard libraries
try:
from onionrutils import dependencycheck # noqa
except ModuleNotFoundError as e:
print('Missing requirement: ' + str(e) + ' installed')
sys.exit(1)
# Import 3rd party libraries
from filenuke import nuke # noqa

@ -1,9 +0,0 @@
# API Servers
Contains the WSGI servers Onionr uses for remote peer communication and local daemon control
## Files
* \_\_init\_\_.py: Exposes the server classes
* private: Contains the client API (the server used to interact with the local Onionr daemon, and view the web UI)
* public: Contains the public API (the server used by remote peers to talk to our daemon)

@ -1,9 +0,0 @@
"""Flask WSGI apps for the public and private API servers.
Public is net-facing server meant for other nodes
Private is meant for controlling and accessing this node
"""
from . import private
private_api = private.private_api

@ -1,8 +0,0 @@
# Private API Server
Private API server, used to access the web interface locally and control Onionr
## Files
* \_\_init\_\_.py: Sets up the server and a few misc functions
* register_private_blueprints.py: Adds in flask blueprints for various sub-APIs

@ -1,121 +0,0 @@
"""Onionr - Private P2P Communication.
This file handles all incoming http requests to the client, using Flask
"""
from typing import Dict
from typing import Set
from typing import TYPE_CHECKING
import hmac
import flask
from gevent.pywsgi import WSGIServer
from onionrutils import epoch
import httpapi
from filepaths import private_API_host_file
import logger
from onionrutils import waitforsetvar
from . import register_private_blueprints
import config
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
class PrivateAPI:
"""Client HTTP api for controlling onionr and using UI."""
callbacks: Dict[str, Dict] = {'public': {}, 'private': {}}
def __init__(self):
"""Initialize the api server, preping variables for later use.
This initialization defines all of the API entry points
and handlers for the endpoints and errors
This also saves the used host (random localhost IP address)
to the data folder in host.txt
"""
self.config = config
self.startTime = epoch.get_epoch()
app = flask.Flask(__name__)
self.httpServer = ''
self.queueResponse = {}
register_private_blueprints.register_private_blueprints(self, app)
httpapi.load_plugin_blueprints(app)
self.app = app
def start(self):
"""Start client gevent API web server with flask client app."""
fd_handler = httpapi.fdsafehandler.FDSafeHandler
self.clientToken = config.get('client.webpassword')
if config.get('general.bind_address'):
with open(private_API_host_file, 'w') as bindFile:
bindFile.write(config.get('general.bind_address'))
self.host = config.get('general.bind_address')
else:
self.host = httpapi.apiutils.setbindip.set_bind_IP(
private_API_host_file)
bind_port = int(config.get('client.client.port', 59496))
self.bindPort = bind_port
self.httpServer = WSGIServer((self.host, self.bindPort),
self.app, log=None,
handler_class=fd_handler)
logger.info(f'Running API on {self.host}:{self.bindPort}', terminal=True)
self.httpServer.serve_forever()
def setPublicAPIInstance(self, inst):
"""Dynamically set public API instance."""
self.publicAPI = inst
def validateToken(self, token):
"""Validate that the client token matches the given token.
Used to prevent CSRF and other attacks.
"""
if not self.clientToken:
logger.error("client password needs to be set")
return False
try:
return hmac.compare_digest(self.clientToken, token)
except TypeError:
return False
def getUptime(self) -> int:
"""Safely wait for uptime to be set and return it."""
while True:
try:
return epoch.get_epoch() - self.startTime
except (AttributeError, NameError):
# Don't error on race condition with startup
pass
def getBlockData(self, bHash, decrypt=False, raw=False,
headerOnly=False) -> bytes:
"""Returns block data bytes."""
return self.get_block_data.get_block_data(bHash,
decrypt=decrypt,
raw=raw,
headerOnly=headerOnly)
private_api = PrivateAPI()

@ -1,46 +0,0 @@
"""Onionr - Private P2P Communication.
This file registers blueprints for the private api server
"""
from threading import Thread
from gevent import sleep
from httpapi import security, friendsapi, configapi
from httpapi import miscclientapi, apiutils
from httpapi import themeapi
from httpapi import fileoffsetreader
from httpapi.sse.private import private_sse_blueprint
from httpapi import addblock
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
def register_private_blueprints(private_api, app):
"""Register private API plask blueprints."""
app.register_blueprint(security.client.ClientAPISecurity(
private_api).client_api_security_bp)
app.register_blueprint(friendsapi.friends)
app.register_blueprint(configapi.config_BP)
app.register_blueprint(miscclientapi.endpoints.PrivateEndpoints(
private_api).private_endpoints_bp)
app.register_blueprint(apiutils.shutdown.shutdown_bp)
app.register_blueprint(miscclientapi.staticfiles.static_files_bp)
app.register_blueprint(themeapi.theme_blueprint)
app.register_blueprint(private_sse_blueprint)
app.register_blueprint(fileoffsetreader.offset_reader_api)
app.register_blueprint(addblock.blockapi_blueprint)
return app

@ -20,6 +20,7 @@ upload_list = home + 'upload-list.json'
config_file = home + 'config.json'
daemon_mark_file = home + '/daemon-true.txt'
lock_file = home + 'onionr.lock'
pid_file = home + 'onionr.pid'
site_cache = home + 'onionr-sites.txt'

@ -1,13 +0,0 @@
# httpapi
The httpapi contains collections of endpoints for the client and public API servers.
## Files:
configapi: manage onionr configuration from the client http api
friendsapi: add, remove and list friends from the client http api
miscpublicapi: misculanious onionr network interaction from the **public** httpapi, such as announcements, block fetching and uploading.
profilesapi: work in progress in returning a profile page for an Onionr user

@ -1,36 +0,0 @@
"""
Onionr - Private P2P Communication
Register plugins flask blueprints for the client http server
"""
import onionrplugins
import config
from .fdsafehandler import FDSafeHandler
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
def load_plugin_blueprints(flaskapp, blueprint: str = 'flask_blueprint'):
"""Iterate enabled plugins and load any http endpoints they have"""
config.reload()
disabled = config.get('plugins.disabled', [])
for plugin in onionrplugins.get_enabled_plugins():
if plugin in disabled:
continue
plugin = onionrplugins.get_plugin(plugin)
try:
flaskapp.register_blueprint(getattr(plugin, blueprint))
except AttributeError:
pass

@ -1,45 +0,0 @@
"""Onionr - Private P2P Communication.
Serialized APIs
"""
from asyncio.log import logger
import secrets
from flask import Blueprint, Response, request
from onionrblocks import Block
import blockdb
import logger
from gossip import blockqueues
from gossip.constants import BLOCK_ID_SIZE
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
blockapi_blueprint = Blueprint('blockapi', __name__)
# Add a block that we generated (or received from a transport like LAN/sneakernet)
@blockapi_blueprint.route('/addvdfblock', methods=['POST'])
def block_serialized():
stream_to_use = secrets.randbits(1)
req_data = request.data
block_id = req_data[:BLOCK_ID_SIZE]
block_data = req_data[BLOCK_ID_SIZE:]
#blockdb.add_block_to_db(Block(block_id, block_data, auto_verify=False))
blockqueues.gossip_block_queues[stream_to_use].put(
Block(block_id, block_data, auto_verify=False), block=False)
return "ok"

@ -1 +0,0 @@
from . import shutdown, setbindip

@ -1,42 +0,0 @@
import gevent
from gevent import socket, sleep
import secrets, random
import config, logger
import os
# Hacky monkey patch so we can bind random localhosts without gevent trying to switch with an empty hub
socket.getfqdn = lambda n: n
def _get_acceptable_random_number()->int:
"""Return a cryptographically random number in the inclusive range (1, 255)"""
number = 0
while number == 0:
number = secrets.randbelow(0xFF)
return number
def set_bind_IP(filePath=''):
'''Set a random localhost IP to a specified file (intended for private or public API localhost IPs)'''
if config.get('general.random_bind_ip', True):
hostOctets = []
# Build the random localhost address
for i in range(3):
hostOctets.append(str(_get_acceptable_random_number()))
hostOctets = ['127'] + hostOctets
# Convert the localhost address to a normal string address
data = '.'.join(hostOctets)
# Try to bind IP. Some platforms like Mac block non normal 127.x.x.x
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind((data, 0))
except OSError:
# if mac/non-bindable, show warning and default to 127.0.0.1
logger.warn('Your platform appears to not support random local host addresses 127.x.x.x. Falling back to 127.0.0.1.')
data = '127.0.0.1'
s.close()
else:
data = '127.0.0.1'
if filePath != '':
with open(filePath, 'w') as bindFile:
bindFile.write(data)
return data

@ -1,30 +0,0 @@
"""Onionr - Private P2P Communication.
Shutdown the node either hard or cleanly
"""
from flask import Blueprint, Response
from flask import g
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
shutdown_bp = Blueprint('shutdown', __name__)
def shutdown(client_api_inst):
try:
client_api_inst.httpServer.stop()
except AttributeError:
pass
return Response("bye")

@ -1,66 +0,0 @@
"""
Onionr - Private P2P Communication
This file handles configuration setting and getting from the HTTP API
"""
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
from json import JSONDecodeError
import ujson as json
from flask import Blueprint, request, Response, abort
import config, onionrutils
from onionrutils.bytesconverter import bytes_to_str
config.reload()
config_BP = Blueprint('config_BP', __name__)
@config_BP.route('/config/get')
def get_all_config():
"""Simply return all configuration as JSON string"""
return Response(json.dumps(config.get_config(), indent=4, sort_keys=True))
@config_BP.route('/config/get/<key>')
def get_by_key(key):
"""Return a config setting by key"""
return Response(json.dumps(config.get(key)))
@config_BP.route('/config/setall', methods=['POST'])
def set_all_config():
"""Overwrite existing JSON config with new JSON string"""
try:
new_config = request.get_json(force=True)
except JSONDecodeError:
abort(400)
else:
config.set_config(new_config)
config.save()
return Response('success')
@config_BP.route('/config/set/<key>', methods=['POST'])
def set_by_key(key):
"""Overwrite/set only 1 config key"""
"""
{
'data': data
}
"""
try:
data = json.loads(bytes_to_str(request.data))
except (JSONDecodeError, KeyError):
abort(400)
config.set(key, data, True)
return Response('success')

@ -1,16 +0,0 @@
from gevent.pywsgi import WSGIServer, WSGIHandler
from gevent import Timeout
class FDSafeHandler(WSGIHandler):
'''Our WSGI handler. Doesn't do much non-default except timeouts'''
def handle(self):
self.timeout = Timeout(120, Exception)
self.timeout.start()
try:
WSGIHandler.handle(self)
except Timeout as ex:
if ex is self.timeout:
pass
else:
raise

@ -1,30 +0,0 @@
from os.path import exists, dirname
import ujson
from flask import Blueprint, Response, request
from utils.identifyhome import identify_home
from utils.readoffset import read_from_offset
offset_reader_api = Blueprint('offsetreaderapi', __name__)
@offset_reader_api.route('/readfileoffset/<name>')
def offset_reader_endpoint(name):
if not name[:-4].isalnum():
return Response(400, "Path must be alphanumeric except for file ext")
path = identify_home() + name
if not exists(path):
return Response(404, "Path not found in Onionr data directory")
offset = request.args.get('offset')
if not offset:
offset = 0
else:
offset = int(offset)
result = read_from_offset(path, offset)._asdict()
return ujson.dumps(result, reject_bytes=False)

@ -1,75 +0,0 @@
"""Onionr - Private P2P Communication.
This file creates http endpoints for friend management
"""
import ujson as json
from onionrusers import contactmanager
from flask import Blueprint, Response, request, abort, redirect
from coredb import keydb
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
friends = Blueprint('friends', __name__)
@friends.route('/friends/list')
def list_friends():
pubkey_list = {}
friend_list = contactmanager.ContactManager.list_friends()
for friend in friend_list:
pubkey_list[friend.publicKey] = {'name': friend.get_info('name')}
return json.dumps(pubkey_list)
@friends.route('/friends/add/<pubkey>', methods=['POST'])
def add_friend(pubkey):
contactmanager.ContactManager(pubkey, saveUser=True).setTrust(1)
try:
return redirect(request.referrer + '#' + request.form['token'])
except TypeError:
return Response(
"Added, but referrer not set, cannot return to friends page")
@friends.route('/friends/remove/<pubkey>', methods=['POST'])
def remove_friend(pubkey):
contactmanager.ContactManager(pubkey).setTrust(0)
contactmanager.ContactManager(pubkey).delete_contact()
keydb.removekeys.remove_user(pubkey)
try:
return redirect(request.referrer + '#' + request.form['token'])
except TypeError:
return Response(
"Friend removed, but referrer not set, cannot return to page")
@friends.route('/friends/setinfo/<pubkey>/<key>', methods=['POST'])
def set_info(pubkey, key):
data = request.form['data']
contactmanager.ContactManager(pubkey).set_info(key, data)
try:
return redirect(request.referrer + '#' + request.form['token'])
except TypeError:
return Response(
"Info set, but referrer not set, cannot return to friends page")
@friends.route('/friends/getinfo/<pubkey>/<key>')
def get_info(pubkey, key):
ret_data = contactmanager.ContactManager(pubkey).get_info(key)
if ret_data is None:
abort(404)
else:
return ret_data

@ -1,35 +0,0 @@
'''
Onionr - Private P2P Communication
Set default onionr http headers
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
'''
FEATURE_POLICY = """vibrate; vr; webauthn; usb; sync-xhr; speaker;
picture-in-picture; payment; midi; microphone; magnetometer; gyroscope;
geolocation; fullscreen; encrypted-media; document-domain;
camera; accelerometer; ambient-light-sensor""".replace('\n', '') # have to remove \n for flask
def set_default_onionr_http_headers(flask_response):
'''Response headers'''
flask_response.headers['Content-Security-Policy'] = "default-src 'none'; style-src data: 'unsafe-inline'; img-src data:"
flask_response.headers['X-Frame-Options'] = 'deny'
flask_response.headers['X-Content-Type-Options'] = "nosniff"
flask_response.headers['Server'] = ''
flask_response.headers['Date'] = 'Thu, 1 Jan 1970 00:00:00 GMT' # Clock info is probably useful to attackers. Set to unix epoch.
flask_response.headers['Connection'] = "close"
flask_response.headers['Clear-Site-Data'] = '"cache", "cookies", "storage", "executionContexts"'
flask_response.headers['Feature-Policy'] = FEATURE_POLICY
flask_response.headers['Referrer-Policy'] = 'same-origin'
return flask_response

@ -1 +0,0 @@
from . import staticfiles, endpoints

@ -1,111 +0,0 @@
"""Onionr - Private P2P Communication.
Misc client API endpoints too small to need their own file and that need access to the client api inst
"""
import os
import subprocess
import platform
from sys import stdout as sys_stdout
from flask import Response, Blueprint, request, send_from_directory, abort
from flask import g
from gevent import sleep
import unpaddedbase32
from httpapi import apiutils
import onionrcrypto
import config
from onionrutils import mnemonickeys
from onionrutils import bytesconverter
import onionrvalues
from utils import reconstructhash
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
pub_key = onionrcrypto.pub_key.replace('=', '')
SCRIPT_NAME = os.path.dirname(os.path.realpath(__file__)) + \
f'/../../../{onionrvalues.SCRIPT_NAME}'
class PrivateEndpoints:
def __init__(self, client_api):
private_endpoints_bp = Blueprint('privateendpoints', __name__)
self.private_endpoints_bp = private_endpoints_bp
@private_endpoints_bp.route('/www/<path:path>', endpoint='www')
def wwwPublic(path):
if not config.get("www.private.run", True):
abort(403)
return send_from_directory(config.get('www.private.path',
'static-data/www/private/'), path)
@private_endpoints_bp.route('/getpid')
def get_pid():
return Response(str(os.getpid()))
@private_endpoints_bp.route('/isatty')
def get_is_atty():
return Response(str(sys_stdout.isatty()).lower())
@private_endpoints_bp.route('/ping')
def ping():
# Used to check if client api is working
return Response("pong!")
@private_endpoints_bp.route('/shutdown')
def shutdown():
return apiutils.shutdown.shutdown(client_api)
@private_endpoints_bp.route('/restartclean')
def restart_clean():
subprocess.Popen([SCRIPT_NAME, 'restart'])
return Response("bye")
@private_endpoints_bp.route('/getuptime')
def show_uptime():
return Response(str(client_api.getUptime()))
@private_endpoints_bp.route('/getActivePubkey')
def get_active_pubkey():
return Response(pub_key)
@private_endpoints_bp.route('/getHumanReadable')
def get_human_readable_default():
return Response(mnemonickeys.get_human_readable_ID())
@private_endpoints_bp.route('/getHumanReadable/<name>')
def get_human_readable(name):
name = unpaddedbase32.repad(bytesconverter.str_to_bytes(name))
return Response(mnemonickeys.get_human_readable_ID(name))
@private_endpoints_bp.route('/getBase32FromHumanReadable/<words>')
def get_base32_from_human_readable(words):
return Response(
bytesconverter.bytes_to_str(mnemonickeys.get_base32(words)))
@private_endpoints_bp.route('/setonboarding', methods=['POST'])
def set_onboarding():
return Response(
config.onboarding.set_config_from_onboarding(request.get_json()))
@private_endpoints_bp.route('/os')
def get_os_system():
return Response(platform.system().lower())

@ -1,80 +0,0 @@
"""Onionr - Private P2P Communication
Register static file routes
"""
import os
import mimetypes
from flask import Blueprint, send_from_directory
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
# Was having some mime type issues on windows, this appeared to fix it.
# we have no-sniff set, so if the mime types are invalid sripts can't load.
mimetypes.add_type('application/javascript', '.js')
mimetypes.add_type('text/css', '.css')
static_files_bp = Blueprint('staticfiles', __name__)
# should be set to onionr install directory from onionr startup
root = os.path.dirname(os.path.realpath(__file__)) + \
'/../../../static-data/www/'
@static_files_bp.route('/onboarding/', endpoint='onboardingIndex')
def onboard():
return send_from_directory(f'{root}onboarding/', "index.html")
@static_files_bp.route('/onboarding/<path:path>', endpoint='onboarding')
def onboard_files(path):
return send_from_directory(f'{root}onboarding/', path)
@static_files_bp.route('/friends/<path:path>', endpoint='friends')
def loadContacts(path):
return send_from_directory(root + 'friends/', path)
@static_files_bp.route('/friends/', endpoint='friendsindex')
def loadContacts():
return send_from_directory(root + 'friends/', 'index.html')
@static_files_bp.route('/profiles/<path:path>', endpoint='profiles')
def loadContacts(path):
return send_from_directory(root + 'profiles/', path)
@static_files_bp.route('/profiles/', endpoint='profilesindex')
def loadContacts():
return send_from_directory(root + 'profiles/', 'index.html')
@static_files_bp.route('/shared/<path:path>', endpoint='sharedContent')
def sharedContent(path):
return send_from_directory(root + 'shared/', path)
@static_files_bp.route('/', endpoint='onionrhome')
def hello():
# ui home
return send_from_directory(root + 'private/', 'index.html')
@static_files_bp.route('/private/<path:path>', endpoint='homedata')
def homedata(path):
return send_from_directory(root + 'private/', path)

@ -1 +0,0 @@
from . import client

@ -1,102 +0,0 @@
"""Onionr - Private P2P Communication.
Process incoming requests to the client api server to validate
that they are legitimate and not DNSR/XSRF or other local adversary
"""
from ipaddress import ip_address
import hmac
from flask import Blueprint, request, abort, g
from httpapi import httpheaders
from . import pluginwhitelist
import config
import logger
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
# Be extremely mindful of this.
# These are endpoints available without a password
whitelist_endpoints = [
'www', 'staticfiles.homedata',
'staticfiles.sharedContent',
'staticfiles.friends', 'staticfiles.friendsindex', 'siteapi.site',
'siteapi.siteFile', 'staticfiles.onionrhome',
'themes.getTheme', 'staticfiles.onboarding', 'staticfiles.onboardingIndex']
remote_safe_whitelist = ['www', 'staticfiles']
public_remote_enabled = config.get('ui.public_remote_enabled', False)
public_remote_hostnames = config.get('ui.public_remote_hosts', [])
class ClientAPISecurity:
def __init__(self, client_api):
client_api_security_bp = Blueprint('clientapisecurity', __name__)
self.client_api_security_bp = client_api_security_bp
self.client_api = client_api
pluginwhitelist.load_plugin_security_whitelist_endpoints(
whitelist_endpoints)
@client_api_security_bp.before_app_request
def validate_request():
"""Validate request has set password & is the correct hostname."""
# For the purpose of preventing DNS rebinding attacks
if ip_address(client_api.host).is_loopback:
localhost = True
if request.host != '%s:%s' % \
(client_api.host, client_api.bindPort):
localhost = False
if not localhost and public_remote_enabled:
if request.host not in public_remote_hostnames:
logger.warn(
f'{request.host} not in {public_remote_hostnames}')
abort(403)
else:
if not localhost:
logger.warn(
f'Possible DNS rebinding attack by {request.host}')
abort(403)
# Static files for Onionr sites
if request.path.startswith('/site/'):
return
if request.endpoint in whitelist_endpoints:
return
try:
if not hmac.compare_digest(
request.headers['token'], client_api.clientToken):
if not hmac.compare_digest(
request.form['token'], client_api.clientToken):
abort(403)
except KeyError:
if not hmac.compare_digest(
request.form['token'], client_api.clientToken):
abort(403)
@client_api_security_bp.after_app_request
def after_req(resp):
# Security headers
resp = httpheaders.set_default_onionr_http_headers(resp)
if request.endpoint in ('siteapi.site', 'siteapi.siteFile'):
resp.headers['Content-Security-Policy'] = \
"default-src 'none'; style-src 'self' data: 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:" # noqa
else:
resp.headers['Content-Security-Policy'] = \
"default-src 'none'; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self'; frame-src 'none'; font-src 'self'; connect-src 'self'" # noqa
return resp

@ -1,67 +0,0 @@
"""Onionr - Private P2P Communication.
Process incoming requests to the public api server for certain attacks
"""
from flask import Blueprint, request, abort, g
from httpapi import httpheaders
from onionrutils import epoch
from lan import getip
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
class LANAPISecurity:
def __init__(self, lan_client):
lan_api_security_bp = Blueprint('lanapisecurity', __name__)
self.lan_api_security_bp = lan_api_security_bp
@lan_api_security_bp.before_app_request
def validate_request():
"""Validate request has the correct hostname"""
# If high security level, deny requests to public
# (HS should be disabled anyway for Tor, but might not be for I2P)
transports = getip.lan_ips
if lan_client.config.get('general.security_level', default=1) > 0:
abort(403)
if request.host not in transports:
# Abort conn if wrong HTTP hostname, to prevent DNS rebinding
abort(403)
lan_client.hitCount += 1 # raise hit count for valid requests
try:
if 'onionr' in request.headers['User-Agent'].lower():
g.is_onionr_client = True
else:
g.is_onionr_client = False
except KeyError:
g.is_onionr_client = False
@lan_api_security_bp.after_app_request
def send_headers(resp):
"""Send api, access control headers"""
resp = httpheaders.set_default_onionr_http_headers(resp)
# Network API version
resp.headers['X-API'] = lan_client.API_VERSION
# Delete some HTTP headers for Onionr user agents
NON_NETWORK_HEADERS = ('Content-Security-Policy', 'X-Frame-Options',
'X-Content-Type-Options', 'Feature-Policy',
'Clear-Site-Data', 'Referrer-Policy')
try:
if g.is_onionr_client:
for header in NON_NETWORK_HEADERS:
del resp.headers[header]
except AttributeError:
abort(403)
lan_client.lastRequest = epoch.get_rounded_epoch(roundS=5)
return resp

@ -1,33 +0,0 @@
"""Onionr - Private P2P Communication.
Load web UI client endpoints into the whitelist from plugins
"""
import onionrplugins
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
def load_plugin_security_whitelist_endpoints(whitelist: list):
"""Accept a list reference of whitelist endpoints from security/client.py and
append plugin's specified endpoints to them by attribute"""
for plugin in onionrplugins.get_enabled_plugins():
try:
plugin = onionrplugins.get_plugin(plugin)
except FileNotFoundError:
continue
try:
whitelist.extend(getattr(plugin, "security_whitelist"))
except AttributeError:
pass

@ -1,6 +0,0 @@
# sse
This folder contains a wrapper for handling server sent event loops

@ -1,18 +0,0 @@
"""Onionr - Private P2P Communication.
server sent event modules, incl a wrapper and endpoints for client + public api
"""
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""

@ -1,41 +0,0 @@
"""Onionr - Private P2P Communication.
SSE API for node client access
"""
from pathlib import Path
from flask import g, Blueprint
from gevent import sleep
import gevent
import ujson
from onionrutils.epoch import get_epoch
from .. import wrapper
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
private_sse_blueprint = Blueprint('privatesse', __name__)
SSEWrapper = wrapper.SSEWrapper()
gevent.hub.Hub.NOT_ERROR = (gevent.GreenletExit, SystemExit, Exception)
@private_sse_blueprint.route('/hello')
def stream_hello():
def print_hello():
while True:
yield "hello\n\n"
sleep(1)
return SSEWrapper.handle_sse_request(print_hello)

@ -1,34 +0,0 @@
"""Onionr - Private P2P Communication.
wrapper for server sent event endpoints
"""
from typing import Callable
from flask import Response
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
class SSEWrapper:
def __init__(self):
self.active_count: int = 0
def handle_sse_request(self, handler: Callable):
self.active_count += 1
resp = Response(handler())
resp.content_type = "text/event-stream"
self.active_count -= 1
return resp

@ -1,46 +0,0 @@
"""
Onionr - Private P2P Communication
API to get current CSS theme for the client web UI
"""
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <https://www.gnu.org/licenses/>.
"""
from flask import Blueprint, Response
import config
from utils import readstatic
theme_blueprint = Blueprint('themes', __name__)
LIGHT_THEME_FILES = ['bulma-light.min.css', 'styles-light.css']
DARK_THEME_FILES = ['bulma-dark.min.css', 'styles-dark.css']