diff --git a/config/csv_import/manual_csv_export/st-ten-11_15_RECIPES_14-04-2025.csv b/config/csv_import/manual_csv_import/st-ten-11_15_RECIPES_14-04-2025.csv
similarity index 100%
rename from config/csv_import/manual_csv_export/st-ten-11_15_RECIPES_14-04-2025.csv
rename to config/csv_import/manual_csv_import/st-ten-11_15_RECIPES_14-04-2025.csv
diff --git a/src/components/component.py b/src/components/component.py
index 9fd0990..952cc75 100644
--- a/src/components/component.py
+++ b/src/components/component.py
@@ -1,4 +1,5 @@
import logging
+import sys
import traceback
import types
@@ -179,6 +180,8 @@ class Component(QObject):
waits until the requested action has been completed by the component
this will return immediately if threaded=False was passed at component initialization
"""
+ timeout = 60 if "--debug" in sys.argv else timeout
+
if self._threaded:
timeout = round(timeout * 1000)
if self._lock.tryAcquire(max(self._lock.available(), 1), timeout):
diff --git a/src/components/modbus_component.py b/src/components/modbus_component.py
index 93494df..08609fd 100644
--- a/src/components/modbus_component.py
+++ b/src/components/modbus_component.py
@@ -20,6 +20,7 @@ if "--sim-modbus" not in sys.argv:
else:
from components.dummies.pymodbus import ModbusClient
+ #from components.dummies.pymodbus import ModbusTcpClient
from PyQt5.QtCore import QMutex, pyqtSignal
diff --git a/src/lib/db/models/users.py b/src/lib/db/models/users.py
index ad3423d..1b990da 100755
--- a/src/lib/db/models/users.py
+++ b/src/lib/db/models/users.py
@@ -26,7 +26,32 @@ class Session:
self.username = self.user.username
self.badge_number = self.user.badge_number
self.roles = Users.parse_roles(self.user.roles)
- self.is_admin = self.user.is_admin
+ self._is_admin = self.user.is_admin # Store original admin status
+ self.temp_admin = False # Flag to track temporary admin privilege
+
+ def enable_temp_admin(self):
+ """Enable temporary admin privileges"""
+ self.temp_admin = True
+ Log.log("Admin", f"Temporary admin privileges enabled for user: {self.username!r}")
+ return True
+
+ def disable_temp_admin(self):
+ """Disable temporary admin privileges"""
+ self.temp_admin = False
+ Log.log("Admin", f"Temporary admin privileges disabled for user: {self.username!r}")
+ return True
+
+ @property
+ def is_admin(self):
+ # Return True if user is an admin or has temporary admin privileges
+ return self._is_admin or self.temp_admin
+
+ @property
+ def current_roles(self):
+ roles = set(self.roles)
+ if self.temp_admin:
+ roles.add("admin")
+ return list(roles)
def json_dumps_roles(roles):
@@ -92,9 +117,21 @@ class Users(BaseModel):
Log.log("Login", f"username: {username!r}: BAD username")
return False
if user.verify(password):
- Log.log("Login", f"username: {user.username!r}: SUCCESSFUL, roles: {user.roles!r}")
+ # Check if there's an existing session with temp_admin privileges
global current_session
+ had_temp_admin = current_session is not None and current_session.temp_admin
+
+ # Create new session
current_session = Session(user)
+
+ # Preserve temp_admin status if it was set
+ if had_temp_admin:
+ current_session.temp_admin = True
+
+ # Log the actual roles including temp_admin if applicable
+ roles_to_log = current_session.current_roles if had_temp_admin else user.roles
+ Log.log("Login", f"username: {user.username!r}: SUCCESSFUL, roles: {roles_to_log!r}")
+
return current_session
else:
Log.log("Login", f"username: {user.username!r}, password {password!r}: BAD password")
@@ -116,6 +153,58 @@ class Users(BaseModel):
global current_session
return current_session
+ @classmethod
+ @db.atomic()
+ def enable_temp_admin(cls, admin_password):
+ """Enable temporary admin privileges for the current session"""
+ session = cls.get_session()
+ if session is None:
+ Log.log("Admin", "Cannot enable temp admin: No active session")
+ return False
+
+ # If user is already a permanent admin, no need to enable temp admin
+ if "admin" in cls.parse_roles(session.user.roles):
+ Log.log("Admin", f"User {session.username!r} is already a permanent admin")
+ return True
+
+ # If user already has temporary admin privileges, nothing to do
+ if session.temp_admin:
+ Log.log("Admin", f"User {session.username!r} already has temporary admin privileges")
+ return True
+
+ # Find an admin user to verify the password against
+ admin_users = [user for user in cls.get_users() if "admin" in cls.parse_roles(user.roles)]
+
+ if not admin_users:
+ Log.log("Admin", "No admin users found in the system")
+ return False
+
+ # Try to verify the password with any admin user
+ for admin_user in admin_users:
+ if admin_user.verify(admin_password):
+ # Use the session's enable_temp_admin method
+ return session.enable_temp_admin()
+
+ Log.log("Admin", f"Failed to enable temp admin for user {session.username!r}: Invalid admin password")
+ return False
+
+ @classmethod
+ @db.atomic()
+ def disable_temp_admin(cls):
+ """Disable temporary admin privileges for the current session"""
+ session = cls.get_session()
+ if session is None:
+ Log.log("Admin", "Cannot disable temp admin: No active session")
+ return False
+
+ # If user is not a temp admin, nothing to do
+ if not session.temp_admin:
+ Log.log("Admin", f"User {session.username!r} does not have temporary admin privileges")
+ return True
+
+ # Use the session's disable_temp_admin method
+ return session.disable_temp_admin()
+
@db.atomic()
def verify(self, password):
if self.password is None:
diff --git a/src/main.py b/src/main.py
index faf5266..f05e582 100644
--- a/src/main.py
+++ b/src/main.py
@@ -75,7 +75,8 @@ try:
from lib.db import Users
from lib.helpers import ConfigReader
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
- from PyQt5.QtWidgets import QApplication, QMessageBox
+ from PyQt5.QtWidgets import QApplication, QMessageBox, QInputDialog, QLineEdit
+ import sip
from ui import About, Archive, Login, Main_Window, Test, Users_Management,Logs_Management ,Recipe_Selection, \
Barcode_Recipe_Selection
@@ -196,8 +197,8 @@ try:
if "--about" in sys.argv:
self.main_window.about_a.trigger()
- # admin menu should not be visible before an admin logs in
- self.main_window.admin_m.menuAction().setVisible(False)
+ # Keep the admin menu always visible
+ self.main_window.admin_m.menuAction().setVisible(True)
self.main_window.about_a.triggered.connect(
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(About()))
@@ -217,6 +218,8 @@ try:
self.main_window.cut_a.setVisible(False)
self.main_window.diagnostics_a.triggered.connect(
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(Diagnostics(selfie())))
+ self.main_window.admin_enable_a.triggered.connect(
+ lambda checked, selfie=weakref.ref(self): selfie().enable_admin_privileges())
if "--users-management" in sys.argv:
self.main_window.users_management_a.trigger()
@@ -252,22 +255,48 @@ try:
def logged_in(self):
session = Users.get_session()
+ # Always make the admin menu visible
+ self.main_window.admin_m.menuAction().setVisible(True)
+
if session is not None:
+ # Check if user has admin privileges (either permanent or temporary)
+ # Use session.is_admin instead of checking roles directly to be consistent with user.py
if session.is_admin:
- self.main_window.admin_m.menuAction().setVisible(True)
self.main_window.tag_a.setVisible(True)
+ self.main_window.admin_enable_a.setVisible(False) # Hide admin enable action for admins
+ # Show admin features for users with admin privileges (permanent or temporary)
+ self.main_window.users_management_a.setVisible(True)
+ self.main_window.save_tecna_recipes_a.setVisible(True)
+ self.main_window.diagnostics_a.setVisible(True)
else:
- self.main_window.admin_m.menuAction().setVisible(False)
+ # For non-admin users, only show the admin enable button
+ self.main_window.admin_enable_a.setVisible(True) # Show admin enable action for non-admins
+ self.main_window.users_management_a.setVisible(False) # Hide user management for non-admins
+ self.main_window.save_tecna_recipes_a.setVisible(False) # Hide tecna recipes for non-admins
+ self.main_window.diagnostics_a.setVisible(False) # Hide diagnostics for non-admins
self.main_window.tag_a.setVisible(False)
# open test
+
+ # Update background color based on admin privileges
+ self.update_window_backgrounds()
+
self.main_window.open_tab(Test(self.config, self.components, self))
self.main_window.centralWidget().request_autotest("login")
- else:
- self.main_window.admin_m.menuAction().setVisible(False)
def logout(self):
# Users.logout()
- self.main_window.admin_m.menuAction().setVisible(False)
+ # Keep the admin menu visible
+ self.main_window.admin_m.menuAction().setVisible(True)
+ # Get the current session (if any)
+ session = Users.get_session()
+ # Note: We no longer reset temp_admin here to preserve admin status across login/logout
+
+ # Reset background color to default
+ self.main_window.setStyleSheet("")
+ for window_name, window in self.main_window.windows.items():
+ if window is not None and not sip.isdeleted(window):
+ window.setStyleSheet("")
+
if type(self.main_window.centralWidget().centralWidget.widget) in (
Recipe_Selection, Barcode_Recipe_Selection):
# LOGOUT IMMEDIATELY IF NOT TESTING
@@ -299,6 +328,171 @@ try:
def cut_tube(self):
self.main_window.centralWidget().cut_tube()
+ def enable_admin_privileges(self):
+ session = Users.get_session()
+ if session is None:
+ QMessageBox.warning(self.main_window, "Errore", "Nessun utente loggato")
+ return
+
+ # If user already has temporary admin privileges, toggle them off
+ if session.temp_admin:
+ # Use the Users class method to disable temp admin
+ Users.disable_temp_admin()
+ # Keep the admin menu visible
+ self.main_window.admin_m.menuAction().setVisible(True)
+ self.main_window.users_management_a.setVisible(False)
+ self.main_window.save_tecna_recipes_a.setVisible(False)
+ self.main_window.diagnostics_a.setVisible(False)
+ self.main_window.tag_a.setVisible(False)
+ self.main_window.admin_enable_a.setVisible(True) # Show admin enable action after removing temp admin privileges
+
+ # Call disable_temp_admin on the Test widget if it exists
+ current_widget = self.main_window.centralWidget()
+ if current_widget is not None and hasattr(current_widget, 'disable_temp_admin'):
+ current_widget.disable_temp_admin()
+ QMessageBox.information(
+ self.main_window,
+ "Successo",
+ "Privilegi di amministratore disabilitati"
+ )
+ # Update background color for all windows
+ self.update_window_backgrounds()
+
+ # Refresh the current UI component to reflect removed admin privileges
+ current_widget = self.main_window.centralWidget()
+ if current_widget is not None:
+ # If the current widget has a refresh method, call it
+ if hasattr(current_widget, 'refresh'):
+ current_widget.refresh()
+ # If the current widget has a crud attribute, refresh it
+ if hasattr(current_widget, 'crud'):
+ if callable(current_widget.crud):
+ crud = current_widget.crud()
+ else:
+ crud = current_widget.crud
+ if hasattr(crud, 'refresh'):
+ crud.refresh()
+
+ # Refresh all other open windows to reflect removed admin privileges
+ for window_name, window in self.main_window.windows.items():
+ if window is not None and not sip.isdeleted(window):
+ central_widget = window.centralWidget()
+ if central_widget is not None:
+ # If the central widget has a refresh method, call it
+ if hasattr(central_widget, 'refresh'):
+ central_widget.refresh()
+ # If the central widget has a crud attribute, refresh it
+ if hasattr(central_widget, 'crud'):
+ if callable(central_widget.crud):
+ crud = central_widget.crud()
+ else:
+ crud = central_widget.crud
+ if hasattr(crud, 'refresh'):
+ crud.refresh()
+ return
+
+ # If user is a permanent admin (not temporary), show a message
+ if "admin" in Users.parse_roles(session.user.roles):
+ QMessageBox.information(self.main_window, "Informazione", "Sei giĆ un amministratore permanente")
+ return
+
+ password, ok = QInputDialog.getText(
+ self.main_window,
+ "Admin abilitazione",
+ "Inserisci la password di amministratore:",
+ QLineEdit.Password
+ )
+
+ if not ok or not password:
+ return
+
+ # Find an admin user to verify the password against
+ admin_users = [user for user in Users.get_users() if "admin" in Users.parse_roles(user.roles)]
+
+ if not admin_users:
+ QMessageBox.warning(self.main_window, "Errore", "Nessun utente amministratore trovato nel sistema")
+ return
+
+ # Use the Users class method to enable temp admin
+ if Users.enable_temp_admin(password):
+ self.main_window.admin_m.menuAction().setVisible(True)
+ self.main_window.tag_a.setVisible(True)
+ # Show admin features for users with temporary admin privileges
+ self.main_window.users_management_a.setVisible(True)
+ self.main_window.save_tecna_recipes_a.setVisible(True)
+ self.main_window.diagnostics_a.setVisible(True)
+
+ # Call enable_temp_admin on the Test widget if it exists
+ current_widget = self.main_window.centralWidget()
+ if current_widget is not None and hasattr(current_widget, 'enable_temp_admin'):
+ current_widget.enable_temp_admin()
+ QMessageBox.information(
+ self.main_window,
+ "Successo",
+ "Privilegi di amministratore abilitati temporaneamente"
+ )
+ # Update background color for all windows
+ self.update_window_backgrounds()
+
+ # Refresh the current UI component to reflect new admin privileges
+ current_widget = self.main_window.centralWidget()
+ if current_widget is not None:
+ # If the current widget has a refresh method, call it
+ if hasattr(current_widget, 'refresh'):
+ current_widget.refresh()
+ # If the current widget has a crud attribute, refresh it
+ if hasattr(current_widget, 'crud'):
+ if callable(current_widget.crud):
+ crud = current_widget.crud()
+ else:
+ crud = current_widget.crud
+ if hasattr(crud, 'refresh'):
+ crud.refresh()
+
+ # Refresh all other open windows to reflect new admin privileges
+ for window_name, window in self.main_window.windows.items():
+ if window is not None and not sip.isdeleted(window):
+ central_widget = window.centralWidget()
+ if central_widget is not None:
+ # If the central widget has a refresh method, call it
+ if hasattr(central_widget, 'refresh'):
+ central_widget.refresh()
+ # If the central widget has a crud attribute, refresh it
+ if hasattr(central_widget, 'crud'):
+ if callable(central_widget.crud):
+ crud = central_widget.crud()
+ else:
+ crud = central_widget.crud
+ if hasattr(crud, 'refresh'):
+ crud.refresh()
+ return
+
+ QMessageBox.warning(self.main_window, "Errore", "Password non valida")
+
+ def update_window_backgrounds(self):
+ """Update the background color of all windows based on admin privileges"""
+ session = Users.get_session()
+ if session is None:
+ return
+
+ # Check if user has admin privileges (permanent or temporary)
+ # Use session.is_admin instead of checking roles directly to be consistent with user.py
+ has_admin = session.is_admin
+
+ # Set background color for main window
+ if has_admin:
+ self.main_window.setStyleSheet("background-color: #ffcccc;") # Light red background
+ else:
+ self.main_window.setStyleSheet("") # Reset to default
+
+ # Set background color for all other windows
+ for window_name, window in self.main_window.windows.items():
+ if window is not None and not sip.isdeleted(window):
+ if has_admin:
+ window.setStyleSheet("background-color: #ffcccc;") # Light red background
+ else:
+ window.setStyleSheet("") # Reset to default
+
@pyqtSlot(str)
def load_recipe_from_rfid(self, data):
self.tag_loaded_recipe = data
diff --git a/src/ui/archive/archive.py b/src/ui/archive/archive.py
index f0a1e6d..cf6a046 100755
--- a/src/ui/archive/archive.py
+++ b/src/ui/archive/archive.py
@@ -75,6 +75,19 @@ class Archive(Widget):
if self.selected is not None and self.printer is not None:
self.printer.print_archive_label(self.selected)
+ def refresh(self):
+ """Update the CRUD component based on current admin privileges"""
+ session = Users.get_session()
+ # Check if user has admin privileges (either permanent or temporary)
+ # Use session.is_admin instead of checking roles directly to be consistent with user.py
+ has_admin = session.is_admin
+
+ # Update CRUD readonly status
+ if has_admin:
+ self.crud.set_readonly(False)
+ else:
+ self.crud.set_readonly(True)
+
def export(self, csv_path=None):
if csv_path is None:
csv_path, _ = QFileDialog.getSaveFileName(
diff --git a/src/ui/crud/crud.py b/src/ui/crud/crud.py
index 583bf4b..c239718 100755
--- a/src/ui/crud/crud.py
+++ b/src/ui/crud/crud.py
@@ -6,6 +6,7 @@ import weakref
from datetime import datetime
from lib.db import Crud_DB
+from lib.db.models.users import Users
from peewee import TextField
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QSize
from PyQt5.QtWidgets import (QAbstractItemView, QComboBox, QDialog,
@@ -273,6 +274,15 @@ class Crud(Widget):
self.filter_delay.setSingleShot(True)
self.filter_delay.setInterval(500)
self.filter_delay.timeout.connect(lambda selfi=weakref.ref(self): selfi().do_filter(selfi().filter_cn))
+
+ # Setup privilege check timer
+ self.privilege_timer = QTimer()
+ self.privilege_timer.setInterval(5000) # 5 seconds
+ self.privilege_timer.timeout.connect(self.check_privileges)
+ self.privilege_timer.start()
+
+ # Initial check of privileges
+ self.check_privileges()
self.db_tw.crud = self
self.refresh("init")
self.db_tw.horizontalHeader().sectionClicked.connect(self.toggle_sort)
@@ -298,6 +308,13 @@ class Crud(Widget):
self.start_b.setHidden(True)
self.end_b.setHidden(True)
self.page_n_sb.setHidden(True)
+
+ # Setup buttons based on readonly status
+ self._setup_buttons()
+
+ def _setup_buttons(self):
+ """Configure buttons based on readonly status"""
+
if self.readonly is None or self.readonly is True:
self.cancel_b.setEnabled(False)
self.cancel_b.setHidden(True)
@@ -308,11 +325,77 @@ class Crud(Widget):
self.delete_b.setEnabled(False)
self.delete_b.setHidden(True)
else:
+ self.cancel_b.setEnabled(True)
+ self.cancel_b.setHidden(False)
+ self.commit_b.setEnabled(True)
+ self.commit_b.setHidden(False)
+ self.add_b.setEnabled(True)
+ self.add_b.setHidden(False)
+ self.delete_b.setEnabled(True)
+ self.delete_b.setHidden(False)
+
+ # Connect button signals if not already connected
+ try:
+ self.cancel_b.clicked.disconnect()
+ except:
+ pass
+ try:
+ self.commit_b.clicked.disconnect()
+ except:
+ pass
+ try:
+ self.add_b.clicked.disconnect()
+ except:
+ pass
+ try:
+ self.delete_b.clicked.disconnect()
+ except:
+ pass
+
self.cancel_b.clicked.connect(self.cancel)
self.commit_b.clicked.connect(self.commit)
self.add_b.clicked.connect(self.add)
self.delete_b.clicked.connect(self.delete)
+ def check_privileges(self):
+ """Check current user privileges and update readonly status if needed"""
+ try:
+ session = Users.get_session()
+ if session is None:
+ # No user logged in, set to readonly
+ self.set_readonly(True)
+ return
+
+ # Check if user has admin privileges (either permanent or temporary)
+ is_admin = "admin" in Users.parse_roles(session.user.roles) or session.temp_admin
+
+ # Update readonly status based on admin privileges
+ # If user is admin, they can edit (readonly=False)
+ # If user is not admin, they can only view (readonly=True)
+ self.set_readonly(not is_admin)
+ except Exception as e:
+ # Log any errors but don't crash
+ self.log.exception(f"Error checking privileges: {str(e)}")
+
+ def set_readonly(self, readonly):
+ """Update the readonly status and refresh the UI accordingly"""
+ if self.readonly == readonly:
+ return # No change needed
+
+ self.readonly = readonly
+
+ # Update selection mode
+ if self.readonly is None or self.readonly is True:
+ self.db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
+ else:
+ self.db_tw.setSelectionMode(QAbstractItemView.ExtendedSelection)
+
+ # Update buttons
+ self._setup_buttons()
+
+ # Refresh the UI to update cell widgets
+ self.refresh()
+
sort_cycle = [True, False, None]
sort_symbol = [" \u25B4", " \u25BE", ""]
@@ -567,3 +650,21 @@ class Crud(Widget):
def emit(self):
self.set_modified(self._modified)
self.show_selection()
+
+ def hideEvent(self, event):
+ """Stop the privilege timer when the widget is hidden"""
+ if hasattr(self, 'privilege_timer'):
+ self.privilege_timer.stop()
+ super().hideEvent(event)
+
+ def showEvent(self, event):
+ """Restart the privilege timer when the widget is shown"""
+ if hasattr(self, 'privilege_timer'):
+ self.privilege_timer.start()
+ super().showEvent(event)
+
+ def closeEvent(self, event):
+ """Clean up resources when the widget is closed"""
+ if hasattr(self, 'privilege_timer'):
+ self.privilege_timer.stop()
+ super().closeEvent(event)
diff --git a/src/ui/logs_management/info.py b/src/ui/logs_management/info.py
index 85ccf7f..a7eaf0a 100644
--- a/src/ui/logs_management/info.py
+++ b/src/ui/logs_management/info.py
@@ -1,6 +1,6 @@
import traceback
-from lib.db import log # Presumendo che esista un modulo per accedere alla tabella "log"
+from lib.db import log, Users # Presumendo che esista un modulo per accedere alla tabella "log"
from PyQt5.QtWidgets import QMessageBox, QTableWidget
from ui.crud import Crud, Line_Edit_Cell_Widget
from ui.widget import Widget
@@ -42,6 +42,19 @@ class Logs_Management(Widget):
# Adjust the column widths based on content
self.adjust_column_widths()
+ def refresh(self):
+ """Update the CRUD component based on current admin privileges"""
+ session = Users.get_session()
+ # Check if user has admin privileges (either permanent or temporary)
+ # Use session.is_admin instead of checking roles directly to be consistent with user.py
+ has_admin = session.is_admin
+
+ # Update CRUD readonly status
+ if has_admin:
+ self.crud.set_readonly(["id"]) # Only id field is readonly
+ else:
+ self.crud.set_readonly(True) # All fields are readonly
+
def adjust_column_widths(self):
"""Adjust the widths of columns to fit their content."""
# Ensure that Crud has db_tw which is a QTableWidget
diff --git a/src/ui/main_window/main_window.py b/src/ui/main_window/main_window.py
index 9052ef9..4d9f46a 100644
--- a/src/ui/main_window/main_window.py
+++ b/src/ui/main_window/main_window.py
@@ -3,10 +3,10 @@ import sip
from PyQt5.QtCore import pyqtSignal
from ui.window import Window
-parser = argparse.ArgumentParser(prog='STTEN', description='Leak test system',add_help=False)
+parser = argparse.ArgumentParser(prog='STTEN', description='Leak test system', add_help=False)
parser.add_argument('-w', '--width')
parser.add_argument('-h', '--height')
-args,unspec = parser.parse_known_args()
+args, unspec = parser.parse_known_args()
class Main_Window(Window):
do = pyqtSignal(dict)
@@ -22,6 +22,9 @@ class Main_Window(Window):
self.run_request.connect(self.run_request_handler)
# print("MAIN_WINDOW ", str(int(QThread.currentThreadId())), flush=True)
+ # Initialize windows dictionary
+ self.windows = {}
+
if "width" in args:
if args.width is not None:
self.setFixedWidth(int(args.width))
diff --git a/src/ui/main_window/main_window.ui b/src/ui/main_window/main_window.ui
index e570c97..8605633 100644
--- a/src/ui/main_window/main_window.ui
+++ b/src/ui/main_window/main_window.ui
@@ -53,6 +53,7 @@
+
diff --git a/src/ui/recipe_selection/recipe_selection.py b/src/ui/recipe_selection/recipe_selection.py
index 79c13a6..65c9e19 100755
--- a/src/ui/recipe_selection/recipe_selection.py
+++ b/src/ui/recipe_selection/recipe_selection.py
@@ -187,6 +187,45 @@ class Recipe_Selection(Widget):
def check(self, modified, selected):
self.select_b.setEnabled(modified is False and selected is not None)
+ def refresh(self):
+ """Update the UI based on current admin privileges"""
+ session = Users.get_session()
+ # Check if user has admin privileges (either permanent or temporary)
+ # Use session.is_admin instead of checking roles directly to be consistent with user.py
+ has_admin = session.is_admin
+
+ # Update button visibility
+ self.import_b.setVisible(has_admin)
+ self.export_b.setVisible(has_admin)
+ self.delete_all_b.setVisible(has_admin)
+
+ # Connect or disconnect button handlers
+ if has_admin:
+ # Disconnect existing handlers to avoid multiple connections
+ try:
+ self.import_b.clicked.disconnect()
+ except:
+ pass
+ try:
+ self.export_b.clicked.disconnect()
+ except:
+ pass
+ try:
+ self.delete_all_b.clicked.disconnect()
+ except:
+ pass
+
+ # Connect handlers
+ self.import_b.clicked.connect(lambda checked, selfi=weakref.ref(self): selfi().import_recipes())
+ self.export_b.clicked.connect(lambda checked, selfi=weakref.ref(self): selfi().export_recipes())
+ self.delete_all_b.clicked.connect(lambda checked, selfi=weakref.ref(self): selfi().delete_recipes())
+
+ # Update CRUD readonly status
+ if has_admin:
+ self.crud.set_readonly(False)
+ else:
+ self.crud.set_readonly(True)
+
def select(self):
if self.selected is not None:
self.ok.emit(self.crud.db.table_model.get_by_id(self.selected))
@@ -489,6 +528,3 @@ class Recipe_Selection(Widget):
return self.WINDOWS_BASE_DIR
else:
raise Exception("Unsupported operating system!")
-
-
-
diff --git a/src/ui/recipes_management/recipes_management.py b/src/ui/recipes_management/recipes_management.py
index 5f5ea96..d886945 100644
--- a/src/ui/recipes_management/recipes_management.py
+++ b/src/ui/recipes_management/recipes_management.py
@@ -1,3 +1,4 @@
+from lib.db import Users
from ui.crud import Crud, Json_External_Dialog_Editor_Cell_Widget
from ui.recipe_spec_editor import Recipe_Spec_Editor
from ui.widget import Widget
@@ -28,3 +29,16 @@ class Recipes_Management(Widget):
widget_classes={"spec": lambda *args, **kwargs: Json_External_Dialog_Editor_Cell_Widget(Recipe_Spec_Editor, *args, **kwargs), },
)
self.layout().addWidget(self.crud, 0, 0, -1, -1)
+
+ def refresh(self):
+ """Update the CRUD component based on current admin privileges"""
+ session = Users.get_session()
+ # Check if user has admin privileges (either permanent or temporary)
+ # Use session.is_admin instead of checking roles directly to be consistent with user.py
+ has_admin = session.is_admin
+
+ # Update CRUD readonly status
+ if has_admin:
+ self.crud.set_readonly(False)
+ else:
+ self.crud.set_readonly(True)
diff --git a/src/ui/test/test.py b/src/ui/test/test.py
index 84bbc15..5bb5251 100755
--- a/src/ui/test/test.py
+++ b/src/ui/test/test.py
@@ -46,11 +46,22 @@ class Test(Widget):
self.machine_description_l.setText(self.config.get("machine", {}).get("description", "N/A"))
# SHOW USERNAME
session = Users.get_session()
- self.user_l.setText(session.username)
+ self.original_username = session.username
+
+ # Check if we should set the label to "ADMIN"
+ if session.username.upper() == "ADMIN":
+ self.user_l.setText("ADMIN")
+ session._is_admin = True
+ else:
+ self.user_l.setText(session.username)
+ session._is_admin = False
+
if session.is_admin:
self.user_l.setStyleSheet("QLabel { color: red; }")
else:
self.user_l.setStyleSheet("")
+ # Store original admin status
+ self.had_admin = session.is_admin
self.flag_label.setVisible(False)
if len(sys.argv) > 1:
@@ -277,6 +288,52 @@ class Test(Widget):
def setCentralWidget(self, widget):
replace_widget(self, "centralWidget", widget)
+ def enable_temp_admin(self):
+ """Enable temporary admin privileges for the current user"""
+ session = Users.get_session()
+ if session is None:
+ self.log.warning("Cannot enable temporary admin privileges: No active session")
+ return False
+
+ # Save the current label text before changing it
+ self.saved_user_label_text = self.user_l.text()
+
+ # Enable temporary admin privileges using the Session class method
+ session.enable_temp_admin()
+
+ # Update UI to reflect admin status
+ self.user_l.setText("ADMIN")
+ self.user_l.setStyleSheet("QLabel { color: red; }")
+
+ self.log.info(f"Temporary admin privileges enabled for user: {session.username}")
+ return True
+
+ def disable_temp_admin(self):
+ """Disable temporary admin privileges and restore original user status"""
+ session = Users.get_session()
+ if session is None:
+ self.log.warning("Cannot disable temporary admin privileges: No active session")
+ return False
+
+ # Disable temporary admin privileges using the Session class method
+ session.disable_temp_admin()
+
+ # Restore original UI
+ # Use the saved label text if available, otherwise fall back to original username
+ if hasattr(self, 'saved_user_label_text'):
+ self.user_l.setText(self.saved_user_label_text)
+ else:
+ self.user_l.setText(self.original_username)
+
+ # Set style based on original admin status
+ if self.had_admin:
+ self.user_l.setStyleSheet("QLabel { color: red; }")
+ else:
+ self.user_l.setStyleSheet("")
+
+ self.log.info(f"Temporary admin privileges disabled for user: {session.username}")
+ return True
+
def request_autotest(self, reason): # you can cancel the request calling request_autotest(False)
if "--no-autotest" not in sys.argv:
@@ -1036,4 +1093,4 @@ class Test(Widget):
self.flag_label.setStyleSheet("QLabel { color: red; font-weight: bold; }") # Customize color if needed
self.flag_label.setVisible(True) # Ensure the label is visible
else: # No args provided
- self.flag_label.setVisible(False) # Hide label
\ No newline at end of file
+ self.flag_label.setVisible(False) # Hide label
diff --git a/src/ui/test_test/test_test.py b/src/ui/test_test/test_test.py
index 707b23a..09d8fd5 100644
--- a/src/ui/test_test/test_test.py
+++ b/src/ui/test_test/test_test.py
@@ -241,12 +241,24 @@ class Test_Test(Widget):
def challenge_admin(self, info):
if not self.admin_challenged:
+ # Store the current session's admin flag
+ from lib.db import Users
+ session = Users.get_session()
+ was_temp_admin = False
+ if session is not None:
+ was_temp_admin = session.temp_admin
+
d = Dialog(parent=self)
d.setCentralWidget(Test_Admin_Permission(info))
d.setModal(True)
d.show()
d.centralWidget.password_le.setFocus()
r = d.exec()
+
+ # Restore the session's admin flag if it was set before
+ if session is not None and was_temp_admin:
+ session.temp_admin = was_temp_admin
+
if r == d.Accepted:
self.admin_challenged = True
elif r == d.Rejected:
diff --git a/src/ui/users_management/users_management.py b/src/ui/users_management/users_management.py
index 99887d6..8e2b3f9 100644
--- a/src/ui/users_management/users_management.py
+++ b/src/ui/users_management/users_management.py
@@ -1,6 +1,7 @@
import traceback
from lib.db import Users
+from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMessageBox
from ui.crud import Crud, Line_Edit_Cell_Widget
from ui.widget import Widget
@@ -10,6 +11,12 @@ class Users_Management(Widget):
def __init__(self):
super().__init__()
+ # Call refresh method after initialization to set correct readonly status
+ self.refresh_timer = QTimer()
+ self.refresh_timer.setSingleShot(True)
+ self.refresh_timer.timeout.connect(self.refresh)
+ self.refresh_timer.start(10) # Short delay to ensure widget is fully initialized
+
class Username_Line_Edit_Cell_Widget(Line_Edit_Cell_Widget):
def parse(self, action=None, row_number=None, crud=None):
data = super().parse(action=action, row_number=row_number, crud=crud)
@@ -65,6 +72,19 @@ class Users_Management(Widget):
)
self.layout().addWidget(self.crud, 0, 0, -1, -1)
+ def refresh(self):
+ """Update the CRUD component based on current admin privileges"""
+ session = Users.get_session()
+ # Check if user has admin privileges (either permanent or temporary)
+ # Use session.is_admin instead of checking roles directly to be consistent with user.py
+ has_admin = session.is_admin
+
+ # Update CRUD readonly status
+ if has_admin:
+ self.crud.set_readonly(False)
+ else:
+ self.crud.set_readonly(True) # All fields are readonly
+
def row_filter(self, row, row_number, crud):
if row["password"] is not None and all(map(lambda x: x == "\u2022", row["password"][0])):
row.pop("password", None)