added config observers, delete plaintext blocks when plaintext storage is disabled

This commit is contained in:
Kevin Froman 2020-01-07 05:44:53 -06:00
parent 2e31155a5d
commit f78809fa2a
9 changed files with 156 additions and 116 deletions

View File

@ -1,9 +1,12 @@
''' """Onionr - Private P2P Communication.
Onionr - Private P2P Communication
This file deals with configuration management. This file deals with configuration management.
''' """
''' import os, json, logger
import filepaths
from . import onboarding
"""
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
@ -16,20 +19,14 @@
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, json, logger
import filepaths
from . import onboarding
_configfile = filepaths.config_file _configfile = filepaths.config_file
_config = {} _config = {}
def get(key, default = None, save = False): def get(key, default = None, save = False):
''' """Gets the key from configuration, or returns `default`"""
Gets the key from configuration, or returns `default`
'''
key = str(key).split('.') key = str(key).split('.')
data = _config data = _config
@ -41,20 +38,24 @@ def get(key, default = None, save = False):
return default return default
data = data[item] data = data[item]
if not last in data: if last not in data:
if save: if save:
set(key, default, savefile = True) set(key, default, savefile = True)
return default return default
return data[last] return data[last]
def set(key, value = None, savefile = False): def set(key, value = None, savefile = False):
''' """Sets the key in configuration to `value`"""
Sets the key in configuration to `value` from . import observers
''' config_set_observers = {
'general.store_plaintext_blocks': [observers.delete_plaintext]
}
global _config global _config
whole_key = key
key = str(key).split('.') key = str(key).split('.')
data = _config data = _config
@ -64,6 +65,11 @@ def set(key, value = None, savefile = False):
if (not item in data) or (not type(data[item]) == dict): if (not item in data) or (not type(data[item]) == dict):
data[item] = dict() data[item] = dict()
data = data[item] data = data[item]
try:
for observer in config_set_observers[whole_key]:
observer(value)
except KeyError:
pass
if value is None: if value is None:
del data[last] del data[last]
@ -73,6 +79,7 @@ def set(key, value = None, savefile = False):
if savefile: if savefile:
save() save()
def is_set(key): def is_set(key):
key = str(key).split('.') key = str(key).split('.')
data = _config data = _config
@ -89,18 +96,16 @@ def is_set(key):
return True return True
def check(): def check():
''' """Checks if the configuration file exists, creates it if not"""
Checks if the configuration file exists, creates it if not
'''
if not os.path.exists(os.path.dirname(get_config_file())): if not os.path.exists(os.path.dirname(get_config_file())):
os.makedirs(os.path.dirname(get_config_file())) os.makedirs(os.path.dirname(get_config_file()))
def save(): def save():
''' """Saves the configuration data to the configuration file"""
Saves the configuration data to the configuration file
'''
check() check()
try: try:
@ -109,10 +114,9 @@ def save():
except json.JSONDecodeError: except json.JSONDecodeError:
logger.warn('Failed to write to configuration file.') logger.warn('Failed to write to configuration file.')
def reload(): def reload():
''' """Reloads the configuration data in memory from the file"""
Reloads the configuration data in memory from the file
'''
check() check()
try: try:
with open(get_config_file(), 'r', encoding="utf8") as configfile: with open(get_config_file(), 'r', encoding="utf8") as configfile:
@ -121,28 +125,24 @@ def reload():
pass pass
#logger.debug('Failed to parse configuration file.') #logger.debug('Failed to parse configuration file.')
def get_config(): def get_config():
''' """Gets the entire configuration as an array"""
Gets the entire configuration as an array
'''
return _config return _config
def set_config(config): def set_config(config):
''' """Sets the configuration to the array in arguments"""
Sets the configuration to the array in arguments
'''
global _config global _config
_config = config _config = config
def get_config_file(): def get_config_file():
''' """Returns the absolute path to the configuration file"""
Returns the absolute path to the configuration file
'''
return _configfile return _configfile
def set_config_file(configfile): def set_config_file(configfile):
''' """Sets the path to the configuration file."""
Sets the path to the configuration file
'''
global _configfile global _configfile
_configfile = os.abs.abspath(configfile) _configfile = os.abs.abspath(configfile)

View File

@ -0,0 +1,2 @@
from . import plaintextdelete
delete_plaintext = plaintextdelete.delete_plaintext

View File

@ -0,0 +1,36 @@
"""Onionr - Private P2P Communication.
Delete plaintext blocks, used when plaintext is disabled in config
"""
from onionrblocks import onionrblockapi
from coredb import blockmetadb
from onionrstorage.removeblock import remove_block
import onionrstorage
"""
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 delete_plaintext(dont_delete: bool):
"""Delete, but do not blacklist, plaintext blocks."""
if dont_delete:
return
block_list = blockmetadb.get_block_list()
for block in block_list:
block = onionrblockapi.Block(hash=block)
if not block.isEncrypted:
remove_block(block.hash)
onionrstorage.deleteBlock(block.hash)

View File

@ -1,9 +1,14 @@
''' """
Onionr - Private P2P Communication Onionr - Private P2P Communication
This module works with information relating to blocks stored on the node This module works with information relating to blocks stored on the node
''' """
''' import sqlite3
from etc import onionrvalues
from . import expiredblocks, updateblockinfo, add
from .. import dbfiles
"""
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
@ -16,19 +21,16 @@
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 sqlite3
from etc import onionrvalues
from . import expiredblocks, updateblockinfo, add
from .. import dbfiles
update_block_info = updateblockinfo.update_block_info update_block_info = updateblockinfo.update_block_info
add_to_block_DB = add.add_to_block_DB add_to_block_DB = add.add_to_block_DB
def get_block_list(dateRec = None, unsaved = False): def get_block_list(dateRec = None, unsaved = False):
''' """
Get list of our blocks Get list of our blocks
''' """
if dateRec == None: if dateRec == None:
dateRec = 0 dateRec = 0
@ -44,10 +46,11 @@ def get_block_list(dateRec = None, unsaved = False):
conn.close() conn.close()
return rows return rows
def get_block_date(blockHash): def get_block_date(blockHash):
''' """
Returns the date a block was received Returns the date a block was received
''' """
conn = sqlite3.connect(dbfiles.block_meta_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT) conn = sqlite3.connect(dbfiles.block_meta_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT)
c = conn.cursor() c = conn.cursor()
@ -60,10 +63,11 @@ def get_block_date(blockHash):
conn.close() conn.close()
return None return None
def get_blocks_by_type(blockType, orderDate=True): def get_blocks_by_type(blockType, orderDate=True):
''' """
Returns a list of blocks by the type Returns a list of blocks by the type
''' """
conn = sqlite3.connect(dbfiles.block_meta_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT) conn = sqlite3.connect(dbfiles.block_meta_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT)
c = conn.cursor() c = conn.cursor()
@ -80,4 +84,5 @@ def get_blocks_by_type(blockType, orderDate=True):
for i in row: for i in row:
rows.append(i) rows.append(i)
conn.close() conn.close()
return rows return rows

View File

@ -95,7 +95,7 @@ class Block:
# Check for replay attacks # Check for replay attacks
try: try:
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60: if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60:
if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack
except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e: except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e:
if not self.bypassReplayCheck: if not self.bypassReplayCheck:
# Zero out variables to prevent reading of replays # Zero out variables to prevent reading of replays
@ -187,7 +187,7 @@ class Block:
self.date = datetime.datetime.fromtimestamp(self.getDate()) self.date = datetime.datetime.fromtimestamp(self.getDate())
self.valid = True self.valid = True
if self.autoDecrypt: if self.autoDecrypt:
self.decrypt() self.decrypt()
@ -462,61 +462,6 @@ class Block:
return self return self
# static functions # static functions
def getBlocks(type = None, signer = None, signed = None, reverse = False, limit = None):
'''
Returns a list of Block objects based on supplied filters
Inputs:
- type (str): filters by block type
- signer (str/list): filters by signer (one in the list has to be a signer)
- signed (bool): filters out by whether or not the block is signed
- reverse (bool): reverses the list if True
Outputs:
- (list): a list of Block objects that match the input
'''
try:
relevant_blocks = list()
blocks = (blockmetadb.get_block_list() if type is None else blockmetadb.get_blocks_by_type(type))
for block in blocks:
if Block.exists(block):
block = Block(block)
relevant = True
if (not signed is None) and (block.isSigned() != bool(signed)):
relevant = False
if not signer is None:
if isinstance(signer, (str,)):
signer = [signer]
if isinstance(signer, (bytes,)):
signer = [signer.decode()]
isSigner = False
for key in signer:
if block.isSigner(key):
isSigner = True
break
if not isSigner:
relevant = False
if relevant and (limit is None or len(relevant_Blocks) <= int(limit)):
relevant_blocks.append(block)
if bool(reverse):
relevant_blocks.reverse()
return relevant_blocks
except Exception as e:
logger.debug('Failed to get blocks.', error = e)
return list()
def exists(bHash): def exists(bHash):
''' '''
Checks if a block is saved to file or not Checks if a block is saved to file or not
@ -536,7 +481,7 @@ class Block:
if isinstance(bHash, Block): if isinstance(bHash, Block):
bHash = bHash.getHash() bHash = bHash.getHash()
ret = isinstance(onionrstorage.getData(bHash), type(None)) ret = isinstance(onionrstorage.getData(bHash), type(None))
return not ret return not ret

View File

@ -19,7 +19,6 @@
''' '''
import os import os
from . import identifyhome from . import identifyhome
from onionrsetup import dbcreator
import filepaths import filepaths
home = identifyhome.identify_home() home = identifyhome.identify_home()
@ -31,6 +30,8 @@ def create_dirs():
if not os.path.exists(path): if not os.path.exists(path):
os.mkdir(path) os.mkdir(path)
from onionrsetup import dbcreator
for db in dbcreator.create_funcs: for db in dbcreator.create_funcs:
try: try:
db() db()

View File

@ -3,5 +3,3 @@
Onionr has two test suites, this directory's unittests and integration tests. Onionr has two test suites, this directory's unittests and integration tests.
In these unittests, be careful to manage import order. The test home directory environment variable needs to be set before most imports. In these unittests, be careful to manage import order. The test home directory environment variable needs to be set before most imports.

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python3
import sys, os
sys.path.append(".")
sys.path.append("src/")
import unittest, uuid
import json
TEST_DIR = 'testdata/%s-%s' % (uuid.uuid4(), os.path.basename(__file__)) + '/'
print("Test directory:", TEST_DIR)
os.environ["ONIONR_HOME"] = TEST_DIR
from utils import identifyhome, createdirs
from onionrsetup import setup_config
createdirs.create_dirs()
setup_config()
import config
from coredb import blockmetadb
from onionrblocks.insert import insert_block
class TestTemplate(unittest.TestCase):
def test_plaintext_config(self):
b1 = insert_block('test block')
self.assertIn(b1, blockmetadb.get_block_list())
config.set('general.store_plaintext_blocks', False)
self.assertNotIn(b1, blockmetadb.get_block_list())
unittest.main()

25
tests/test_template.py Normal file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
import sys, os
sys.path.append(".")
sys.path.append("src/")
import unittest, uuid
import json
TEST_DIR = 'testdata/%s-%s' % (uuid.uuid4(), os.path.basename(__file__)) + '/'
print("Test directory:", TEST_DIR)
os.environ["ONIONR_HOME"] = TEST_DIR
from utils import identifyhome, createdirs
from onionrsetup import setup_config
createdirs.create_dirs()
setup_config()
class TestTemplate(unittest.TestCase):
'''
Tests both the onionrusers class and the contactmanager (which inherits it)
'''
def test_true(self):
self.assertTrue(True)
unittest.main()