Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into alessandro/fix/ftps_server_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hexfati committed Nov 20, 2024
2 parents fc48954 + ea8058a commit a0445b3
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 127 deletions.
50 changes: 45 additions & 5 deletions wadas/domain/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
def load_configuration_from_file(file_path):
"""Method to load configuration from YAML file."""

valid_ftp_keyring = True
valid_email_keyring = True
with open(str(file_path), "r") as file:

logging.info("Loading configuration from file...")
Expand All @@ -38,7 +40,27 @@ def load_configuration_from_file(file_path):
for key in notification:
if key in Notifier.notifiers:
if key == Notifier.NotifierTypes.EMAIL.value:
Notifier.notifiers[key] = EmailNotifier(**notification[key])
email_notifier = EmailNotifier(**notification[key])
Notifier.notifiers[key] = email_notifier
credentials = keyring.get_credential("WADAS_email", email_notifier.sender_email)
if not credentials:
logger.error(
"Unable to find email credentials for %s stored on the system."
"Please insert them through email configuration dialog.",
email_notifier.sender_email,
)
valid_email_keyring = False
elif credentials and credentials.username != email_notifier.sender_email:
logger.error(
"Email username on the system (%s) does not match with username "
"provided in configuration file (%s). Please make sure valid email "
"credentials are in use by editing them from email configuration "
"dialog.",
credentials.username,
email_notifier.sender_email,
)
valid_email_keyring = False

# FTP Server
if FTPsServer.ftps_server and FTPsServer.ftps_server.server:
FTPsServer.ftps_server.server.close_all()
Expand Down Expand Up @@ -70,11 +92,28 @@ def load_configuration_from_file(file_path):
os.makedirs(ftp_camera.ftp_folder, exist_ok=True)
credentials = keyring.get_credential(f"WADAS_FTP_camera_{ftp_camera.id}", "")
if credentials:
FTPsServer.ftps_server.add_user(
credentials.username,
credentials.password,
ftp_camera.ftp_folder,
if credentials.username != ftp_camera.user:
logger.error(
"Keyring stored user (%s) differs from configuration file one (%s)."
" Please make sure to align system stored credential with"
" configuration file. System credentials will be used.",
ftp_camera.user,
credentials.username,
)
valid_ftp_keyring = False
else:
FTPsServer.ftps_server.add_user(
credentials.username,
credentials.password,
ftp_camera.ftp_folder,
)
else:
logger.error(
"Unable to find credentials for %s on this system. "
"Please add credentials manually from FTP Camera configuration dialog.",
ftp_camera.id,
)
valid_ftp_keyring = False
Camera.detection_params = wadas_config["camera_detection_params"]
# FastAPI Actuator Server
FastAPIActuatorServer.actuator_server = (
Expand Down Expand Up @@ -107,6 +146,7 @@ def load_configuration_from_file(file_path):
OperationMode.cur_operation_mode = None

logger.info("Configuration loaded from file %s.", file_path)
return valid_ftp_keyring, valid_email_keyring


def save_configuration_to_file(file):
Expand Down
10 changes: 7 additions & 3 deletions wadas/domain/ftp_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
class FTPCamera(Camera):
"""FTP Camera class, specialization of Camera class."""

def __init__(self, id, ftp_folder, enabled=True, actuators=None):
def __init__(self, id, ftp_folder, ftp_user, enabled=True, actuators=None):
if actuators is None:
actuators = []
super().__init__(id, enabled)
self.type = Camera.CameraTypes.FTP_CAMERA
self.ftp_folder = ftp_folder
self.user = ftp_user
self.actuators = actuators

def serialize(self):
Expand All @@ -25,8 +26,9 @@ def serialize(self):
return {
"type": self.type.value,
"id": self.id,
"enabled": self.enabled,
"ftp_folder": self.ftp_folder,
"ftp_user": self.user,
"enabled": self.enabled,
"actuators": actuators,
}

Expand All @@ -37,4 +39,6 @@ def deserialize(data):
actuators = (
[Actuator.actuators[key] for key in data["actuators"]] if "actuators" in data else []
)
return FTPCamera(data["id"], data["ftp_folder"], data["enabled"], actuators)
return FTPCamera(
data["id"], data["ftp_folder"], data["ftp_user"], data["enabled"], actuators
)
4 changes: 4 additions & 0 deletions wadas/domain/ftps_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def has_user(self, username):
"""Wrapper method of authorizer to check if a user already exists"""
return self.authorizer.has_user(username)

def remove_user(self, username):
"""Wrapper method of authorizer to remove a user."""
return self.authorizer.remove_user(username)

def run(self):
"""Method to create new thread and run a FTPS server."""
self.server = ThreadedFTPServer((self.ip, self.port), self.handler)
Expand Down
46 changes: 34 additions & 12 deletions wadas/ui/configure_email_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def initialize_form(self):
self.ui.lineEdit_smtpServer.setText(self.email_notifier.smtp_hostname)
self.ui.lineEdit_port.setText(self.email_notifier.smtp_port)
credentials = keyring.get_credential("WADAS_email", self.email_notifier.sender_email)
if credentials.username == self.email_notifier.sender_email:
if credentials and credentials.username == self.email_notifier.sender_email:
self.ui.lineEdit_senderEmail.setText(credentials.username)
self.ui.lineEdit_password.setText(credentials.password)
if recipients_email := self.email_notifier.recipients_email:
Expand All @@ -82,21 +82,40 @@ def accept_and_close(self):
recipients,
self.ui.checkBox_email_en.isChecked(),
)
self.add_email_credentials(self.ui.lineEdit_senderEmail.text(), self.ui.lineEdit_password.text())
else:
self.email_notifier.enabled = self.ui.checkBox_email_en.isChecked()
self.email_notifier.sender_email = self.ui.lineEdit_senderEmail.text()
self.email_notifier.smtp_hostname = self.ui.lineEdit_smtpServer.text()
self.email_notifier.smtp_port = self.ui.lineEdit_port.text()
self.email_notifier.recipients_email = []
self.email_notifier.recipients_email = recipients
keyring.set_password(
"WADAS_email",
self.ui.lineEdit_senderEmail.text(),
self.ui.lineEdit_password.text(),
)
self.add_email_credentials(self.ui.lineEdit_senderEmail.text(), self.ui.lineEdit_password.text())

Notifier.notifiers[Notifier.NotifierTypes.EMAIL.value] = self.email_notifier
self.accept()

def add_email_credentials(self, username, password):
"""Method to add email credentials to system keyring."""

# If credentials exist remove them (workaround keyring bug)
credentials = keyring.get_credential(
f"WADAS_email", ""
)
if credentials:
try:
keyring.delete_password(f"WADAS_email", credentials.username)
except keyring.errors.PasswordDeleteError:
# Credentials not in the system
pass

# Set new/modified credentials for camera
keyring.set_password(
"WADAS_email",
username,
password,
)

def validate_email_configurations(self):
"""Check if inserted email config is valid."""

Expand Down Expand Up @@ -192,9 +211,12 @@ def send_email(self):
self.ui.lineEdit_port.text(),
context=context,
) as smtp_server:

smtp_server.login(sender, credentials.password)

for recipient in recipients:
smtp_server.sendmail(sender, recipient, message.as_string())
self.ui.label_status.setText("Test email(s) sent!")
try:
smtp_server.login(sender, credentials.password)

for recipient in recipients:
smtp_server.sendmail(sender, recipient, message.as_string())
self.ui.plainTextEdit_test_log.setPlainText("Test email(s) sent!")
except smtplib.SMTPResponseException as e:
self.ui.label_status.setText(f"Test email(s) Failed!")
self.ui.plainTextEdit_test_log.setPlainText(f"{e.smtp_code}: {e.smtp_error}")
80 changes: 56 additions & 24 deletions wadas/ui/configure_ftp_cameras_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,28 +164,37 @@ def accept_and_close(self):
f"WADAS_FTP_camera_{camera.id}", ""
)
if credentials and (
credentials.username != cur_user
or credentials.password != cur_pass
credentials.username == cur_user
and credentials.password == cur_pass
):
keyring.set_password(
f"WADAS_FTP_camera_{cur_ui_id}",
break
else:
self.add_camera_credentials(cur_ui_id, cur_user, cur_pass)
# If user existed in ftp server, remove it.
if FTPsServer.ftps_server.has_user(cur_user):
FTPsServer.ftps_server.remove_user(cur_user)
# add modified/missing user.
FTPsServer.ftps_server.add_user(
cur_user,
cur_pass,
camera.ftp_folder,
)
break
if not found:
# If camera id is not in cameras list, then is new or modified
camera_user = self.get_camera_user(i)
camera_pass = self.get_camera_pass(i)
camera_ftp_path = os.path.join(FTPsServer.ftps_server.ftp_dir, cur_ui_id)
camera = FTPCamera(
cur_ui_id,
os.path.join(FTPsServer.ftps_server.ftp_dir, cur_ui_id),
camera_ftp_path,
camera_user
)
cameras.append(camera)
# Store credentials in keyring
keyring.set_password(
f"WADAS_FTP_camera_{cur_ui_id}",
self.get_camera_user(i),
self.get_camera_pass(i),
)
self.add_camera_credentials(cur_ui_id, camera_user, camera_pass)
# Add FTP Camera to FTP server users list
self.add_camera_to_ftp_server(cur_ui_id, camera_ftp_path, camera_user, camera_pass)

# Check for cameras old id (prior to modification) and remove them
orphan_cameras = (
Expand All @@ -205,25 +214,48 @@ def accept_and_close(self):
cur_camera_id = self.get_camera_id(i)
if cur_camera_id:
cur_cam_ftp_dir = os.path.join(FTPsServer.ftps_server.ftp_dir, cur_camera_id)
camera = FTPCamera(cur_camera_id, cur_cam_ftp_dir)
camera_user = self.get_camera_user(i)
camera_pass = self.get_camera_pass(i)
camera = FTPCamera(cur_camera_id, cur_cam_ftp_dir, camera_user)
cameras.append(camera)
# Store credentials in keyring
keyring.set_password(
f"WADAS_FTP_camera_{cur_camera_id}",
self.get_camera_user(i),
self.get_camera_pass(i),
)
self.add_camera_credentials(cur_camera_id, camera_user, camera_pass)
# Add camera user to FTPS server
if not FTPsServer.ftps_server.has_user(cur_camera_id):
if not os.path.isdir(cur_cam_ftp_dir):
os.makedirs(cur_cam_ftp_dir, exist_ok=True)
FTPsServer.ftps_server.add_user(
self.get_camera_user(i),
self.get_camera_pass(i),
cur_cam_ftp_dir,
)
self.add_camera_to_ftp_server(cur_camera_id, cur_cam_ftp_dir, camera_user, camera_pass)
self.accept()

def add_camera_credentials(self, camera_id, user, password):
"""Method to add camera credentials to keyring."""

# If credentials exist remove them (workaround keyring bug)
credentials = keyring.get_credential(
f"WADAS_FTP_camera_{camera_id}", ""
)
if credentials:
try:
keyring.delete_password(f"WADAS_FTP_camera_{camera_id}", credentials.username)
except keyring.errors.PasswordDeleteError:
# Credentials not in the system
pass

# Set new/modified credentials for camera
keyring.set_password(
f"WADAS_FTP_camera_{camera_id}",
user,
password,
)

def add_camera_to_ftp_server(self, cur_camera_id, cam_ftp_dir, camera_user, camera_pass):
"""Method to add FTP camera to FTP server"""
if not FTPsServer.ftps_server.has_user(cur_camera_id):
if not os.path.isdir(cam_ftp_dir):
os.makedirs(cam_ftp_dir, exist_ok=True)
FTPsServer.ftps_server.add_user(
camera_user,
camera_pass,
cam_ftp_dir,
)

def reject_and_close(self):
self._stop_ftp_server()

Expand Down
Loading

0 comments on commit a0445b3

Please sign in to comment.