Merge pull request #5 from beardog108/crypto3
Refactor configuration management code
This commit is contained in:
commit
4f28141de6
@ -20,7 +20,7 @@
|
|||||||
import flask
|
import flask
|
||||||
from flask import request, Response, abort
|
from flask import request, Response, abort
|
||||||
from multiprocessing import Process
|
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
|
from core import Core
|
||||||
import onionrutils, onionrcrypto
|
import onionrutils, onionrcrypto
|
||||||
@ -37,31 +37,32 @@ class API:
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __init__(self, config, debug):
|
def __init__(self, debug):
|
||||||
'''
|
'''
|
||||||
Initialize the api server, preping variables for later use
|
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 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
|
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
|
self._developmentMode = True
|
||||||
logger.set_level(logger.LEVEL_DEBUG)
|
logger.set_level(logger.LEVEL_DEBUG)
|
||||||
#logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
|
|
||||||
else:
|
else:
|
||||||
self._developmentMode = False
|
self._developmentMode = False
|
||||||
logger.set_level(logger.LEVEL_INFO)
|
logger.set_level(logger.LEVEL_INFO)
|
||||||
|
|
||||||
self.config = config
|
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self._privateDelayTime = 3
|
self._privateDelayTime = 3
|
||||||
self._core = Core()
|
self._core = Core()
|
||||||
self._crypto = onionrcrypto.OnionrCrypto(self._core)
|
self._crypto = onionrcrypto.OnionrCrypto(self._core)
|
||||||
self._utils = onionrutils.OnionrUtils(self._core)
|
self._utils = onionrutils.OnionrUtils(self._core)
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
bindPort = int(self.config['CLIENT']['PORT'])
|
bindPort = int(config.get('CLIENT')['PORT'])
|
||||||
self.bindPort = bindPort
|
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":
|
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
||||||
logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)
|
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
|
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):
|
def raw(data):
|
||||||
'''
|
'''
|
||||||
Outputs raw data to console without formatting
|
Outputs raw data to console without formatting
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
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 sys, os, configparser, base64, random, getpass, shutil, subprocess, requests, time, logger, platform
|
import sys, os, base64, random, getpass, shutil, subprocess, requests, time, platform
|
||||||
import api, core, gui
|
import api, core, gui, config, logger
|
||||||
from onionrutils import OnionrUtils
|
from onionrutils import OnionrUtils
|
||||||
from netcontroller import NetController
|
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.
|
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.
|
In general, external programs and plugins should not use this class.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.chdir(sys.path[0])
|
os.chdir(sys.path[0])
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
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
|
self._developmentMode = True
|
||||||
logger.set_level(logger.LEVEL_DEBUG)
|
logger.set_level(logger.LEVEL_DEBUG)
|
||||||
else:
|
else:
|
||||||
@ -54,7 +71,7 @@ class Onionr:
|
|||||||
self.onionrCore = core.Core()
|
self.onionrCore = core.Core()
|
||||||
self.onionrUtils = OnionrUtils(self.onionrCore)
|
self.onionrUtils = OnionrUtils(self.onionrCore)
|
||||||
|
|
||||||
# Get configuration and Handle commands
|
# Handle commands
|
||||||
|
|
||||||
self.debug = False # Whole application debugging
|
self.debug = False # Whole application debugging
|
||||||
|
|
||||||
@ -79,10 +96,8 @@ class Onionr:
|
|||||||
self.onionrCore.createAddressDB()
|
self.onionrCore.createAddressDB()
|
||||||
|
|
||||||
# Get configuration
|
# Get configuration
|
||||||
self.config = configparser.ConfigParser()
|
|
||||||
if os.path.exists('data/config.ini'):
|
if not exists:
|
||||||
self.config.read('data/config.ini')
|
|
||||||
else:
|
|
||||||
# Generate default config
|
# Generate default config
|
||||||
# Hostname should only be set if different from 127.x.x.x. Important for DNS rebinding attack prevention.
|
# Hostname should only be set if different from 127.x.x.x. Important for DNS rebinding attack prevention.
|
||||||
if self.debug:
|
if self.debug:
|
||||||
@ -92,9 +107,7 @@ class Onionr:
|
|||||||
randomPort = random.randint(1024, 65535)
|
randomPort = random.randint(1024, 65535)
|
||||||
if self.onionrUtils.checkPort(randomPort):
|
if self.onionrUtils.checkPort(randomPort):
|
||||||
break
|
break
|
||||||
self.config['CLIENT'] = {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}
|
config.set('CLIENT', {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}, True)
|
||||||
with open('data/config.ini', 'w') as configfile:
|
|
||||||
self.config.write(configfile)
|
|
||||||
|
|
||||||
command = ''
|
command = ''
|
||||||
try:
|
try:
|
||||||
@ -117,13 +130,14 @@ class Onionr:
|
|||||||
|
|
||||||
def getCommands(self):
|
def getCommands(self):
|
||||||
return {
|
return {
|
||||||
|
'help': self.showHelp,
|
||||||
|
'version': self.version,
|
||||||
|
'config': self.configure,
|
||||||
'start': self.start,
|
'start': self.start,
|
||||||
'stop': self.killDaemon,
|
'stop': self.killDaemon,
|
||||||
'version': self.version,
|
'stats': self.showStats,
|
||||||
'listpeers': self.listPeers,
|
'listpeers': self.listPeers,
|
||||||
'list-peers': self.listPeers,
|
'list-peers': self.listPeers,
|
||||||
'stats': self.showStats,
|
|
||||||
'help': self.showHelp,
|
|
||||||
'': self.showHelpSuggestion,
|
'': self.showHelpSuggestion,
|
||||||
'addmsg': self.addMessage,
|
'addmsg': self.addMessage,
|
||||||
'addmessage': self.addMessage,
|
'addmessage': self.addMessage,
|
||||||
@ -139,6 +153,7 @@ class Onionr:
|
|||||||
return {
|
return {
|
||||||
'help': 'Displays this Onionr help menu',
|
'help': 'Displays this Onionr help menu',
|
||||||
'version': 'Displays the Onionr version',
|
'version': 'Displays the Onionr version',
|
||||||
|
'config': 'Configures something and adds it to the file',
|
||||||
'start': 'Starts the Onionr daemon',
|
'start': 'Starts the Onionr daemon',
|
||||||
'stop': 'Stops the Onionr daemon',
|
'stop': 'Stops the Onionr daemon',
|
||||||
'stats': 'Displays node statistics',
|
'stats': 'Displays node statistics',
|
||||||
@ -149,6 +164,23 @@ class Onionr:
|
|||||||
'gui': 'Opens a graphical interface for 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):
|
def execute(self, argument):
|
||||||
'''
|
'''
|
||||||
Executes a command
|
Executes a command
|
||||||
@ -271,7 +303,7 @@ class Onionr:
|
|||||||
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
||||||
if self._developmentMode:
|
if self._developmentMode:
|
||||||
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
|
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...')
|
logger.info('Tor is starting...')
|
||||||
if not net.startTor():
|
if not net.startTor():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -280,7 +312,7 @@ class Onionr:
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
|
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
|
||||||
logger.debug('Started communicator')
|
logger.debug('Started communicator')
|
||||||
api.API(self.config, self.debug)
|
api.API(self.debug)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -290,7 +322,7 @@ class Onionr:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
logger.warn('Killing the running daemon')
|
logger.warn('Killing the running daemon')
|
||||||
net = NetController(self.config['CLIENT']['PORT'])
|
net = NetController(config.get('CLIENT')['PORT'])
|
||||||
try:
|
try:
|
||||||
self.onionrUtils.localCommand('shutdown')
|
self.onionrUtils.localCommand('shutdown')
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
|
@ -18,30 +18,34 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
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
|
# 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
|
import nacl.signing, nacl.encoding
|
||||||
|
|
||||||
if sys.version_info < (3, 6):
|
if sys.version_info < (3, 6):
|
||||||
try:
|
try:
|
||||||
import sha3
|
import sha3
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
|
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
class OnionrUtils:
|
class OnionrUtils:
|
||||||
'''Various useful functions'''
|
'''
|
||||||
|
Various useful function
|
||||||
|
'''
|
||||||
def __init__(self, coreInstance):
|
def __init__(self, coreInstance):
|
||||||
self.fingerprintFile = 'data/own-fingerprint.txt'
|
self.fingerprintFile = 'data/own-fingerprint.txt'
|
||||||
self._core = coreInstance
|
self._core = coreInstance
|
||||||
return
|
return
|
||||||
|
|
||||||
def localCommand(self, command):
|
def localCommand(self, command):
|
||||||
'''
|
'''
|
||||||
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
|
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.reload()
|
||||||
config.read('data/config.ini')
|
|
||||||
else:
|
# TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless.
|
||||||
return
|
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config.get('CLIENT')['PORT']) + '/client/?action=' + command + '&token=' + config.get('CLIENT')['CLIENT HMAC'])
|
||||||
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config['CLIENT']['PORT']) + '/client/?action=' + command + '&token=' + config['CLIENT']['CLIENT HMAC'])
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -141,7 +145,7 @@ class OnionrUtils:
|
|||||||
retVal = False
|
retVal = False
|
||||||
|
|
||||||
return retVal
|
return retVal
|
||||||
|
|
||||||
def validatePubKey(self, key):
|
def validatePubKey(self, key):
|
||||||
'''Validate if a string is a valid base32 encoded Ed25519 key'''
|
'''Validate if a string is a valid base32 encoded Ed25519 key'''
|
||||||
retVal = False
|
retVal = False
|
||||||
@ -195,5 +199,5 @@ class OnionrUtils:
|
|||||||
retVal = False
|
retVal = False
|
||||||
if not idNoDomain.isalnum():
|
if not idNoDomain.isalnum():
|
||||||
retVal = False
|
retVal = False
|
||||||
|
|
||||||
return retVal
|
return retVal
|
||||||
|
Loading…
Reference in New Issue
Block a user