Merge pull request #831 from micahflee/812_fix_v3_onions

[WIP] Fix v3 onion services
This commit is contained in:
Micah Lee 2018-12-16 17:01:40 -08:00 committed by GitHub
commit a26c457b3c
9 changed files with 326 additions and 185 deletions

View File

@ -373,6 +373,11 @@ class Common(object):
'settings_whats_this': """ 'settings_whats_this': """
QLabel { QLabel {
font-size: 12px; font-size: 12px;
}""",
'settings_connect_to_tor': """
QLabel {
font-style: italic;
}""" }"""
} }

View File

@ -146,6 +146,9 @@ class Onion(object):
# The tor process # The tor process
self.tor_proc = None self.tor_proc = None
# The Tor controller
self.c = None
# Start out not connected to Tor # Start out not connected to Tor
self.connected_to_tor = False self.connected_to_tor = False
@ -387,6 +390,7 @@ class Onion(object):
# Get the tor version # Get the tor version
self.tor_version = self.c.get_version().version_str self.tor_version = self.c.get_version().version_str
self.common.log('Onion', 'connect', 'Connected to tor {}'.format(self.tor_version))
# Do the versions of stem and tor that I'm using support ephemeral onion services? # Do the versions of stem and tor that I'm using support ephemeral onion services?
list_ephemeral_hidden_services = getattr(self.c, "list_ephemeral_hidden_services", None) list_ephemeral_hidden_services = getattr(self.c, "list_ephemeral_hidden_services", None)
@ -403,7 +407,9 @@ class Onion(object):
self.supports_stealth = False self.supports_stealth = False
# Does this version of Tor support next-gen ('v3') onions? # Does this version of Tor support next-gen ('v3') onions?
self.supports_next_gen_onions = self.tor_version > Version('0.3.3.1') # Note, this is the version of Tor where this bug was fixed:
# https://trac.torproject.org/projects/tor/ticket/28619
self.supports_v3_onions = self.tor_version >= Version('0.4.0.0')
def is_authenticated(self): def is_authenticated(self):
""" """
@ -461,7 +467,7 @@ class Onion(object):
else: else:
key_type = "NEW" key_type = "NEW"
# Work out if we can support v3 onion services, which are preferred # Work out if we can support v3 onion services, which are preferred
if Version(self.tor_version) >= Version('0.3.3.1') and not self.settings.get('use_legacy_v2_onions'): if self.supports_v3_onions and not self.settings.get('use_legacy_v2_onions'):
key_content = "ED25519-V3" key_content = "ED25519-V3"
else: else:
# fall back to v2 onion services # fall back to v2 onion services

View File

@ -88,6 +88,10 @@ class SettingsDialog(QtWidgets.QDialog):
self.shutdown_timeout_widget = QtWidgets.QWidget() self.shutdown_timeout_widget = QtWidgets.QWidget()
self.shutdown_timeout_widget.setLayout(shutdown_timeout_layout) self.shutdown_timeout_widget.setLayout(shutdown_timeout_layout)
# Label telling user to connect to Tor for onion service settings
self.connect_to_tor_label = QtWidgets.QLabel(strings._("gui_connect_to_tor_for_onion_settings"))
self.connect_to_tor_label.setStyleSheet(self.common.css['settings_connect_to_tor'])
# Whether or not to use legacy v2 onions # Whether or not to use legacy v2 onions
self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox() self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox()
self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked)
@ -149,16 +153,23 @@ class SettingsDialog(QtWidgets.QDialog):
self.hidservauth_copy_button.clicked.connect(self.hidservauth_copy_button_clicked) self.hidservauth_copy_button.clicked.connect(self.hidservauth_copy_button_clicked)
self.hidservauth_copy_button.hide() self.hidservauth_copy_button.hide()
# Onion settings widget
onion_settings_layout = QtWidgets.QVBoxLayout()
onion_settings_layout.setContentsMargins(0, 0, 0, 0)
onion_settings_layout.addWidget(self.use_legacy_v2_onions_widget)
onion_settings_layout.addWidget(self.save_private_key_widget)
onion_settings_layout.addWidget(self.use_stealth_widget)
onion_settings_layout.addWidget(hidservauth_details)
onion_settings_layout.addWidget(self.hidservauth_copy_button)
self.onion_settings_widget = QtWidgets.QWidget()
self.onion_settings_widget.setLayout(onion_settings_layout)
# General options layout # General options layout
general_group_layout = QtWidgets.QVBoxLayout() general_group_layout = QtWidgets.QVBoxLayout()
general_group_layout.addWidget(self.public_mode_widget) general_group_layout.addWidget(self.public_mode_widget)
general_group_layout.addWidget(self.shutdown_timeout_widget) general_group_layout.addWidget(self.shutdown_timeout_widget)
general_group_layout.addWidget(self.use_legacy_v2_onions_widget) general_group_layout.addWidget(self.connect_to_tor_label)
general_group_layout.addWidget(self.save_private_key_widget) general_group_layout.addWidget(self.onion_settings_widget)
general_group_layout.addWidget(self.use_stealth_widget)
general_group_layout.addWidget(hidservauth_details)
general_group_layout.addWidget(self.hidservauth_copy_button)
general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label")) general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label"))
general_group.setLayout(general_group_layout) general_group.setLayout(general_group_layout)
@ -460,6 +471,9 @@ class SettingsDialog(QtWidgets.QDialog):
self.setLayout(layout) self.setLayout(layout)
self.cancel_button.setFocus() self.cancel_button.setFocus()
self.reload_settings()
def reload_settings(self):
# Load settings, and fill them in # Load settings, and fill them in
self.old_settings = Settings(self.common, self.config) self.old_settings = Settings(self.common, self.config)
self.old_settings.load() self.old_settings.load()
@ -579,6 +593,24 @@ class SettingsDialog(QtWidgets.QDialog):
new_bridges = ''.join(new_bridges) new_bridges = ''.join(new_bridges)
self.tor_bridges_use_custom_textbox.setPlainText(new_bridges) self.tor_bridges_use_custom_textbox.setPlainText(new_bridges)
# If we're connected to Tor, show onion service settings, show label if not
if self.onion.is_authenticated():
self.connect_to_tor_label.hide()
self.onion_settings_widget.show()
# If v3 onion services are supported, allow using legacy mode
if self.onion.supports_v3_onions:
self.common.log('SettingsDialog', '__init__', 'v3 onions are supported')
self.use_legacy_v2_onions_checkbox.show()
else:
self.common.log('SettingsDialog', '__init__', 'v3 onions are not supported')
self.use_legacy_v2_onions_widget.hide()
self.use_legacy_v2_onions_checkbox_clicked(True)
else:
self.connect_to_tor_label.show()
self.onion_settings_widget.hide()
def connection_type_bundled_toggled(self, checked): def connection_type_bundled_toggled(self, checked):
""" """
Connection type bundled was toggled. If checked, hide authentication fields. Connection type bundled was toggled. If checked, hide authentication fields.
@ -754,7 +786,7 @@ class SettingsDialog(QtWidgets.QDialog):
onion.connect(custom_settings=settings, config=self.config, tor_status_update_func=tor_status_update_func) onion.connect(custom_settings=settings, config=self.config, tor_status_update_func=tor_status_update_func)
# If an exception hasn't been raised yet, the Tor settings work # If an exception hasn't been raised yet, the Tor settings work
Alert(self.common, strings._('settings_test_success').format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth, onion.supports_next_gen_onions)) Alert(self.common, strings._('settings_test_success').format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth, onion.supports_v3_onions))
# Clean up # Clean up
onion.cleanup() onion.cleanup()

View File

@ -69,7 +69,7 @@
"error_ephemeral_not_supported": "OnionShare requires at least both Tor 0.2.7.1 and python3-stem 1.4.0.", "error_ephemeral_not_supported": "OnionShare requires at least both Tor 0.2.7.1 and python3-stem 1.4.0.",
"gui_settings_window_title": "Settings", "gui_settings_window_title": "Settings",
"gui_settings_whats_this": "<a href='{0:s}'>What's this?</a>", "gui_settings_whats_this": "<a href='{0:s}'>What's this?</a>",
"gui_settings_stealth_option": "Use client authorization (legacy)", "gui_settings_stealth_option": "Use client authorization",
"gui_settings_stealth_hidservauth_string": "Having saved your private key for reuse, means you can now\nclick to copy your HidServAuth.", "gui_settings_stealth_hidservauth_string": "Having saved your private key for reuse, means you can now\nclick to copy your HidServAuth.",
"gui_settings_autoupdate_label": "Check for new version", "gui_settings_autoupdate_label": "Check for new version",
"gui_settings_autoupdate_option": "Notify me when a new version is available", "gui_settings_autoupdate_option": "Notify me when a new version is available",
@ -135,8 +135,9 @@
"gui_server_started_after_timeout": "The auto-stop timer ran out before the server started.\nPlease make a new share.", "gui_server_started_after_timeout": "The auto-stop timer ran out before the server started.\nPlease make a new share.",
"gui_server_timeout_expired": "The auto-stop timer already ran out.\nPlease update it to start sharing.", "gui_server_timeout_expired": "The auto-stop timer already ran out.\nPlease update it to start sharing.",
"share_via_onionshare": "OnionShare it", "share_via_onionshare": "OnionShare it",
"gui_connect_to_tor_for_onion_settings": "Connect to Tor to see onion service settings",
"gui_use_legacy_v2_onions_checkbox": "Use legacy addresses", "gui_use_legacy_v2_onions_checkbox": "Use legacy addresses",
"gui_save_private_key_checkbox": "Use a persistent address (legacy)", "gui_save_private_key_checkbox": "Use a persistent address",
"gui_share_url_description": "<b>Anyone</b> with this OnionShare address can <b>download</b> your files using the <b>Tor Browser</b>: <img src='{}' />", "gui_share_url_description": "<b>Anyone</b> with this OnionShare address can <b>download</b> your files using the <b>Tor Browser</b>: <img src='{}' />",
"gui_receive_url_description": "<b>Anyone</b> with this OnionShare address can <b>upload</b> files to your computer using the <b>Tor Browser</b>: <img src='{}' />", "gui_receive_url_description": "<b>Anyone</b> with this OnionShare address can <b>upload</b> files to your computer using the <b>Tor Browser</b>: <img src='{}' />",
"gui_url_label_persistent": "This share will not auto-stop.<br><br>Every subsequent share reuses the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)", "gui_url_label_persistent": "This share will not auto-stop.<br><br>Every subsequent share reuses the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)",

View File

@ -1,5 +1,7 @@
import json import json
import os import os
import unittest
from PyQt5 import QtCore, QtTest
from onionshare import strings from onionshare import strings
from onionshare.common import Common from onionshare.common import Common
@ -8,10 +10,27 @@ from onionshare.onion import Onion
from onionshare_gui import Application, OnionShare from onionshare_gui import Application, OnionShare
from onionshare_gui.settings_dialog import SettingsDialog from onionshare_gui.settings_dialog import SettingsDialog
class OnionStub(object):
def __init__(self, is_authenticated, supports_v3_onions):
self._is_authenticated = is_authenticated
self.supports_v3_onions = supports_v3_onions
def is_authenticated(self):
return self._is_authenticated
class SettingsGuiBaseTest(object): class SettingsGuiBaseTest(object):
@staticmethod @staticmethod
def set_up(test_settings): def set_up():
'''Create the GUI''' '''Create the GUI'''
# Default settings for the settings GUI tests
test_settings = {
"no_bridges": False,
"tor_bridges_use_custom_bridges": "Bridge 1.2.3.4:56 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 5.6.7.8:910 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 11.12.13.14:1516 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n",
}
# Create our test file # Create our test file
testfile = open('/tmp/test.txt', 'w') testfile = open('/tmp/test.txt', 'w')
testfile.write('onionshare') testfile.write('onionshare')
@ -37,8 +56,186 @@ class SettingsGuiBaseTest(object):
gui = SettingsDialog(common, testonion, qtapp, '/tmp/settings.json', True) gui = SettingsDialog(common, testonion, qtapp, '/tmp/settings.json', True)
return gui return gui
@staticmethod @staticmethod
def tear_down(): def tear_down():
'''Clean up after tests''' '''Clean up after tests'''
os.remove('/tmp/settings.json') os.remove('/tmp/settings.json')
def run_settings_gui_tests(self):
self.gui.show()
# Window is shown
self.assertTrue(self.gui.isVisible())
self.assertEqual(self.gui.windowTitle(), strings._('gui_settings_window_title'))
# Check for updates button is hidden
self.assertFalse(self.gui.check_for_updates_button.isVisible())
# public mode is off
self.assertFalse(self.gui.public_mode_checkbox.isChecked())
# enable public mode
QtTest.QTest.mouseClick(self.gui.public_mode_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.public_mode_checkbox.height()/2))
self.assertTrue(self.gui.public_mode_checkbox.isChecked())
# shutdown timer is off
self.assertFalse(self.gui.shutdown_timeout_checkbox.isChecked())
# enable shutdown timer
QtTest.QTest.mouseClick(self.gui.shutdown_timeout_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.shutdown_timeout_checkbox.height()/2))
self.assertTrue(self.gui.shutdown_timeout_checkbox.isChecked())
# legacy mode checkbox and related widgets
if self.gui.onion.is_authenticated():
if self.gui.onion.supports_v3_onions:
# legacy mode is off
self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isChecked())
# persistence, stealth is hidden and disabled
self.assertFalse(self.gui.save_private_key_widget.isVisible())
self.assertFalse(self.gui.save_private_key_checkbox.isChecked())
self.assertFalse(self.gui.use_stealth_widget.isVisible())
self.assertFalse(self.gui.stealth_checkbox.isChecked())
self.assertFalse(self.gui.hidservauth_copy_button.isVisible())
# enable legacy mode
QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2))
self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isChecked())
self.assertTrue(self.gui.save_private_key_checkbox.isVisible())
self.assertTrue(self.gui.use_stealth_widget.isVisible())
# enable persistent mode
QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2))
self.assertTrue(self.gui.save_private_key_checkbox.isChecked())
# enable stealth mode
QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2))
self.assertTrue(self.gui.stealth_checkbox.isChecked())
# now that stealth, persistence are enabled, we can't turn off legacy mode
self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isEnabled())
# disable stealth, persistence
QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2))
QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2))
# legacy mode checkbox is enabled again
self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isEnabled())
# uncheck legacy mode
QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2))
# legacy options hidden again
self.assertFalse(self.gui.save_private_key_widget.isVisible())
self.assertFalse(self.gui.use_stealth_widget.isVisible())
# re-enable legacy mode
QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2))
else:
# legacy mode setting is hidden
self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isVisible())
# legacy options are showing
self.assertTrue(self.gui.save_private_key_widget.isVisible())
self.assertTrue(self.gui.use_stealth_widget.isVisible())
# enable them all again so that we can see the setting stick in settings.json
QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2))
QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2))
else:
# None of the onion settings should appear
self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isVisible())
self.assertFalse(self.gui.save_private_key_widget.isVisible())
self.assertFalse(self.gui.save_private_key_checkbox.isChecked())
self.assertFalse(self.gui.use_stealth_widget.isVisible())
self.assertFalse(self.gui.stealth_checkbox.isChecked())
self.assertFalse(self.gui.hidservauth_copy_button.isVisible())
# stay open toggled off, on
self.assertTrue(self.gui.close_after_first_download_checkbox.isChecked())
QtTest.QTest.mouseClick(self.gui.close_after_first_download_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.close_after_first_download_checkbox.height()/2))
self.assertFalse(self.gui.close_after_first_download_checkbox.isChecked())
# receive mode
self.gui.downloads_dir_lineedit.setText('/tmp/OnionShareSettingsTest')
# bundled mode is enabled
self.assertTrue(self.gui.connection_type_bundled_radio.isEnabled())
self.assertTrue(self.gui.connection_type_bundled_radio.isChecked())
# bridge options are shown
self.assertTrue(self.gui.connection_type_bridges_radio_group.isVisible())
# bridges are set to custom
self.assertFalse(self.gui.tor_bridges_no_bridges_radio.isChecked())
self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked())
# switch to obfs4
QtTest.QTest.mouseClick(self.gui.tor_bridges_use_obfs4_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_obfs4_radio.height()/2))
self.assertTrue(self.gui.tor_bridges_use_obfs4_radio.isChecked())
# custom bridges are hidden
self.assertFalse(self.gui.tor_bridges_use_custom_textbox_options.isVisible())
# other modes are unchecked but enabled
self.assertTrue(self.gui.connection_type_automatic_radio.isEnabled())
self.assertTrue(self.gui.connection_type_control_port_radio.isEnabled())
self.assertTrue(self.gui.connection_type_socket_file_radio.isEnabled())
self.assertFalse(self.gui.connection_type_automatic_radio.isChecked())
self.assertFalse(self.gui.connection_type_control_port_radio.isChecked())
self.assertFalse(self.gui.connection_type_socket_file_radio.isChecked())
# enable automatic mode
QtTest.QTest.mouseClick(self.gui.connection_type_automatic_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_automatic_radio.height()/2))
self.assertTrue(self.gui.connection_type_automatic_radio.isChecked())
# bundled is off
self.assertFalse(self.gui.connection_type_bundled_radio.isChecked())
# bridges are hidden
self.assertFalse(self.gui.connection_type_bridges_radio_group.isVisible())
# auth type is hidden in bundled or automatic mode
self.assertFalse(self.gui.authenticate_no_auth_radio.isVisible())
self.assertFalse(self.gui.authenticate_password_radio.isVisible())
# enable control port mode
QtTest.QTest.mouseClick(self.gui.connection_type_control_port_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_control_port_radio.height()/2))
self.assertTrue(self.gui.connection_type_control_port_radio.isChecked())
# automatic is off
self.assertFalse(self.gui.connection_type_automatic_radio.isChecked())
# auth options appear
self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible())
self.assertTrue(self.gui.authenticate_password_radio.isVisible())
# enable socket mode
QtTest.QTest.mouseClick(self.gui.connection_type_socket_file_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_socket_file_radio.height()/2))
self.assertTrue(self.gui.connection_type_socket_file_radio.isChecked())
# control port is off
self.assertFalse(self.gui.connection_type_control_port_radio.isChecked())
# auth options are still present
self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible())
self.assertTrue(self.gui.authenticate_password_radio.isVisible())
# re-enable bundled mode
QtTest.QTest.mouseClick(self.gui.connection_type_bundled_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_bundled_radio.height()/2))
# go back to custom bridges
QtTest.QTest.mouseClick(self.gui.tor_bridges_use_custom_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_custom_radio.height()/2))
self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked())
self.assertTrue(self.gui.tor_bridges_use_custom_textbox.isVisible())
self.assertFalse(self.gui.tor_bridges_use_obfs4_radio.isChecked())
self.gui.tor_bridges_use_custom_textbox.setPlainText('94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\n148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\n93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3')
# Test that the Settings Dialog can save the settings and close itself
QtTest.QTest.mouseClick(self.gui.save_button, QtCore.Qt.LeftButton)
self.assertFalse(self.gui.isVisible())
# Test our settings are reflected in the settings json
with open('/tmp/settings.json') as f:
data = json.load(f)
self.assertTrue(data["public_mode"])
self.assertTrue(data["shutdown_timeout"])
if self.gui.onion.is_authenticated():
if self.gui.onion.supports_v3_onions:
self.assertTrue(data["use_legacy_v2_onions"])
self.assertTrue(data["save_private_key"])
self.assertTrue(data["use_stealth"])
else:
self.assertFalse(data["use_legacy_v2_onions"])
self.assertFalse(data["save_private_key"])
self.assertFalse(data["use_stealth"])
self.assertEqual(data["downloads_dir"], "/tmp/OnionShareSettingsTest")
self.assertFalse(data["close_after_first_download"])
self.assertEqual(data["connection_type"], "bundled")
self.assertFalse(data["tor_bridges_use_obfs4"])
self.assertEqual(data["tor_bridges_use_custom_bridges"], "Bridge 94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\nBridge 148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\nBridge 93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3\n")

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
import unittest
from onionshare import strings
from .SettingsGuiBaseTest import SettingsGuiBaseTest, OnionStub
class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest):
@classmethod
def setUpClass(cls):
cls.gui = SettingsGuiBaseTest.set_up()
@classmethod
def tearDownClass(cls):
SettingsGuiBaseTest.tear_down()
def test_gui_legacy_tor(self):
self.gui.onion = OnionStub(True, False)
self.gui.reload_settings()
self.run_settings_gui_tests()
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
import unittest
from onionshare import strings
from .SettingsGuiBaseTest import SettingsGuiBaseTest, OnionStub
class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest):
@classmethod
def setUpClass(cls):
cls.gui = SettingsGuiBaseTest.set_up()
@classmethod
def tearDownClass(cls):
SettingsGuiBaseTest.tear_down()
def test_gui_no_tor(self):
self.gui.onion = OnionStub(False, False)
self.gui.reload_settings()
self.run_settings_gui_tests()
if __name__ == "__main__":
unittest.main()

View File

@ -1,172 +0,0 @@
#!/usr/bin/env python3
import json
import unittest
from PyQt5 import QtCore, QtTest
from onionshare import strings
from .SettingsGuiBaseTest import SettingsGuiBaseTest
class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest):
@classmethod
def setUpClass(cls):
test_settings = {
"no_bridges": False,
"tor_bridges_use_custom_bridges": "Bridge 1.2.3.4:56 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 5.6.7.8:910 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 11.12.13.14:1516 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n",
}
cls.gui = SettingsGuiBaseTest.set_up(test_settings)
@classmethod
def tearDownClass(cls):
SettingsGuiBaseTest.tear_down()
def test_gui(self):
self.gui.show()
# Window is shown
self.assertTrue(self.gui.isVisible())
self.assertEqual(self.gui.windowTitle(), strings._('gui_settings_window_title'))
# Check for updates button is hidden
self.assertFalse(self.gui.check_for_updates_button.isVisible())
# public mode is off
self.assertFalse(self.gui.public_mode_checkbox.isChecked())
# enable public mode
QtTest.QTest.mouseClick(self.gui.public_mode_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.public_mode_checkbox.height()/2))
self.assertTrue(self.gui.public_mode_checkbox.isChecked())
# shutdown timer is off
self.assertFalse(self.gui.shutdown_timeout_checkbox.isChecked())
# enable shutdown timer
QtTest.QTest.mouseClick(self.gui.shutdown_timeout_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.shutdown_timeout_checkbox.height()/2))
self.assertTrue(self.gui.shutdown_timeout_checkbox.isChecked())
# legacy mode checkbox and related widgets
# legacy mode is off
self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isChecked())
# persistence, stealth is hidden and disabled
self.assertFalse(self.gui.save_private_key_widget.isVisible())
self.assertFalse(self.gui.save_private_key_checkbox.isChecked())
self.assertFalse(self.gui.use_stealth_widget.isVisible())
self.assertFalse(self.gui.stealth_checkbox.isChecked())
self.assertFalse(self.gui.hidservauth_copy_button.isVisible())
# enable legacy mode
QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2))
self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isChecked())
self.assertTrue(self.gui.save_private_key_checkbox.isVisible())
self.assertTrue(self.gui.use_stealth_widget.isVisible())
# enable persistent mode
QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2))
self.assertTrue(self.gui.save_private_key_checkbox.isChecked())
# enable stealth mode
QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2))
self.assertTrue(self.gui.stealth_checkbox.isChecked())
# now that stealth, persistence are enabled, we can't turn off legacy mode
self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isEnabled())
# disable stealth, persistence
QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2))
QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2))
# legacy mode checkbox is enabled again
self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isEnabled())
# uncheck legacy mode
QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2))
# legacy options hidden again
self.assertFalse(self.gui.save_private_key_widget.isVisible())
self.assertFalse(self.gui.use_stealth_widget.isVisible())
# enable them all again so that we can see the setting stick in settings.json
QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2))
QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2))
QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2))
# stay open toggled off, on
self.assertTrue(self.gui.close_after_first_download_checkbox.isChecked())
QtTest.QTest.mouseClick(self.gui.close_after_first_download_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.close_after_first_download_checkbox.height()/2))
self.assertFalse(self.gui.close_after_first_download_checkbox.isChecked())
# receive mode
self.gui.downloads_dir_lineedit.setText('/tmp/OnionShareSettingsTest')
# bundled mode is enabled
self.assertTrue(self.gui.connection_type_bundled_radio.isEnabled())
self.assertTrue(self.gui.connection_type_bundled_radio.isChecked())
# bridge options are shown
self.assertTrue(self.gui.connection_type_bridges_radio_group.isVisible())
# bridges are set to custom
self.assertFalse(self.gui.tor_bridges_no_bridges_radio.isChecked())
self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked())
# switch to obfs4
QtTest.QTest.mouseClick(self.gui.tor_bridges_use_obfs4_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_obfs4_radio.height()/2))
self.assertTrue(self.gui.tor_bridges_use_obfs4_radio.isChecked())
# custom bridges are hidden
self.assertFalse(self.gui.tor_bridges_use_custom_textbox_options.isVisible())
# other modes are unchecked but enabled
self.assertTrue(self.gui.connection_type_automatic_radio.isEnabled())
self.assertTrue(self.gui.connection_type_control_port_radio.isEnabled())
self.assertTrue(self.gui.connection_type_socket_file_radio.isEnabled())
self.assertFalse(self.gui.connection_type_automatic_radio.isChecked())
self.assertFalse(self.gui.connection_type_control_port_radio.isChecked())
self.assertFalse(self.gui.connection_type_socket_file_radio.isChecked())
# enable automatic mode
QtTest.QTest.mouseClick(self.gui.connection_type_automatic_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_automatic_radio.height()/2))
self.assertTrue(self.gui.connection_type_automatic_radio.isChecked())
# bundled is off
self.assertFalse(self.gui.connection_type_bundled_radio.isChecked())
# bridges are hidden
self.assertFalse(self.gui.connection_type_bridges_radio_group.isVisible())
# auth type is hidden in bundled or automatic mode
self.assertFalse(self.gui.authenticate_no_auth_radio.isVisible())
self.assertFalse(self.gui.authenticate_password_radio.isVisible())
# enable control port mode
QtTest.QTest.mouseClick(self.gui.connection_type_control_port_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_control_port_radio.height()/2))
self.assertTrue(self.gui.connection_type_control_port_radio.isChecked())
# automatic is off
self.assertFalse(self.gui.connection_type_automatic_radio.isChecked())
# auth options appear
self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible())
self.assertTrue(self.gui.authenticate_password_radio.isVisible())
# enable socket mode
QtTest.QTest.mouseClick(self.gui.connection_type_socket_file_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_socket_file_radio.height()/2))
self.assertTrue(self.gui.connection_type_socket_file_radio.isChecked())
# control port is off
self.assertFalse(self.gui.connection_type_control_port_radio.isChecked())
# auth options are still present
self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible())
self.assertTrue(self.gui.authenticate_password_radio.isVisible())
# re-enable bundled mode
QtTest.QTest.mouseClick(self.gui.connection_type_bundled_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_bundled_radio.height()/2))
# go back to custom bridges
QtTest.QTest.mouseClick(self.gui.tor_bridges_use_custom_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_custom_radio.height()/2))
self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked())
self.assertTrue(self.gui.tor_bridges_use_custom_textbox.isVisible())
self.assertFalse(self.gui.tor_bridges_use_obfs4_radio.isChecked())
self.gui.tor_bridges_use_custom_textbox.setPlainText('94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\n148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\n93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3')
# Test that the Settings Dialog can save the settings and close itself
QtTest.QTest.mouseClick(self.gui.save_button, QtCore.Qt.LeftButton)
self.assertFalse(self.gui.isVisible())
# Test our settings are reflected in the settings json
with open('/tmp/settings.json') as f:
data = json.load(f)
self.assertTrue(data["public_mode"])
self.assertTrue(data["shutdown_timeout"])
self.assertTrue(data["use_legacy_v2_onions"])
self.assertTrue(data["save_private_key"])
self.assertTrue(data["use_stealth"])
self.assertEqual(data["downloads_dir"], "/tmp/OnionShareSettingsTest")
self.assertFalse(data["close_after_first_download"])
self.assertEqual(data["connection_type"], "bundled")
self.assertFalse(data["tor_bridges_use_obfs4"])
self.assertEqual(data["tor_bridges_use_custom_bridges"], "Bridge 94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\nBridge 148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\nBridge 93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3\n")
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
import unittest
from onionshare import strings
from .SettingsGuiBaseTest import SettingsGuiBaseTest, OnionStub
class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest):
@classmethod
def setUpClass(cls):
cls.gui = SettingsGuiBaseTest.set_up()
@classmethod
def tearDownClass(cls):
SettingsGuiBaseTest.tear_down()
def test_gui_v3_tor(self):
self.gui.onion = OnionStub(True, True)
self.gui.reload_settings()
self.run_settings_gui_tests()
if __name__ == "__main__":
unittest.main()