Merge pull request #5 from beardog108/crypto3
Refactor configuration management code
This commit is contained in:
commit
4f28141de6
@ -20,7 +20,7 @@
|
||||
import flask
|
||||
from flask import request, Response, abort
|
||||
from multiprocessing import Process
|
||||
import configparser, sys, random, threading, hmac, hashlib, base64, time, math, os, logger
|
||||
import sys, random, threading, hmac, hashlib, base64, time, math, os, logger, config
|
||||
|
||||
from core import Core
|
||||
import onionrutils, onionrcrypto
|
||||
@ -37,31 +37,32 @@ class API:
|
||||
else:
|
||||
return True
|
||||
|
||||
def __init__(self, config, debug):
|
||||
def __init__(self, debug):
|
||||
'''
|
||||
Initialize the api server, preping variables for later use
|
||||
|
||||
This initilization 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
|
||||
'''
|
||||
if os.path.exists('dev-enabled'):
|
||||
|
||||
config.reload()
|
||||
|
||||
if config.get('devmode', True):
|
||||
self._developmentMode = True
|
||||
logger.set_level(logger.LEVEL_DEBUG)
|
||||
#logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
|
||||
else:
|
||||
self._developmentMode = False
|
||||
logger.set_level(logger.LEVEL_INFO)
|
||||
|
||||
self.config = config
|
||||
self.debug = debug
|
||||
self._privateDelayTime = 3
|
||||
self._core = Core()
|
||||
self._crypto = onionrcrypto.OnionrCrypto(self._core)
|
||||
self._utils = onionrutils.OnionrUtils(self._core)
|
||||
app = flask.Flask(__name__)
|
||||
bindPort = int(self.config['CLIENT']['PORT'])
|
||||
bindPort = int(config.get('CLIENT')['PORT'])
|
||||
self.bindPort = bindPort
|
||||
self.clientToken = self.config['CLIENT']['CLIENT HMAC']
|
||||
self.clientToken = config.get('CLIENT')['CLIENT HMAC']
|
||||
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
||||
logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)
|
||||
|
||||
|
110
onionr/config.py
Normal file
110
onionr/config.py
Normal file
@ -0,0 +1,110 @@
|
||||
'''
|
||||
Onionr - P2P Microblogging Platform & Social network
|
||||
|
||||
This file deals with configuration management.
|
||||
'''
|
||||
'''
|
||||
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/>.
|
||||
'''
|
||||
|
||||
import os, json, logger
|
||||
|
||||
_configfile = os.path.abspath('data/config.json')
|
||||
_config = {}
|
||||
|
||||
def get(key, default = None):
|
||||
'''
|
||||
Gets the key from configuration, or returns `default`
|
||||
'''
|
||||
if is_set(key):
|
||||
return get_config()[key]
|
||||
return default
|
||||
|
||||
def set(key, value = None, savefile = False):
|
||||
'''
|
||||
Sets the key in configuration to `value`
|
||||
'''
|
||||
|
||||
global _config
|
||||
_config[key] = value
|
||||
|
||||
if savefile:
|
||||
save()
|
||||
|
||||
def is_set(key):
|
||||
return key in get_config() and not get_config()[key] is None
|
||||
|
||||
def check():
|
||||
'''
|
||||
Checks if the configuration file exists, creates it if not
|
||||
'''
|
||||
|
||||
try:
|
||||
if not os.path.exists(os.path.dirname(get_config_file())):
|
||||
os.path.mkdirs(os.path.dirname(get_config_file()))
|
||||
if not os.path.isfile(get_config_file()):
|
||||
open(get_config_file(), 'a', encoding="utf8").close()
|
||||
save()
|
||||
except:
|
||||
logger.warn('Failed to check configuration file.')
|
||||
|
||||
def save():
|
||||
'''
|
||||
Saves the configuration data to the configuration file
|
||||
'''
|
||||
|
||||
check()
|
||||
try:
|
||||
with open(get_config_file(), 'w', encoding="utf8") as configfile:
|
||||
json.dump(get_config(), configfile, indent=2, sort_keys=True)
|
||||
except:
|
||||
logger.warn('Failed to write to configuration file.')
|
||||
|
||||
def reload():
|
||||
'''
|
||||
Reloads the configuration data in memory from the file
|
||||
'''
|
||||
|
||||
check()
|
||||
try:
|
||||
with open(get_config_file(), 'r', encoding="utf8") as configfile:
|
||||
set_config(json.loads(configfile.read()))
|
||||
except:
|
||||
logger.warn('Failed to parse configuration file.')
|
||||
|
||||
def get_config():
|
||||
'''
|
||||
Gets the entire configuration as an array
|
||||
'''
|
||||
return _config
|
||||
|
||||
def set_config(config):
|
||||
'''
|
||||
Sets the configuration to the array in arguments
|
||||
'''
|
||||
global _config
|
||||
_config = config
|
||||
|
||||
def get_config_file():
|
||||
'''
|
||||
Returns the absolute path to the configuration file
|
||||
'''
|
||||
return _configfile
|
||||
|
||||
def set_config_file(configfile):
|
||||
'''
|
||||
Sets the path to the configuration file
|
||||
'''
|
||||
global _configfile
|
||||
_configfile = os.abs.abspath(configfile)
|
@ -108,6 +108,21 @@ def get_level():
|
||||
|
||||
return _level
|
||||
|
||||
def set_file(outputfile):
|
||||
'''
|
||||
Set the file to output to, if enabled
|
||||
'''
|
||||
|
||||
global _outputfile
|
||||
_outputfile = outputfile
|
||||
|
||||
def get_file():
|
||||
'''
|
||||
Get the file to output to
|
||||
'''
|
||||
|
||||
return _outputfile
|
||||
|
||||
def raw(data):
|
||||
'''
|
||||
Outputs raw data to console without formatting
|
||||
|
@ -20,8 +20,8 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
import sys, os, configparser, base64, random, getpass, shutil, subprocess, requests, time, logger, platform
|
||||
import api, core, gui
|
||||
import sys, os, base64, random, getpass, shutil, subprocess, requests, time, platform
|
||||
import api, core, gui, config, logger
|
||||
from onionrutils import OnionrUtils
|
||||
from netcontroller import NetController
|
||||
|
||||
@ -39,12 +39,29 @@ class Onionr:
|
||||
Main Onionr class. This is for the CLI program, and does not handle much of the logic.
|
||||
In general, external programs and plugins should not use this class.
|
||||
'''
|
||||
|
||||
try:
|
||||
os.chdir(sys.path[0])
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
if os.path.exists('dev-enabled'):
|
||||
# Load global configuration data
|
||||
|
||||
exists = os.path.exists(config.get_config_file())
|
||||
config.set_config({'devmode': True, 'log.file': True, 'log.console': True, 'log.outputfile': 'data/output.log', 'log.color': True}) # this is the default config, it will be overwritten if a config file already exists. Else, it saves it
|
||||
config.reload() # this will read the configuration file into memory
|
||||
|
||||
settings = 0b000
|
||||
if config.get('log.color', True):
|
||||
settings = settings | logger.USE_ANSI
|
||||
if config.get('log.console', True):
|
||||
settings = settings | logger.OUTPUT_TO_CONSOLE
|
||||
if config.get('log.file', False):
|
||||
settings = settings | logger.OUTPUT_TO_FILE
|
||||
logger.set_file(config.get('log.outputfile', 'data/output.log'))
|
||||
logger.set_settings(settings)
|
||||
|
||||
if config.get('devmode', True):
|
||||
self._developmentMode = True
|
||||
logger.set_level(logger.LEVEL_DEBUG)
|
||||
else:
|
||||
@ -54,7 +71,7 @@ class Onionr:
|
||||
self.onionrCore = core.Core()
|
||||
self.onionrUtils = OnionrUtils(self.onionrCore)
|
||||
|
||||
# Get configuration and Handle commands
|
||||
# Handle commands
|
||||
|
||||
self.debug = False # Whole application debugging
|
||||
|
||||
@ -79,10 +96,8 @@ class Onionr:
|
||||
self.onionrCore.createAddressDB()
|
||||
|
||||
# Get configuration
|
||||
self.config = configparser.ConfigParser()
|
||||
if os.path.exists('data/config.ini'):
|
||||
self.config.read('data/config.ini')
|
||||
else:
|
||||
|
||||
if not exists:
|
||||
# Generate default config
|
||||
# Hostname should only be set if different from 127.x.x.x. Important for DNS rebinding attack prevention.
|
||||
if self.debug:
|
||||
@ -92,9 +107,7 @@ class Onionr:
|
||||
randomPort = random.randint(1024, 65535)
|
||||
if self.onionrUtils.checkPort(randomPort):
|
||||
break
|
||||
self.config['CLIENT'] = {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}
|
||||
with open('data/config.ini', 'w') as configfile:
|
||||
self.config.write(configfile)
|
||||
config.set('CLIENT', {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}, True)
|
||||
|
||||
command = ''
|
||||
try:
|
||||
@ -117,13 +130,14 @@ class Onionr:
|
||||
|
||||
def getCommands(self):
|
||||
return {
|
||||
'help': self.showHelp,
|
||||
'version': self.version,
|
||||
'config': self.configure,
|
||||
'start': self.start,
|
||||
'stop': self.killDaemon,
|
||||
'version': self.version,
|
||||
'stats': self.showStats,
|
||||
'listpeers': self.listPeers,
|
||||
'list-peers': self.listPeers,
|
||||
'stats': self.showStats,
|
||||
'help': self.showHelp,
|
||||
'': self.showHelpSuggestion,
|
||||
'addmsg': self.addMessage,
|
||||
'addmessage': self.addMessage,
|
||||
@ -139,6 +153,7 @@ class Onionr:
|
||||
return {
|
||||
'help': 'Displays this Onionr help menu',
|
||||
'version': 'Displays the Onionr version',
|
||||
'config': 'Configures something and adds it to the file',
|
||||
'start': 'Starts the Onionr daemon',
|
||||
'stop': 'Stops the Onionr daemon',
|
||||
'stats': 'Displays node statistics',
|
||||
@ -149,6 +164,23 @@ class Onionr:
|
||||
'gui': 'Opens a graphical interface for Onionr'
|
||||
}
|
||||
|
||||
def configure(self):
|
||||
'''
|
||||
Displays something from the configuration file, or sets it
|
||||
'''
|
||||
|
||||
if len(sys.argv) >= 4:
|
||||
config.reload()
|
||||
config.set(sys.argv[2], sys.argv[3], True)
|
||||
logger.debug('Configuration file updated.')
|
||||
elif len(sys.argv) >= 3:
|
||||
config.reload()
|
||||
logger.info(logger.colors.bold + sys.argv[2] + ': ' + logger.colors.reset + str(config.get(sys.argv[2], logger.colors.fg.red + 'Not set.')))
|
||||
else:
|
||||
logger.info(logger.colors.bold + 'Get a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key>')
|
||||
logger.info(logger.colors.bold + 'Set a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key> <value>')
|
||||
|
||||
|
||||
def execute(self, argument):
|
||||
'''
|
||||
Executes a command
|
||||
@ -271,7 +303,7 @@ class Onionr:
|
||||
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
||||
if self._developmentMode:
|
||||
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
|
||||
net = NetController(self.config['CLIENT']['PORT'])
|
||||
net = NetController(config.get('CLIENT')['PORT'])
|
||||
logger.info('Tor is starting...')
|
||||
if not net.startTor():
|
||||
sys.exit(1)
|
||||
@ -280,7 +312,7 @@ class Onionr:
|
||||
time.sleep(1)
|
||||
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
|
||||
logger.debug('Started communicator')
|
||||
api.API(self.config, self.debug)
|
||||
api.API(self.debug)
|
||||
|
||||
return
|
||||
|
||||
@ -290,7 +322,7 @@ class Onionr:
|
||||
'''
|
||||
|
||||
logger.warn('Killing the running daemon')
|
||||
net = NetController(self.config['CLIENT']['PORT'])
|
||||
net = NetController(config.get('CLIENT')['PORT'])
|
||||
try:
|
||||
self.onionrUtils.localCommand('shutdown')
|
||||
except requests.exceptions.ConnectionError:
|
||||
|
@ -18,30 +18,34 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
# Misc functions that do not fit in the main api, but are useful
|
||||
import getpass, sys, requests, configparser, os, socket, hashlib, logger, sqlite3
|
||||
import getpass, sys, requests, os, socket, hashlib, logger, sqlite3, config
|
||||
import nacl.signing, nacl.encoding
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
try:
|
||||
import sha3
|
||||
except ModuleNotFoundError:
|
||||
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
|
||||
sys.exit(1)
|
||||
|
||||
class OnionrUtils:
|
||||
'''Various useful functions'''
|
||||
'''
|
||||
Various useful function
|
||||
'''
|
||||
def __init__(self, coreInstance):
|
||||
self.fingerprintFile = 'data/own-fingerprint.txt'
|
||||
self._core = coreInstance
|
||||
return
|
||||
|
||||
def localCommand(self, command):
|
||||
'''
|
||||
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
|
||||
'''
|
||||
config = configparser.ConfigParser()
|
||||
if os.path.exists('data/config.ini'):
|
||||
config.read('data/config.ini')
|
||||
else:
|
||||
return
|
||||
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config['CLIENT']['PORT']) + '/client/?action=' + command + '&token=' + config['CLIENT']['CLIENT HMAC'])
|
||||
|
||||
config.reload()
|
||||
|
||||
# TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless.
|
||||
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config.get('CLIENT')['PORT']) + '/client/?action=' + command + '&token=' + config.get('CLIENT')['CLIENT HMAC'])
|
||||
|
||||
return
|
||||
|
||||
@ -141,7 +145,7 @@ class OnionrUtils:
|
||||
retVal = False
|
||||
|
||||
return retVal
|
||||
|
||||
|
||||
def validatePubKey(self, key):
|
||||
'''Validate if a string is a valid base32 encoded Ed25519 key'''
|
||||
retVal = False
|
||||
@ -195,5 +199,5 @@ class OnionrUtils:
|
||||
retVal = False
|
||||
if not idNoDomain.isalnum():
|
||||
retVal = False
|
||||
|
||||
return retVal
|
||||
|
||||
return retVal
|
||||
|
Loading…
Reference in New Issue
Block a user