tfc-mirror/tests/common/test_db_settings.py

253 lines
14 KiB
Python

#!/usr/bin/env python3.7
# -*- coding: utf-8 -*-
"""
TFC - Onion-routed, endpoint secure messaging system
Copyright (C) 2013-2020 Markus Ottela
This file is part of TFC.
TFC 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.
TFC 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 TFC. If not, see <https://www.gnu.org/licenses/>.
"""
import os.path
import unittest
from unittest import mock
from typing import Any
from src.common.db_settings import Settings
from src.common.statics import CLEAR_ENTIRE_SCREEN, CURSOR_LEFT_UP_CORNER, DIR_USER_DATA, RX, SETTING_LENGTH, TX
from tests.mock_classes import ContactList, create_group, GroupList, MasterKey
from tests.utils import cd_unit_test, cleanup, tamper_file, TFCTestCase
class TestSettings(TFCTestCase):
def setUp(self) -> None:
"""Pre-test actions."""
self.unit_test_dir = cd_unit_test()
self.file_name = f"{DIR_USER_DATA}{TX}_settings"
self.master_key = MasterKey()
self.settings = Settings(self.master_key, operation=TX, local_test=False)
self.contact_list = ContactList(nicks=[f'contact_{n}' for n in range(18)])
self.group_list = GroupList(groups=[f'group_{n}' for n in range(18)])
self.group_list.groups[0] = create_group('group_0', [f'contact_{n}' for n in range(18)])
self.args = self.contact_list, self.group_list
def tearDown(self) -> None:
"""Post-test actions."""
cleanup(self.unit_test_dir)
def test_invalid_type_raises_critical_error_on_store(self) -> None:
self.settings.tm_random_delay = b'bytestring'
with self.assertRaises(SystemExit):
self.settings.store_settings()
def test_invalid_type_raises_critical_error_on_load(self) -> None:
with self.assertRaises(SystemExit):
self.settings.nc_bypass_messages = b'bytestring'
self.settings.load_settings()
def test_store_and_load_tx_settings(self) -> None:
# Test store
self.assertFalse(self.settings.disable_gui_dialog)
self.settings.disable_gui_dialog = True
self.settings.store_settings()
self.assertEqual(os.path.getsize(self.file_name), SETTING_LENGTH)
# Test load
settings2 = Settings(self.master_key, TX, False)
self.assertTrue(settings2.disable_gui_dialog)
def test_store_and_load_rx_settings(self) -> None:
# Setup
self.settings = Settings(self.master_key, operation=RX, local_test=False)
# Test store
self.assertFalse(self.settings.disable_gui_dialog)
self.settings.disable_gui_dialog = True
self.settings.store_settings()
self.assertEqual(os.path.getsize(self.file_name), SETTING_LENGTH)
# Test load
settings2 = Settings(self.master_key, RX, False)
self.assertTrue(settings2.disable_gui_dialog)
def test_load_of_modified_database_raises_critical_error(self) -> None:
# Store settings to database
self.settings.store_settings()
# Test reading from database works normally
self.assertIsInstance(Settings(self.master_key, operation=TX, local_test=False), Settings)
# Test loading of the tampered database raises CriticalError
tamper_file(self.file_name, tamper_size=1)
with self.assertRaises(SystemExit):
Settings(self.master_key, operation=TX, local_test=False)
def test_invalid_type_raises_critical_error_when_changing_settings(self) -> None:
self.settings.traffic_masking = b'bytestring'
with self.assertRaises(SystemExit):
self.assertIsNone(self.settings.change_setting('traffic_masking', 'True', *self.args))
def test_change_settings(self) -> None:
self.assert_se("Error: Invalid setting value 'Falsee'.",
self.settings.change_setting, 'disable_gui_dialog', 'Falsee', *self.args)
self.assert_se("Error: Invalid setting value '1.1'.",
self.settings.change_setting, 'max_number_of_group_members', '1.1', *self.args)
self.assert_se("Error: Invalid setting value '18446744073709551616'.",
self.settings.change_setting, 'max_number_of_contacts', str(2 ** 64), *self.args)
self.assert_se("Error: Invalid setting value '-1.1'.",
self.settings.change_setting, 'tm_static_delay', '-1.1', *self.args)
self.assert_se("Error: Invalid setting value 'True'.",
self.settings.change_setting, 'tm_static_delay', 'True', *self.args)
self.assertIsNone(self.settings.change_setting('traffic_masking', 'True', *self.args))
self.assertIsNone(self.settings.change_setting('max_number_of_group_members', '100', *self.args))
@mock.patch('builtins.input', side_effect=['No', 'Yes'])
def test_validate_key_value_pair(self, _: Any) -> None:
self.assert_se("Error: Database padding settings must be divisible by 10.",
self.settings.validate_key_value_pair, 'max_number_of_group_members', 0, *self.args)
self.assert_se("Error: Database padding settings must be divisible by 10.",
self.settings.validate_key_value_pair, 'max_number_of_group_members', 18, *self.args)
self.assert_se("Error: Database padding settings must be divisible by 10.",
self.settings.validate_key_value_pair, 'max_number_of_groups', 18, *self.args)
self.assert_se("Error: Database padding settings must be divisible by 10.",
self.settings.validate_key_value_pair, 'max_number_of_contacts', 18, *self.args)
self.assert_se("Error: Can't set the max number of members lower than 20.",
self.settings.validate_key_value_pair, 'max_number_of_group_members', 10, *self.args)
self.assert_se("Error: Can't set the max number of groups lower than 20.",
self.settings.validate_key_value_pair, 'max_number_of_groups', 10, *self.args)
self.assert_se("Error: Can't set the max number of contacts lower than 20.",
self.settings.validate_key_value_pair, 'max_number_of_contacts', 10, *self.args)
self.assert_se("Error: Too small value for message notify duration.",
self.settings.validate_key_value_pair, 'new_message_notify_duration', 0.04, *self.args)
self.assert_se("Error: Can't set static delay lower than 0.1.",
self.settings.validate_key_value_pair, 'tm_static_delay', 0.01, *self.args)
self.assert_se("Error: Can't set random delay lower than 0.1.",
self.settings.validate_key_value_pair, 'tm_random_delay', 0.01, *self.args)
self.assert_se("Aborted traffic masking setting change.",
self.settings.validate_key_value_pair, 'tm_random_delay', 0.1, *self.args)
self.assertIsNone(self.settings.validate_key_value_pair("serial_baudrate", 9600, *self.args))
self.assertIsNone(self.settings.validate_key_value_pair("tm_static_delay", 1, *self.args))
@mock.patch('shutil.get_terminal_size', return_value=(64, 64))
def test_too_narrow_terminal_raises_fr_when_printing_settings(self, _: Any) -> None:
# Test
self.assert_se("Error: Screen width is too small.", self.settings.print_settings)
def test_print_settings(self) -> None:
self.settings.max_number_of_group_members = 30
self.settings.log_messages_by_default = True
self.settings.tm_static_delay = 10.2
self.assert_prints(CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + """\
Setting name Current value Default value Description
────────────────────────────────────────────────────────────────────────────────
disable_gui_dialog False False True replaces
GUI dialogs with
CLI prompts
max_number_of_group_members 30 50 Maximum number
of members in a
group
max_number_of_groups 50 50 Maximum number
of groups
max_number_of_contacts 50 50 Maximum number
of contacts
log_messages_by_default True False Default logging
setting for new
contacts/groups
accept_files_by_default False False Default file
reception
setting for new
contacts
show_notifications_by_default True True Default message
notification
setting for new
contacts/groups
log_file_masking False False True hides real
size of log file
during traffic
masking
ask_password_for_log_access True True False disables
password prompt
when viewing/exp
orting logs
nc_bypass_messages False False False removes
Networked
Computer bypass
interrupt
messages
confirm_sent_files True True False sends
files without
asking for
confirmation
double_space_exits False False True exits,
False clears
screen with
double space
command
traffic_masking False False True enables
traffic masking
to hide metadata
tm_static_delay 10.2 2.0 The static delay
between traffic
masking packets
tm_random_delay 2.0 2.0 Max random delay
for traffic
masking timing
obfuscation
allow_contact_requests True True When False, does
not show TFC
contact requests
new_message_notify_preview False False When True, shows
a preview of the
received message
new_message_notify_duration 1.0 1.0 Number of
seconds new
message
notification
appears
max_decompress_size 100000000 100000000 Max size
Receiver accepts
when
decompressing
file
""", self.settings.print_settings)
if __name__ == '__main__':
unittest.main(exit=False)