update: new settings connection tab

This commit is contained in:
JOhn 2026-07-26 19:01:07 -04:00
parent 2e026c5287
commit 97f09ec42e
4 changed files with 216 additions and 49 deletions

0
gui/__main__.py Normal file → Executable file
View file

View file

@ -5,7 +5,6 @@ from core.Constants import Constants
from core.controllers.ConfigurationController import ConfigurationController from core.controllers.ConfigurationController import ConfigurationController
from core.controllers.PolicyController import PolicyController from core.controllers.PolicyController import PolicyController
from core.controllers.ProfileController import ProfileController from core.controllers.ProfileController import ProfileController
from core.services.networking.systemwide.systemwide_utils import get_firewall_setting
from gui.v2.actions.config import load_gui_config from gui.v2.actions.config import load_gui_config
from gui.v2.actions.profile_order import normalize_profile_order from gui.v2.actions.profile_order import normalize_profile_order
@ -13,15 +12,28 @@ from gui.v2.actions.ticket_failure import get_ticket_verification_failure
def read_firewall_setting(): def read_connection_setting(gui_config_file, key):
try: try:
firewall_setting = get_firewall_setting() config = load_gui_config(gui_config_file)
return firewall_setting if config and "connection_settings" in config:
return bool(config["connection_settings"].get(key, False))
except Exception: except Exception:
pass pass
return False return False
def read_firewall_setting(gui_config_file=None):
if gui_config_file is None:
return False
return read_connection_setting(gui_config_file, "firewall_enabled")
def read_managed_dns_setting(gui_config_file=None):
if gui_config_file is None:
return False
return read_connection_setting(gui_config_file, "managed_dns_enabled")
def read_systemwide_enabled(): def read_systemwide_enabled():
try: try:
privilege_policy = PolicyController.get('privilege') privilege_policy = PolicyController.get('privilege')
@ -56,7 +68,8 @@ def prepare(gui_config_file):
"endpoint_verification_enabled": endpoint_verification_enabled, "endpoint_verification_enabled": endpoint_verification_enabled,
"systemwide_enabled": read_systemwide_enabled(), "systemwide_enabled": read_systemwide_enabled(),
"bwrap_enabled": read_bwrap_enabled(), "bwrap_enabled": read_bwrap_enabled(),
"firewall_setting": read_firewall_setting(), "firewall_setting": read_firewall_setting(gui_config_file),
"managed_dns_setting": read_managed_dns_setting(gui_config_file),
"gui_config": load_gui_config(gui_config_file), "gui_config": load_gui_config(gui_config_file),
"ticket_failure": get_ticket_verification_failure(gui_config_file), "ticket_failure": get_ticket_verification_failure(gui_config_file),
} }
@ -71,6 +84,7 @@ def empty_payload():
"systemwide_enabled": False, "systemwide_enabled": False,
"bwrap_enabled": False, "bwrap_enabled": False,
"firewall_setting": False, "firewall_setting": False,
"managed_dns_setting": False,
"gui_config": None, "gui_config": None,
"ticket_failure": None, "ticket_failure": None,
} }

View file

@ -19,23 +19,30 @@ class NetworkingSetupPage(Page):
self.btn_path = main_window.btn_path self.btn_path = main_window.btn_path
self.update_status = main_window self.update_status = main_window
self.manual_scripts_ready = False self.manual_scripts_ready = False
self.setStyleSheet("font-family: Arial;")
self.button_next.clicked.disconnect(self.gestionar_next)
self.button_next.setVisible(True) self.button_next.setVisible(True)
self.button_next.clicked.connect(self.go_to_install) self.button_next.clicked.connect(self.go_to_install)
self._setup_ui() self._setup_ui()
def showEvent(self, event):
super().showEvent(event)
self.button_next.setVisible(True)
self.button_next.raise_()
def _setup_ui(self): def _setup_ui(self):
header_icon = self._icon_label("networking_shield.png", 48, self) header_icon = self._icon_label("networking_shield.png", 48, self)
header_icon.setGeometry(74, 66, 48, 48) header_icon.setGeometry(74, 66, 48, 48)
title = QLabel("Firewall & Managed-DNS Setup", self) title = QLabel("Firewall & Managed-DNS Setup", self)
title.setGeometry(138, 68, 590, 44) title.setGeometry(138, 68, 590, 44)
title.setStyleSheet("font-size: 26px; font-weight: bold; color: cyan;") title.setStyleSheet("font-family: Arial; font-size: 26px; font-weight: bold; color: cyan;")
subtitle = QLabel( subtitle = QLabel(
"Optional privileged networking helpers", "Optional privileged networking helpers",
self) self)
subtitle.setGeometry(140, 108, 560, 24) subtitle.setGeometry(140, 108, 560, 24)
subtitle.setStyleSheet("font-size: 14px; color: #d8ffff;") subtitle.setStyleSheet("font-family: Arial; font-size: 14px; color: #d8ffff;")
description = QLabel( description = QLabel(
"These firewall and managed-DNS helpers are separate bash scripts. " "These firewall and managed-DNS helpers are separate bash scripts. "
@ -44,7 +51,7 @@ class NetworkingSetupPage(Page):
self) self)
description.setGeometry(80, 152, 640, 72) description.setGeometry(80, 152, 640, 72)
description.setWordWrap(True) description.setWordWrap(True)
description.setStyleSheet("font-size: 15px; color: cyan;") description.setStyleSheet("font-family: Arial; font-size: 15px; color: cyan;")
manual_panel = self._option_panel(74, 248, "Option 1", "Manual review") manual_panel = self._option_panel(74, 248, "Option 1", "Manual review")
manual_text = QLabel( manual_text = QLabel(
@ -52,7 +59,7 @@ class NetworkingSetupPage(Page):
manual_panel) manual_panel)
manual_text.setGeometry(24, 76, 252, 48) manual_text.setGeometry(24, 76, 252, 48)
manual_text.setWordWrap(True) manual_text.setWordWrap(True)
manual_text.setStyleSheet("font-size: 13px; color: #d8ffff;") manual_text.setStyleSheet("font-family: Arial; font-size: 13px; color: #d8ffff;")
self.manual_button = QPushButton("Get scripts", manual_panel) self.manual_button = QPushButton("Get scripts", manual_panel)
self.manual_button.setGeometry(24, 132, 252, 38) self.manual_button.setGeometry(24, 132, 252, 38)
@ -63,9 +70,9 @@ class NetworkingSetupPage(Page):
auto_text = QLabel( auto_text = QLabel(
"Let HydraVeil run the installer, then test that the scripts reached the sudo folder.", "Let HydraVeil run the installer, then test that the scripts reached the sudo folder.",
auto_panel) auto_panel)
auto_text.setGeometry(24, 76, 252, 48) auto_text.setGeometry(24, 76, 265, 48)
auto_text.setWordWrap(True) auto_text.setWordWrap(True)
auto_text.setStyleSheet("font-size: 13px; color: #d8ffff;") auto_text.setStyleSheet("font-family: Arial; font-size: 13px; color: #d8ffff;")
self.auto_button = QPushButton("Automated installer", auto_panel) self.auto_button = QPushButton("Automated installer", auto_panel)
self.auto_button.setGeometry(24, 132, 252, 38) self.auto_button.setGeometry(24, 132, 252, 38)
@ -75,7 +82,7 @@ class NetworkingSetupPage(Page):
self.status_label = QLabel("Choose manual or automated setup, or press Next to skip and continue.", self) self.status_label = QLabel("Choose manual or automated setup, or press Next to skip and continue.", self)
self.status_label.setGeometry(82, 462, 636, 38) self.status_label.setGeometry(82, 462, 636, 38)
self.status_label.setWordWrap(True) self.status_label.setWordWrap(True)
self.status_label.setStyleSheet("font-size: 13px; color: #d8ffff;") self.status_label.setStyleSheet("font-family: Arial; font-size: 13px; color: #d8ffff;")
def _option_panel(self, x, y, label, title): def _option_panel(self, x, y, label, title):
@ -92,11 +99,11 @@ class NetworkingSetupPage(Page):
option_label = QLabel(label, panel) option_label = QLabel(label, panel)
option_label.setGeometry(24, 20, 120, 20) option_label.setGeometry(24, 20, 120, 20)
option_label.setStyleSheet("font-size: 11px; font-weight: bold; color: #8de9ff; border: none;") option_label.setStyleSheet("font-family: Arial; font-size: 11px; font-weight: bold; color: #8de9ff; border: none;")
title_label = QLabel(title, panel) title_label = QLabel(title, panel)
title_label.setGeometry(24, 42, 240, 28) title_label.setGeometry(24, 42, 240, 28)
title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: cyan; border: none;") title_label.setStyleSheet("font-family: Arial; font-size: 18px; font-weight: bold; color: cyan; border: none;")
return panel return panel
@ -116,6 +123,7 @@ class NetworkingSetupPage(Page):
color: #f4ffff; color: #f4ffff;
border: none; border: none;
border-radius: 4px; border-radius: 4px;
font-family: Arial;
font-size: 12px; font-size: 12px;
font-weight: bold; font-weight: bold;
padding: 4px 8px; padding: 4px 8px;
@ -131,7 +139,7 @@ class NetworkingSetupPage(Page):
def _set_status(self, message, color="#d8ffff"): def _set_status(self, message, color="#d8ffff"):
self.status_label.setText(message) self.status_label.setText(message)
self.status_label.setStyleSheet(f"font-size: 13px; color: {color};") self.status_label.setStyleSheet(f"font-family: Arial; font-size: 13px; color: {color};")
self.update_status.update_status(message) self.update_status.update_status(message)
def _result_is_valid(self, result: Result): def _result_is_valid(self, result: Result):

View file

@ -17,6 +17,7 @@ from core.Constants import Constants
from core.controllers.ConfigurationController import ConfigurationController from core.controllers.ConfigurationController import ConfigurationController
from core.controllers.PolicyController import PolicyController from core.controllers.PolicyController import PolicyController
from core.controllers.ProfileController import ProfileController from core.controllers.ProfileController import ProfileController
from core.controllers.SystemStateController import SystemStateController
from core.controllers.tickets.UseTicketController import ( from core.controllers.tickets.UseTicketController import (
do_we_use_a_random_ticket, do_we_use_a_random_ticket,
get_unused_tickets, get_unused_tickets,
@ -31,7 +32,7 @@ from core.Errors import (
PolicyRevocationError, PolicyRevocationError,
) )
from core.errors.logger import logger as core_logger from core.errors.logger import logger as core_logger
from core.services.networking.systemwide.systemwide_utils import get_firewall_setting from core.services.helpers.setup_sudo_scripts import test_if_in_sudo_folder
from gui.v2.actions import settings_data from gui.v2.actions import settings_data
from gui.v2.actions.key_interpretation import interpret_key_results from gui.v2.actions.key_interpretation import interpret_key_results
@ -1521,8 +1522,12 @@ class Settings(Page):
def show_connection_page(self): def show_connection_page(self):
core_logger.info("User navigated to Settings -> Connection") core_logger.info("User navigated to Settings -> Connection")
if hasattr(self, "connection_gate_status"):
self.load_firewall_settings()
self.load_managed_dns_settings()
self.refresh_connection_settings_gate()
self.content_layout.setCurrentWidget(self.connection_page) self.content_layout.setCurrentWidget(self.connection_page)
self._select_menu_button("Connection Page") self._select_menu_button("Connections")
def show_logs_page(self): def show_logs_page(self):
core_logger.info("User navigated to Settings -> Error Logs") core_logger.info("User navigated to Settings -> Error Logs")
@ -2114,7 +2119,7 @@ class Settings(Page):
def load_firewall_settings(self, enabled=None): def load_firewall_settings(self, enabled=None):
if enabled is None: if enabled is None:
enabled = settings_data.load(read_firewall_setting) enabled = self.read_gui_connection_setting("firewall_enabled")
self.firewall_toggle.setChecked(enabled) self.firewall_toggle.setChecked(enabled)
if enabled: if enabled:
self.firewall_status_value.setText("Enabled") self.firewall_status_value.setText("Enabled")
@ -2126,6 +2131,20 @@ class Settings(Page):
f"color: #e67e22; font-size: 14px; {self.font_style}") f"color: #e67e22; font-size: 14px; {self.font_style}")
def load_managed_dns_settings(self, enabled=None):
if enabled is None:
enabled = self.read_gui_connection_setting("managed_dns_enabled")
self.managed_dns_toggle.setChecked(enabled)
if enabled:
self.managed_dns_status_value.setText("Enabled")
self.managed_dns_status_value.setStyleSheet(
f"color: #2ecc71; font-size: 14px; {self.font_style}")
else:
self.managed_dns_status_value.setText("Disabled")
self.managed_dns_status_value.setStyleSheet(
f"color: #e67e22; font-size: 14px; {self.font_style}")
def save_bwrap_settings(self): def save_bwrap_settings(self):
enable = self.bwrap_toggle.isChecked() enable = self.bwrap_toggle.isChecked()
try: try:
@ -2153,28 +2172,108 @@ class Settings(Page):
f"color: red; font-size: 14px; {self.font_style}") f"color: red; font-size: 14px; {self.font_style}")
def save_firewall_settings(self): def save_connection_settings(self):
enable = self.firewall_toggle.isChecked() can_modify, message, _ = self.connection_settings_gate()
if not can_modify:
self.connection_gate_status.setText(message)
self.connection_gate_status.setStyleSheet(
f"color: #ff6b6b; font-size: 12px; {self.font_style}")
self.set_connection_controls_enabled(False)
self.update_status.update_status(message)
return
firewall_enabled = self.firewall_toggle.isChecked()
managed_dns_enabled = self.managed_dns_toggle.isChecked()
try: try:
if enable: self.save_gui_connection_settings(firewall_enabled, managed_dns_enabled)
ConfigurationController.change_firewall(True) self.load_firewall_settings(firewall_enabled)
self.firewall_status_value.setText("Enabled") self.load_managed_dns_settings(managed_dns_enabled)
else:
ConfigurationController.change_firewall(False)
self.firewall_status_value.setText("Disabled")
self.update_status.update_status( self.update_status.update_status(
"Firewall settings updated") "Connection settings updated")
except CommandNotFoundError as e: except CommandNotFoundError as e:
self.firewall_status_value.setText(str(e)) self.firewall_status_value.setText(str(e))
self.firewall_status_value.setStyleSheet( self.firewall_status_value.setStyleSheet(
f"color: red; font-size: 14px; {self.font_style}") f"color: red; font-size: 14px; {self.font_style}")
except Exception as e: except Exception as e:
print(e) self.firewall_status_value.setText(f"Failed to update connection settings {e}")
self.firewall_status_value.setText(f"Failed to update firewall setting {e}")
self.firewall_status_value.setStyleSheet( self.firewall_status_value.setStyleSheet(
f"color: red; font-size: 14px; {self.font_style}") f"color: red; font-size: 14px; {self.font_style}")
def save_firewall_settings(self):
self.save_connection_settings()
def read_gui_connection_setting(self, key):
try:
config = self.update_status._load_gui_config()
if config and "connection_settings" in config:
return bool(config["connection_settings"].get(key, False))
except Exception:
pass
return False
def save_gui_connection_settings(self, firewall_enabled, managed_dns_enabled):
config = self.update_status._load_gui_config()
if config is None:
config = {}
if "connection_settings" not in config:
config["connection_settings"] = {}
config["connection_settings"]["firewall_enabled"] = firewall_enabled
config["connection_settings"]["managed_dns_enabled"] = managed_dns_enabled
self.update_status._save_gui_config(config)
self._prepared["firewall_setting"] = firewall_enabled
self._prepared["managed_dns_setting"] = managed_dns_enabled
def connection_settings_gate(self):
installed, install_message = self.networking_scripts_installed()
if not installed:
return False, f"Firewall and managed-DNS helpers are not installed. {install_message}", True
if self.has_active_systemwide_profile():
return False, "Disable the active systemwide profile before changing firewall or managed-DNS settings.", False
return True, "Firewall and managed-DNS helpers are installed. No active systemwide profile detected.", False
def networking_scripts_installed(self):
try:
result = test_if_in_sudo_folder()
if result.valid:
return True, "Installed."
return False, result.message
except Exception as e:
return False, str(e)
def has_active_systemwide_profile(self):
try:
return SystemStateController.get() is not None
except Exception:
return True
def set_connection_controls_enabled(self, enabled):
for widget_name in ("firewall_toggle", "managed_dns_toggle", "connection_save_button"):
widget = getattr(self, widget_name, None)
if widget is not None:
widget.setEnabled(enabled)
def refresh_connection_settings_gate(self):
can_modify, message, show_install = self.connection_settings_gate()
self.connection_gate_status.setText(message)
color = "#2ecc71" if can_modify else "#ff6b6b"
self.connection_gate_status.setStyleSheet(
f"color: {color}; font-size: 12px; {self.font_style}")
self.set_connection_controls_enabled(can_modify)
self.connection_install_button.setVisible(show_install)
def open_networking_installer(self):
self.custom_window.navigator.navigate("networking_setup")
def load_registrations_settings(self) -> None: def load_registrations_settings(self) -> None:
try: try:
config = self._prepared_value( config = self._prepared_value(
@ -2242,7 +2341,7 @@ class Settings(Page):
def create_connection_page(self): def create_connection_page(self):
page = QWidget() page = QWidget()
layout = QVBoxLayout(page) layout = QVBoxLayout(page)
layout.setSpacing(20) layout.setSpacing(16)
layout.setContentsMargins(20, 20, 20, 20) layout.setContentsMargins(20, 20, 20, 20)
title = QLabel("CONNECTION PAGE") title = QLabel("CONNECTION PAGE")
@ -2251,43 +2350,89 @@ class Settings(Page):
layout.addWidget(title) layout.addWidget(title)
description = QLabel( description = QLabel(
"Control connection settings such as the firewall.") "Control firewall and managed-DNS behavior for systemwide profiles.")
description.setWordWrap(True) description.setWordWrap(True)
description.setStyleSheet( description.setStyleSheet(
f"color: white; font-size: 14px; {self.font_style}") f"color: white; font-size: 14px; {self.font_style}")
layout.addWidget(description) layout.addWidget(description)
status_layout = QHBoxLayout() self.connection_gate_status = QLabel("")
status_label = QLabel("Current status:") self.connection_gate_status.setWordWrap(True)
status_label.setStyleSheet( self.connection_gate_status.setStyleSheet(
f"color: #e67e22; font-size: 12px; {self.font_style}")
layout.addWidget(self.connection_gate_status)
self.connection_install_button = QPushButton("Install networking helpers")
self.connection_install_button.setFixedSize(190, 38)
self.connection_install_button.setStyleSheet(f"""
QPushButton {{
background: #007AFF;
color: white;
border: none;
border-radius: 5px;
font-weight: bold;
{self.font_style}
}}
QPushButton:hover {{
background: #0056CC;
}}
""")
self.connection_install_button.clicked.connect(self.open_networking_installer)
layout.addWidget(self.connection_install_button)
firewall_status_layout = QHBoxLayout()
firewall_status_label = QLabel("Firewall status:")
firewall_status_label.setStyleSheet(
f"color: white; font-size: 14px; {self.font_style}") f"color: white; font-size: 14px; {self.font_style}")
self.firewall_status_value = QLabel("") self.firewall_status_value = QLabel("")
self.firewall_status_value.setStyleSheet( self.firewall_status_value.setStyleSheet(
f"color: #e67e22; font-size: 14px; {self.font_style}") f"color: #e67e22; font-size: 14px; {self.font_style}")
status_layout.addWidget(status_label) firewall_status_layout.addWidget(firewall_status_label)
status_layout.addWidget(self.firewall_status_value) firewall_status_layout.addWidget(self.firewall_status_value)
status_layout.addStretch() firewall_status_layout.addStretch()
layout.addLayout(status_layout) layout.addLayout(firewall_status_layout)
toggle_layout = QHBoxLayout() dns_status_layout = QHBoxLayout()
dns_status_label = QLabel("Managed-DNS status:")
dns_status_label.setStyleSheet(
f"color: white; font-size: 14px; {self.font_style}")
self.managed_dns_status_value = QLabel("")
self.managed_dns_status_value.setStyleSheet(
f"color: #e67e22; font-size: 14px; {self.font_style}")
dns_status_layout.addWidget(dns_status_label)
dns_status_layout.addWidget(self.managed_dns_status_value)
dns_status_layout.addStretch()
layout.addLayout(dns_status_layout)
firewall_toggle_layout = QHBoxLayout()
self.firewall_toggle = QCheckBox("Enable Firewall Automatically when Systemwide Profiles are Activated") self.firewall_toggle = QCheckBox("Enable Firewall Automatically when Systemwide Profiles are Activated")
self.firewall_toggle.setStyleSheet(self.get_checkbox_style()) self.firewall_toggle.setStyleSheet(self.get_checkbox_style())
toggle_layout.addWidget(self.firewall_toggle) firewall_toggle_layout.addWidget(self.firewall_toggle)
toggle_layout.addStretch() firewall_toggle_layout.addStretch()
layout.addLayout(toggle_layout) layout.addLayout(firewall_toggle_layout)
save_button = QPushButton() dns_toggle_layout = QHBoxLayout()
save_button.setFixedSize(75, 46) self.managed_dns_toggle = QCheckBox("Enable Managed-DNS Automatically when Systemwide Profiles are Activated")
save_button.setIcon(QIcon(os.path.join(self.btn_path, "save.png"))) self.managed_dns_toggle.setStyleSheet(self.get_checkbox_style())
save_button.setIconSize(QSize(75, 46)) dns_toggle_layout.addWidget(self.managed_dns_toggle)
save_button.clicked.connect(self.save_firewall_settings) dns_toggle_layout.addStretch()
layout.addLayout(dns_toggle_layout)
self.connection_save_button = QPushButton()
self.connection_save_button.setFixedSize(75, 46)
self.connection_save_button.setIcon(QIcon(os.path.join(self.btn_path, "save.png")))
self.connection_save_button.setIconSize(QSize(75, 46))
self.connection_save_button.clicked.connect(self.save_connection_settings)
button_layout = QHBoxLayout() button_layout = QHBoxLayout()
button_layout.addWidget(save_button) button_layout.addWidget(self.connection_save_button)
button_layout.addStretch() button_layout.addStretch()
layout.addLayout(button_layout) layout.addLayout(button_layout)
layout.addStretch() layout.addStretch()
self.load_firewall_settings( self.load_firewall_settings(
self._prepared_value("firewall_setting", settings_data.read_firewall_setting)) self._prepared_value("firewall_setting", lambda: self.read_gui_connection_setting("firewall_enabled")))
self.load_managed_dns_settings(
self._prepared_value("managed_dns_setting", lambda: self.read_gui_connection_setting("managed_dns_enabled")))
self.refresh_connection_settings_gate()
return page return page