/
Flipper
/
Marginal
Обзор
Документация
Войти
/
Flipper
/
Marginal
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
1
CI/CD
Аналитика
Безопасность
master
src/Locksmith.py
226 строк
7 KB
Alex
Bug fixes.
12 янв 2026, 13:17
12 янв 2026, 13:17
e907759
Код
Авторство
О чём код?
import os import hashlib import secrets from utils import generateFullPath, getBasePath from UIManager import LockSmithDLProxy from ConfigManager import ConfigManager class DuoLock: """ Manages application instance locking via a file-based lock mechanism. Prevents multiple instances of the application from running simultaneously. """ def __init__(self, backbone): """ Initialize DuoLock with application backbone. Args: backbone (Backbone): Central application controller. """ self.backbone = backbone self.configManager = ConfigManager(self.backbone, True) self.lockDir = generateFullPath(getBasePath(), "locker") self.lockFile = os.path.join(self.lockDir, "app.lock") def acquireLock(self): """ Attempt to acquire an application lock. Returns: bool -> True if lock acquired successfully, False if already locked. """ return self.setFileLock() def setFileLock(self) -> bool: """ Create a lock file containing the current process ID (PID). Checks for existing lock and verifies if the process is still running. Returns: bool -> True if lock file created, False if already locked by running process. """ os.makedirs(self.lockDir, exist_ok=True) if os.path.exists(self.lockFile): try: # Read PID from existing lock file with open(self.lockFile, 'r') as f: pid = int(f.read().strip()) # Check if process with this PID is still running (Windows-specific) try: output = os.popen(f'tasklist /FI "PID eq {pid}"').read() if str(pid) not in output: # Stale lock - process not running, remove lock file os.remove(self.lockFile) except: # If tasklist command fails, assume stale lock and remove os.remove(self.lockFile) # Possible point of failure else: # Process is still running - cannot acquire lock return False except (ValueError, IOError): # Invalid lock file content or read error - remove corrupted lock os.remove(self.lockFile) # Create new lock file with current PID with open(self.lockFile, "w") as f: f.write(str(os.getpid())) return True def releaseLock(self): """ Remove the lock file to release the application lock. Returns: None """ if os.path.exists(self.lockFile): os.remove(self.lockFile) def displayLock(self): """ Display a dialog indicating the application is already running/locked. Returns: None """ uiManager = LockSmithDLProxy(self.configManager, self.backbone.translator) uiManager.showLockedDialog() class PassKeeper: """ Manages password hashing, verification, and prompting for application security. Supports configurable password prompt frequencies. """ def __init__(self, backbone): """ Initialize PassKeeper with application backbone. Args: backbone (Backbone): Central application controller. """ self.backbone = backbone self.logger = self.backbone.logger self.translate = self.backbone.translator.translate self.virgin = True # Tracks if password has been verified in current session self.useConfig() def useConfig(self): """ Load password-related configuration from application settings. Returns: None """ self.lock = self.backbone.getConfig("locksmith")["lockFunctions"] self.frequency = self.backbone.getConfig("locksmith")["frequency"] self.salt = self.backbone.getMeta("salt") self.hash = self.backbone.getConfig("locksmith")["hash"] def getHash(self, inputStr: str): """ Generate PBKDF2 HMAC-SHA256 hash of input string using application salt. Args: inputStr (str): String to hash. Returns: str -> Hexadecimal hash string, or empty string if input is empty/None. """ if inputStr == str() or inputStr is None: return "" hashed = hashlib.pbkdf2_hmac( 'sha256', inputStr.encode(), self.salt.encode(), 5 ).hex() return hashed def verify(self, inputStr: str): """ Verify if input string matches stored password hash. Args: inputStr (str): Password input to verify. Returns: bool | None -> True if match, False if not match, None if no stored hash or input is None. """ if self.hash is None: return True if inputStr is None: return None inputHash = hashlib.pbkdf2_hmac( 'sha256', inputStr.encode(), self.salt.encode(), 5 ).hex() # Use timing-attack-safe comparison return secrets.compare_digest(inputHash, self.hash) def getAccess(self): """ Determine if password prompt is needed and verify user input. Returns: tuple -> (accessGranted: bool, promptShown: bool) Frequency meanings: - 0: Never prompt (always grant access) - 1: Always prompt (every time access is needed) - 2: Prompt once per session (only first time each start up) """ # No password protection configured if not self.lock or self.frequency == 0 or self.hash == "": return True, False # Frequency 2: Prompt only once per session if self.frequency == 2 and self.virgin: result = self.verify(self.promptPass()) if result is True and result is not None: self.virgin = False # Password verified for this session return result, True elif self.frequency == 2 and not self.virgin: return True, False # Already verified this session # Frequency 1: Always prompt return self.verify(self.promptPass()), True def promptPass(self) -> str: """ Display password prompt dialog. Returns: str | None -> User input password, or None if dialog cancelled. """ result = self.backbone.uiManager.runPasswordDialog( self.translate("Restricted Action"), self.translate("Enter current password to proceed:"), self.translate ) return str(result) if result is not None else None def setNewPass(self) -> str: """ Display dialog to set a new password. Returns: str | None -> New password input, or None if dialog cancelled. """ result = self.backbone.uiManager.runPasswordDialog( self.translate("Set password"), self.translate("Enter new password:"), self.translate ) return str(result) if result is not None else None