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 @@ + @@ -158,6 +159,11 @@ Calibra Taglio + + + Admin abilitazione + + 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)