From f81c50b1fc8c085c302c97f46edf688bda61ea3f Mon Sep 17 00:00:00 2001 From: Stefano Dell'Osa Date: Tue, 23 Dec 2025 11:30:20 +0100 Subject: [PATCH 1/6] Low resolution icons improved --- wadas/icons/icon-actuator-24.png | Bin 716 -> 19578 bytes wadas/icons/icon-ai-24.png | Bin 390 -> 10573 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/wadas/icons/icon-actuator-24.png b/wadas/icons/icon-actuator-24.png index 6f61d5577dd2a82816338f1b59aae04a0cfd3ff1..523ac635a77c4c706c29d8163bc753a08000381f 100644 GIT binary patch literal 19578 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 literal 390 zcmV;10eSw3P)Qu}h zfFc!eq)U4Qo`JgJw(?uxDiv@6+ygrTZF*zNYSDm2;0-th8o)cy^~RlAG+b>y{6B{RB=C36r;;})Hq)$07*qoM6N<$g4Us#qW}N^ From 0afa407fef02666ec1f56b6ddd57e023bc9a7c6e Mon Sep 17 00:00:00 2001 From: Stefano Dell'Osa Date: Fri, 26 Dec 2025 10:47:22 +0100 Subject: [PATCH 2/6] Removed unneeded validation at configure web interfacedialog initialization --- wadas/ui/configure_web_interface.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/wadas/ui/configure_web_interface.py b/wadas/ui/configure_web_interface.py index 8d7219f6..3cb99372 100644 --- a/wadas/ui/configure_web_interface.py +++ b/wadas/ui/configure_web_interface.py @@ -163,8 +163,6 @@ def initialize_dialog(self): role_cb = self.findChild(QComboBox, f"comboBox_role_{i}") role_txt = role if role in self.roles else "Viewer" role_cb.setCurrentText(role_txt) - - self.validate() else: self.findChild(QLineEdit, "lineEdit_user_0").setEnabled(False) self.findChild(QLineEdit, "lineEdit_password_0").setEnabled(False) From a25845eb8117b2a5a8f2e1b1ff8bbaa6de2f8c47 Mon Sep 17 00:00:00 2001 From: Stefano Dell'Osa Date: Sat, 3 Jan 2026 22:30:49 +0100 Subject: [PATCH 3/6] Introduced basic check to verify pem key and certificate file content --- wadas/domain/utils.py | 34 ++++++++++++++++++++++++ wadas/ui/configure_actuators_dialog.py | 13 +++++++++ wadas/ui/configure_ftp_cameras_dialog.py | 13 +++++++++ 3 files changed, 60 insertions(+) diff --git a/wadas/domain/utils.py b/wadas/domain/utils.py index eac8b876..1c6faa5e 100644 --- a/wadas/domain/utils.py +++ b/wadas/domain/utils.py @@ -120,3 +120,37 @@ def send_data_on_local_socket(port, command): return data finally: client_socket.close() + + +def is_pem_key(pem_key_file): + """Method to validate pem key file content looking for key template pattern(s)""" + + with open(pem_key_file, "rb") as key_file: + data = key_file.read() + + # Check for private key markers + private_key_markers = [ + b"-----BEGIN PRIVATE KEY-----", + b"-----BEGIN RSA PRIVATE KEY-----", + b"-----BEGIN EC PRIVATE KEY-----", + b"-----BEGIN DSA PRIVATE KEY-----", + b"-----BEGIN ENCRYPTED PRIVATE KEY-----", + ] + + if not any(marker in data for marker in private_key_markers): + return False + else: + return True + + +def is_pem_certificate(pem_certificate_file): + """Method to validate pem certificate file content looking for key template pattern(s)""" + + with open(pem_certificate_file, "rb") as certificate_file: + data = certificate_file.read() + + # Check for certificate pattern + if b"-----BEGIN CERTIFICATE-----" not in data: + return False + else: + return True diff --git a/wadas/ui/configure_actuators_dialog.py b/wadas/ui/configure_actuators_dialog.py index 4c83a9a0..8a120858 100644 --- a/wadas/ui/configure_actuators_dialog.py +++ b/wadas/ui/configure_actuators_dialog.py @@ -26,6 +26,8 @@ from wadas.domain.fastapi_actuator_server import FastAPIActuatorServer, initialize_fastapi_logger from wadas.domain.feeder_actuator import FeederActuator from wadas.domain.roadsign_actuator import RoadSignActuator +from wadas.domain.utils import is_pem_certificate, is_pem_key +from wadas.ui.error_message_dialog import WADASErrorMessage from wadas.ui.qt.ui_configure_actuators import Ui_DialogConfigureActuators from wadas.ui.qtextedit_logger import QTextEditLogger @@ -254,6 +256,12 @@ def select_key_file(self): self, "Open SSL key file", os.getcwd(), "Pem File (*.pem)" ) self.ui.label_key_file.setText(str(file_name[0])) + + # Verify key file + if not is_pem_key(file_name[0]): + WADASErrorMessage("Invalid key file provided", + f"Error while loading key file. Please make sure selected file is a valid one.").exec() + self.validate() def select_certificate_file(self): @@ -263,6 +271,11 @@ def select_certificate_file(self): self, "Open SSL certificate file", os.getcwd(), "Pem File (*.pem)" ) self.ui.label_cert_file.setText(str(file_name[0])) + + if not is_pem_certificate(file_name[0]): + WADASErrorMessage("Invalid certificate file provided", + f"Error while loading certificate file. Please make sure selected file is a valid one").exec() + self.validate() def get_actuator_id(self, row): diff --git a/wadas/ui/configure_ftp_cameras_dialog.py b/wadas/ui/configure_ftp_cameras_dialog.py index 2aaca7f1..4689d6d4 100644 --- a/wadas/ui/configure_ftp_cameras_dialog.py +++ b/wadas/ui/configure_ftp_cameras_dialog.py @@ -42,6 +42,8 @@ from wadas.domain.database import DataBase from wadas.domain.ftp_camera import FTPCamera from wadas.domain.ftps_server import FTPsServer +from wadas.domain.utils import is_pem_certificate, is_pem_key +from wadas.ui.error_message_dialog import WADASErrorMessage from wadas.ui.qt.ui_configure_ftp_cameras import Ui_DialogFTPCameras from wadas.ui.qtextedit_logger import QTextEditLogger @@ -328,6 +330,12 @@ def select_key_file(self): self, "Open SSL key file", os.getcwd(), "Pem File (*.pem)" ) self.ui.label_key_file_path.setText(str(file_name[0])) + + # Verify key file + if not is_pem_key(file_name[0]): + WADASErrorMessage("Invalid key file provided", + f"Error while loading key file. Please make sure selected file is a valid one.").exec() + self.validate() def select_certificate_file(self): @@ -337,6 +345,11 @@ def select_certificate_file(self): self, "Open SSL certificate file", os.getcwd(), "Pem File (*.pem)" ) self.ui.label_certificate_file_path.setText(str(file_name[0])) + + if not is_pem_certificate(file_name[0]): + WADASErrorMessage("Invalid certificate file provided", + f"Error while loading certificate file. Please make sure selected file is a valid one").exec() + self.validate() def select_ftp_folder(self): From a4265207f1341cb671357e507d3535758597b8c8 Mon Sep 17 00:00:00 2001 From: Stefano Dell'Osa Date: Sat, 3 Jan 2026 22:38:57 +0100 Subject: [PATCH 4/6] improved camera to actuator association tree view --- wadas/ui/configure_camera_actuator_associations_dialog.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wadas/ui/configure_camera_actuator_associations_dialog.py b/wadas/ui/configure_camera_actuator_associations_dialog.py index c2a039fb..a4a90cc2 100644 --- a/wadas/ui/configure_camera_actuator_associations_dialog.py +++ b/wadas/ui/configure_camera_actuator_associations_dialog.py @@ -52,6 +52,9 @@ def __init__(self): # Set the model to the QTreeView self.ui.treeView.setModel(self.model) + # Expand all items by default + self.ui.treeView.expandAll() + # Signal self.ui.treeView.doubleClicked.connect(self.handle_double_click) self.ui.pushButton_close.clicked.connect(self.accept_and_close) @@ -72,6 +75,7 @@ def handle_double_click(self, index): dialog.exec() # Refresh the camera item in the tree view after editing actuators self.populate_model() + self.ui.treeView.expandAll() def populate_model(self): From 3621a6bdcd8faf523cbdb02ac23fc818b48c32da Mon Sep 17 00:00:00 2001 From: Stefano Dell'Osa Date: Thu, 8 Jan 2026 21:11:10 +0100 Subject: [PATCH 5/6] addressed PR comments --- wadas/domain/utils.py | 54 ++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/wadas/domain/utils.py b/wadas/domain/utils.py index 1c6faa5e..4f43e6ac 100644 --- a/wadas/domain/utils.py +++ b/wadas/domain/utils.py @@ -26,6 +26,10 @@ import uuid from logging.handlers import RotatingFileHandler +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization + def get_timestamp(): """Method to prepare timestamp string to apply to images naming""" @@ -122,35 +126,33 @@ def send_data_on_local_socket(port, command): client_socket.close() -def is_pem_key(pem_key_file): - """Method to validate pem key file content looking for key template pattern(s)""" - - with open(pem_key_file, "rb") as key_file: - data = key_file.read() - - # Check for private key markers - private_key_markers = [ - b"-----BEGIN PRIVATE KEY-----", - b"-----BEGIN RSA PRIVATE KEY-----", - b"-----BEGIN EC PRIVATE KEY-----", - b"-----BEGIN DSA PRIVATE KEY-----", - b"-----BEGIN ENCRYPTED PRIVATE KEY-----", - ] +def is_pem_key( + pem_key_file: str, + password: bytes | None = None, +) -> bool: + """Method to validate pem key file content.""" - if not any(marker in data for marker in private_key_markers): - return False - else: + try: + with open(pem_key_file, "rb") as f: + serialization.load_pem_private_key( + f.read(), + password=password, + backend=default_backend(), + ) return True + except Exception: + return False -def is_pem_certificate(pem_certificate_file): - """Method to validate pem certificate file content looking for key template pattern(s)""" - - with open(pem_certificate_file, "rb") as certificate_file: - data = certificate_file.read() +def is_pem_certificate(pem_certificate_file) -> bool: + """Method to validate pem certificate file content.""" - # Check for certificate pattern - if b"-----BEGIN CERTIFICATE-----" not in data: - return False - else: + try: + with open(pem_certificate_file, "rb") as f: + x509.load_pem_x509_certificate( + f.read(), + backend=default_backend(), + ) return True + except Exception: + return False From ab9c34b15b14aee1cb1c2d4c0f99f23a49654a59 Mon Sep 17 00:00:00 2001 From: Stefano Dell'Osa Date: Fri, 9 Jan 2026 12:30:42 +0100 Subject: [PATCH 6/6] version increased --- wadas/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadas/_version.py b/wadas/_version.py index 8505912b..0cb32e65 100644 --- a/wadas/_version.py +++ b/wadas/_version.py @@ -17,5 +17,5 @@ # Date: 2024-08-14 # Description: module to keep track of WADAS version -__version__ = "v1.0.0.a3" +__version__ = "v1.0.0.a5" __dbversion__ = __version__