diff --git a/app/classes/ratelimiter.php b/app/classes/ratelimiter.php index 7f8f69d..4493faa 100644 --- a/app/classes/ratelimiter.php +++ b/app/classes/ratelimiter.php @@ -2,15 +2,16 @@ class RateLimiter { public $db; + private $database; private $log; public $maxAttempts = 5; // Maximum login attempts public $decayMinutes = 15; // Time window in minutes public $autoBlacklistThreshold = 10; // Attempts before auto-blacklist public $autoBlacklistDuration = 24; // Hours to blacklist for - public $authRatelimitTable = 'login_attempts'; // For username/password attempts - public $pagesRatelimitTable = 'pages_rate_limits'; // For page requests - public $whitelistTable = 'ip_whitelist'; - public $blacklistTable = 'ip_blacklist'; + public $authRatelimitTable = 'security_rate_auth'; // For rate limiting username/password attempts + public $pagesRatelimitTable = 'security_rate_page'; // For rate limiting page requests + public $whitelistTable = 'security_ip_whitelist'; // For whitelisting IPs and network ranges + public $blacklistTable = 'security_ip_blacklist'; // For blacklisting IPs and network ranges private $pageLimits = [ // Default rate limits per minute 'default' => 60, @@ -23,11 +24,8 @@ class RateLimiter { ]; public function __construct($database) { - if ($database instanceof PDO) { - $this->db = $database; - } else { - $this->db = $database->getConnection(); - } + $this->database = $database; // Store the Database object + $this->db = $database->getConnection(); // Initialize logger via Log wrapper require_once __DIR__ . '/log.php'; $this->log = new Log($database); @@ -39,42 +37,47 @@ class RateLimiter { private function createTablesIfNotExist() { // Authentication attempts table $sql = "CREATE TABLE IF NOT EXISTS {$this->authRatelimitTable} ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - attempted_at TEXT DEFAULT (DATETIME('now')) + id int(11) PRIMARY KEY AUTO_INCREMENT, + ip_address VARCHAR(45) NOT NULL, + username VARCHAR(255) NOT NULL, + attempted_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_ip_username (ip_address, username) )"; $this->db->exec($sql); // Pages rate limits table $sql = "CREATE TABLE IF NOT EXISTS {$this->pagesRatelimitTable} ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - endpoint TEXT NOT NULL, - request_time DATETIME DEFAULT CURRENT_TIMESTAMP + id int(11) PRIMARY KEY AUTO_INCREMENT, + ip_address VARCHAR(45) NOT NULL, + endpoint VARCHAR(255) NOT NULL, + request_time DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_ip_endpoint (ip_address, endpoint), + INDEX idx_request_time (request_time) )"; $this->db->exec($sql); // IP whitelist table $sql = "CREATE TABLE IF NOT EXISTS {$this->whitelistTable} ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL UNIQUE, - is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)), - description TEXT, - created_at TEXT DEFAULT (DATETIME('now')), - created_by TEXT + id int(11) PRIMARY KEY AUTO_INCREMENT, + ip_address VARCHAR(45) NOT NULL, + is_network BOOLEAN DEFAULT FALSE, + description VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by VARCHAR(255), + UNIQUE KEY unique_ip (ip_address) )"; $this->db->exec($sql); // IP blacklist table $sql = "CREATE TABLE IF NOT EXISTS {$this->blacklistTable} ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL UNIQUE, - is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)), - reason TEXT, - expiry_time TEXT NULL, - created_at TEXT DEFAULT (DATETIME('now')), - created_by TEXT + id int(11) PRIMARY KEY AUTO_INCREMENT, + ip_address VARCHAR(45) NOT NULL, + is_network BOOLEAN DEFAULT FALSE, + reason VARCHAR(255), + expiry_time TIMESTAMP NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by VARCHAR(255), + UNIQUE KEY unique_ip (ip_address) )"; $this->db->exec($sql); @@ -88,7 +91,7 @@ class RateLimiter { ]; // Insert default whitelisted IPs if they don't exist - $stmt = $this->db->prepare("INSERT OR IGNORE INTO {$this->whitelistTable} + $stmt = $this->db->prepare("INSERT IGNORE INTO {$this->whitelistTable} (ip_address, is_network, description, created_by) VALUES (?, ?, ?, 'system')"); foreach ($defaultIps as $ip) { @@ -104,7 +107,7 @@ class RateLimiter { ['203.0.113.0/24', true, 'TEST-NET-3 Documentation space - RFC 5737'] ]; - $stmt = $this->db->prepare("INSERT OR IGNORE INTO {$this->blacklistTable} + $stmt = $this->db->prepare("INSERT IGNORE INTO {$this->blacklistTable} (ip_address, is_network, reason, created_by) VALUES (?, ?, ?, 'system')"); @@ -119,7 +122,7 @@ class RateLimiter { */ public function getRecentAttempts($ip) { $stmt = $this->db->prepare("SELECT COUNT(*) as attempts FROM {$this->authRatelimitTable} - WHERE ip_address = ? AND attempted_at > datetime('now', '-' || :minutes || ' minutes')"); + WHERE ip_address = ? AND attempted_at > DATE_SUB(NOW(), INTERVAL ? MINUTE)"); $stmt->execute([$ip, $this->decayMinutes]); $result = $stmt->fetch(PDO::FETCH_ASSOC); return intval($result['attempts']); @@ -222,9 +225,13 @@ class RateLimiter { return false; } - $stmt = $this->db->prepare("INSERT OR REPLACE INTO {$this->whitelistTable} + $stmt = $this->db->prepare("INSERT INTO {$this->whitelistTable} (ip_address, is_network, description, created_by) - VALUES (?, ?, ?, ?)"); + VALUES (?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + is_network = VALUES(is_network), + description = VALUES(description), + created_by = VALUES(created_by)"); $result = $stmt->execute([$ip, $isNetwork, $description, $createdBy]); @@ -236,7 +243,7 @@ class RateLimiter { $createdBy, $description ); - $this->log->insertLog($userId ?? 0, $logMessage, 'system'); + $this->log->insertLog($userId ?? null, $logMessage, 'system'); } return $result; @@ -271,7 +278,7 @@ class RateLimiter { $removedBy, $ipDetails['created_by'] ); - $this->log->insertLog($userId ?? 0, $logMessage, 'system'); + $this->log->insertLog($userId ?? null, $logMessage, 'system'); } return $result; @@ -299,9 +306,14 @@ class RateLimiter { $expiryTime = $expiryHours ? date('Y-m-d H:i:s', strtotime("+{$expiryHours} hours")) : null; - $stmt = $this->db->prepare("INSERT OR REPLACE INTO {$this->blacklistTable} + $stmt = $this->db->prepare("INSERT INTO {$this->blacklistTable} (ip_address, is_network, reason, expiry_time, created_by) - VALUES (?, ?, ?, ?, ?)"); + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + is_network = VALUES(is_network), + reason = VALUES(reason), + expiry_time = VALUES(expiry_time), + created_by = VALUES(created_by)"); $result = $stmt->execute([$ip, $isNetwork, $reason, $expiryTime, $createdBy]); @@ -314,7 +326,7 @@ class RateLimiter { $reason, $expiryTime ?? 'never' ); - $this->log->insertLog($userId ?? 0, $logMessage, 'system'); + $this->log->insertLog($userId ?? null, $logMessage, 'system'); } return $result; @@ -348,7 +360,7 @@ class RateLimiter { $ipDetails['created_by'], $ipDetails['reason'] ); - $this->log->insertLog($userId ?? 0, $logMessage, 'system'); + $this->log->insertLog($userId ?? null, $logMessage, 'system'); } return $result; @@ -379,17 +391,17 @@ class RateLimiter { try { // Remove expired blacklist entries $stmt = $this->db->prepare("DELETE FROM {$this->blacklistTable} - WHERE expiry_time IS NOT NULL AND expiry_time < datetime('now')"); + WHERE expiry_time IS NOT NULL AND expiry_time < NOW()"); $stmt->execute(); // Clean old login attempts $stmt = $this->db->prepare("DELETE FROM {$this->authRatelimitTable} - WHERE attempted_at < datetime('now', '-' || :minutes || ' minutes')"); + WHERE attempted_at < DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"); $stmt->execute([':minutes' => $this->decayMinutes]); return true; } catch (Exception $e) { - $this->log->insertLog(0, "Failed to cleanup expired entries: " . $e->getMessage(), 'system'); + $this->log->insertLog(null, "Failed to cleanup expired entries: " . $e->getMessage(), 'system'); Feedback::flash('ERROR', 'DEFAULT', "Failed to cleanup expired entries: " . $e->getMessage()); return false; } @@ -418,7 +430,7 @@ class RateLimiter { $sql = "SELECT COUNT(*) as total_attempts FROM {$this->authRatelimitTable} WHERE ip_address = :ip - AND attempted_at > datetime('now', '-' || :minutes || ' minutes')"; + AND attempted_at > DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; $stmt = $this->db->prepare($sql); $stmt->execute([ ':ip' => $ipAddress, @@ -456,7 +468,7 @@ class RateLimiter { FROM {$this->authRatelimitTable} WHERE ip_address = :ip AND username = :username - AND attempted_at > datetime('now', '-' || :minutes || ' minutes')"; + AND attempted_at > DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; $stmt = $this->db->prepare($sql); $stmt->execute([ @@ -492,7 +504,7 @@ class RateLimiter { public function clearOldAttempts() { $sql = "DELETE FROM {$this->authRatelimitTable} - WHERE attempted_at < datetime('now', '-' || :minutes || ' minutes')"; + WHERE attempted_at < DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; $stmt = $this->db->prepare($sql); $stmt->execute([ @@ -505,7 +517,7 @@ class RateLimiter { FROM {$this->authRatelimitTable} WHERE ip_address = :ip AND username = :username - AND attempted_at > datetime('now', '-' || :minutes || ' minutes')"; + AND attempted_at > DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; $stmt = $this->db->prepare($sql); $stmt->execute([ @@ -547,7 +559,7 @@ class RateLimiter { FROM {$this->pagesRatelimitTable} WHERE ip_address = :ip AND endpoint = :endpoint - AND request_time >= DATETIME('now', '-1 minute')"; + AND request_time >= DATE_SUB(NOW(), INTERVAL 1 MINUTE)"; $stmt = $this->db->prepare($sql); $stmt->execute([ @@ -578,7 +590,7 @@ class RateLimiter { */ private function cleanOldPageRequests() { $sql = "DELETE FROM {$this->pagesRatelimitTable} - WHERE request_time < DATETIME('now', '-1 minute')"; + WHERE request_time < DATE_SUB(NOW(), INTERVAL 1 MINUTE)"; $stmt = $this->db->prepare($sql); $stmt->execute(); @@ -590,8 +602,10 @@ class RateLimiter { private function getPageLimitForEndpoint($endpoint, $userId = null) { // Admin users get higher limits if ($userId) { - $userObj = new User($this->db); - if ($userObj->hasRight($userId, 'admin')) { + // Check admin rights directly from database + $stmt = $this->db->prepare('SELECT COUNT(*) FROM `user_right` ur JOIN `right` r ON ur.right_id = r.id WHERE ur.user_id = ? AND r.name = ?'); + $stmt->execute([$userId, 'superuser']); + if ($stmt->fetchColumn() > 0) { return $this->pageLimits['admin']; } } @@ -627,7 +641,7 @@ class RateLimiter { FROM {$this->pagesRatelimitTable} WHERE ip_address = :ip AND endpoint = :endpoint - AND request_time > DATETIME('now', '-1 minute')"; + AND request_time > DATE_SUB(NOW(), INTERVAL 1 MINUTE)"; $stmt = $this->db->prepare($sql); $stmt->execute([ diff --git a/app/config/jilo-web.conf.php b/app/config/jilo-web.conf.php index f920dfc..6f9757e 100644 --- a/app/config/jilo-web.conf.php +++ b/app/config/jilo-web.conf.php @@ -22,12 +22,20 @@ return [ //******************************************* // database - 'db' => [ - // DB type for the web app, currently only "sqlite" is used - 'db_type' => 'sqlite', - // default is ../app/jilo-web.db + 'db_type' => 'mariadb', + + 'sqlite' => [ 'sqlite_file' => '../app/jilo-web.db', ], + + 'sql' => [ + 'sql_host' => 'localhost', + 'sql_port' => '3306', + 'sql_database' => 'jilo', + 'sql_username' => 'jilouser', + 'sql_password' => 'jilopass', + ], + // avatars path 'avatars_path' => 'uploads/avatars/', // default avatar diff --git a/app/core/DatabaseConnector.php b/app/core/DatabaseConnector.php index b2a7366..9c8cc23 100644 --- a/app/core/DatabaseConnector.php +++ b/app/core/DatabaseConnector.php @@ -21,10 +21,10 @@ class DatabaseConnector try { $db = connectDB($config); - if (!$db['db']) { + if (!$db) { throw new Exception('Could not connect to database'); } - return $db['db']; + return $db; } catch (Exception $e) { // Show error and exit Feedback::flash('ERROR', 'DEFAULT', getError('Error connecting to the database.', $e->getMessage())); diff --git a/app/includes/database.php b/app/includes/database.php index 3ba67a4..2c61ae1 100644 --- a/app/includes/database.php +++ b/app/includes/database.php @@ -1,61 +1,63 @@ 'sqlite', + 'type' => $config['db_type'], 'dbFile' => $dbFile, ]); - return ['db' => $db, 'error' => null]; + $pdo = $db->getConnection(); } catch (Exception $e) { - return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())]; + Feedback::flash('ERROR', 'DEFAULT', getError('Error connecting to DB.', $e->getMessage())); + return false; } + return $db; - // connecting to a jilo-web database of the web app + // mysql/mariadb database + } elseif ($config['db_type'] === 'mysql' || $config['db_type'] === 'mariadb') { + $db = new Database([ + 'type' => $config['db_type'], + 'host' => $config['sql']['sql_host'] ?? 'localhost', + 'port' => $config['sql']['sql_port'] ?? '3306', + 'dbname' => $config['sql']['sql_database'], + 'user' => $config['sql']['sql_username'], + 'password' => $config['sql']['sql_password'], + ]); + try { + $pdo = $db->getConnection(); + } catch (Exception $e) { + Feedback::flash('ERROR', 'DEFAULT', getError('Error connecting to DB.', $e->getMessage())); + return false; + } + return $db; + + // unknown database } else { - - // sqlite database file - if ($config['db']['db_type'] === 'sqlite') { - try { - $db = new Database([ - 'type' => $config['db']['db_type'], - 'dbFile' => $config['db']['sqlite_file'], - ]); - $pdo = $db->getConnection(); - return ['db' => $db, 'error' => null]; - } catch (Exception $e) { - return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())]; - } - // mysql/mariadb database - } elseif ($config['db']['db_type'] === 'mysql' || $config['db']['db_type'] === 'mariadb') { - try { - $db = new Database([ - 'type' => $config['db']['db_type'], - 'host' => $config['db']['sql_host'] ?? 'localhost', - 'port' => $config['db']['sql_port'] ?? '3306', - 'dbname' => $config['db']['sql_database'], - 'user' => $config['db']['sql_username'], - 'password' => $config['db']['sql_password'], - ]); - $pdo = $db->getConnection(); - return ['db' => $db, 'error' => null]; - } catch (Exception $e) { - return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())]; - } - // unknown database - } else { - $error = "Error: unknow database type \"{$config['db']['db_type']}\""; - Feedback::flash('ERROR', 'DEFAULT', $error); - exit(); - } - + Feedback::flash('ERROR', 'DEFAULT', getError("Error: unknown database type \"{$config['db_type']}\"")); + return false; } } + +// connect to Jilo database +function connectJiloDB($config, $dbFile = '', $platformId = '') { + try { + if (!$dbFile || !file_exists($dbFile)) { + throw new Exception(getError("Invalid platform ID \"{$platformId}\", database file \"{$dbFile}\" not found.")); + } + $db = new Database([ + 'type' => 'sqlite', + 'dbFile' => $dbFile, + ]); + return ['db' => $db, 'error' => null]; + } catch (Exception $e) { + return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())]; + } +} diff --git a/app/pages/agents.php b/app/pages/agents.php index 211d17f..8c51012 100644 --- a/app/pages/agents.php +++ b/app/pages/agents.php @@ -19,8 +19,8 @@ $agentId = filter_input(INPUT_GET, 'agent', FILTER_VALIDATE_INT); require '../app/classes/agent.php'; require '../app/classes/host.php'; -$agentObject = new Agent($dbWeb); -$hostObject = new Host($dbWeb); +$agentObject = new Agent($db); +$hostObject = new Host($db); /** * Get the cache key for an agent @@ -50,7 +50,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Apply rate limiting for adding new contacts require '../app/includes/rate_limit_middleware.php'; - checkRateLimit($dbWeb, 'contact', $userId); + checkRateLimit($db, 'contact', $userId); // Validate agent ID for POST operations if ($agentId === false || $agentId === null) { diff --git a/app/pages/components.php b/app/pages/components.php index 1574996..fca154b 100644 --- a/app/pages/components.php +++ b/app/pages/components.php @@ -9,7 +9,7 @@ */ // connect to database -$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id); +$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); // if DB connection has error, display it and stop here if ($response['db'] === null) { diff --git a/app/pages/conferences.php b/app/pages/conferences.php index cb2eff0..12beb8e 100644 --- a/app/pages/conferences.php +++ b/app/pages/conferences.php @@ -9,7 +9,7 @@ */ // connect to database -$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id); +$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); // if DB connection has error, display it and stop here if ($response['db'] === null) { diff --git a/app/pages/config.php b/app/pages/config.php index 58b0a1c..48c80be 100644 --- a/app/pages/config.php +++ b/app/pages/config.php @@ -13,7 +13,7 @@ require '../app/classes/config.php'; require '../app/classes/api_response.php'; // Initialize required objects -$userObject = new User($dbWeb); +$userObject = new User($db); $configObject = new Config(); // For AJAX requests @@ -63,7 +63,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Apply rate limiting require '../app/includes/rate_limit_middleware.php'; - checkRateLimit($dbWeb, 'config', $userId); + checkRateLimit($db, 'config', $userId); // Ensure no output before this point ob_clean(); diff --git a/app/pages/credentials.php b/app/pages/credentials.php index 9ba94a7..7719681 100644 --- a/app/pages/credentials.php +++ b/app/pages/credentials.php @@ -15,7 +15,7 @@ */ // Initialize user object -$userObject = new User($dbWeb); +$userObject = new User($db); // Get action and item from request $action = $_REQUEST['action'] ?? ''; @@ -33,7 +33,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Apply rate limiting require_once '../app/includes/rate_limit_middleware.php'; - checkRateLimit($dbWeb, 'credentials', $userId); + checkRateLimit($db, 'credentials', $userId); switch ($item) { case '2fa': diff --git a/app/pages/dashboard.php b/app/pages/dashboard.php index c79c583..c7545a5 100644 --- a/app/pages/dashboard.php +++ b/app/pages/dashboard.php @@ -16,7 +16,7 @@ require '../app/classes/conference.php'; require '../app/classes/participant.php'; // connect to database -$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id); +$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); // if DB connection has error, display it and stop here if ($response['db'] === null) { diff --git a/app/pages/graphs.php b/app/pages/graphs.php index c076e8b..bb0f997 100644 --- a/app/pages/graphs.php +++ b/app/pages/graphs.php @@ -7,11 +7,11 @@ require '../app/classes/agent.php'; require '../app/classes/conference.php'; require '../app/classes/host.php'; -$agentObject = new Agent($dbWeb); -$hostObject = new Host($dbWeb); +$agentObject = new Agent($db); +$hostObject = new Host($db); // Connect to Jilo database for log data -$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id); +$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); if ($response['db'] === null) { Feedback::flash('ERROR', 'DEFAULT', $response['error']); } else { diff --git a/app/pages/latest.php b/app/pages/latest.php index dce5168..1bb304e 100644 --- a/app/pages/latest.php +++ b/app/pages/latest.php @@ -3,8 +3,8 @@ require '../app/classes/agent.php'; require '../app/classes/host.php'; -$agentObject = new Agent($dbWeb); -$hostObject = new Host($dbWeb); +$agentObject = new Agent($db); +$hostObject = new Host($db); // Define metrics to display $metrics = [ diff --git a/app/pages/login.php b/app/pages/login.php index 06c4aac..7b36018 100644 --- a/app/pages/login.php +++ b/app/pages/login.php @@ -19,7 +19,7 @@ unset($error); try { // connect to database - $db = connectDB($config)['db']; + $db = connectDB($config); // Initialize RateLimiter require_once '../app/classes/ratelimiter.php'; diff --git a/app/pages/participants.php b/app/pages/participants.php index 0afb5c7..bac2ab4 100644 --- a/app/pages/participants.php +++ b/app/pages/participants.php @@ -9,7 +9,7 @@ */ // connect to database -$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id); +$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); // if DB connection has error, display it and stop here if ($response['db'] === null) { diff --git a/app/pages/profile.php b/app/pages/profile.php index 7e966e4..a53aa4e 100644 --- a/app/pages/profile.php +++ b/app/pages/profile.php @@ -30,7 +30,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Apply rate limiting for profile operations require_once '../app/includes/rate_limit_middleware.php'; - checkRateLimit($dbWeb, 'profile', $userId); + checkRateLimit($db, 'profile', $userId); // avatar removal if ($item === 'avatar' && $action === 'remove') { diff --git a/app/pages/security.php b/app/pages/security.php index 6b980e8..6d1cdcb 100644 --- a/app/pages/security.php +++ b/app/pages/security.php @@ -14,7 +14,7 @@ $section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section' // Initialize RateLimiter require_once '../app/classes/ratelimiter.php'; -$rateLimiter = new RateLimiter($dbWeb); +$rateLimiter = new RateLimiter($db); // Handle form submissions if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { @@ -22,7 +22,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { // Apply rate limiting for security operations require_once '../app/includes/rate_limit_middleware.php'; - checkRateLimit($dbWeb, 'security', $userId); + checkRateLimit($db, 'security', $userId); $action = $_POST['action']; $validator = new Validator($_POST); diff --git a/app/pages/settings.php b/app/pages/settings.php index dc32e3c..e031bae 100644 --- a/app/pages/settings.php +++ b/app/pages/settings.php @@ -21,8 +21,8 @@ $host = $_REQUEST['host'] ?? ''; require '../app/classes/host.php'; require '../app/classes/agent.php'; -$hostObject = new Host($dbWeb); -$agentObject = new Agent($dbWeb); +$hostObject = new Host($db); +$agentObject = new Agent($db); if ($_SERVER['REQUEST_METHOD'] == 'POST') { /** @@ -31,7 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Apply rate limiting for profile operations require_once '../app/includes/rate_limit_middleware.php'; - checkRateLimit($dbWeb, 'profile', $userId); + checkRateLimit($db, 'profile', $userId); // Get hash from URL if present $hash = parse_url($_SERVER['REQUEST_URI'], PHP_URL_FRAGMENT) ?? ''; diff --git a/app/pages/status.php b/app/pages/status.php index 31fecd4..fb2badc 100644 --- a/app/pages/status.php +++ b/app/pages/status.php @@ -13,8 +13,8 @@ include '../app/helpers/feedback.php'; require '../app/classes/agent.php'; require '../app/classes/host.php'; -$agentObject = new Agent($dbWeb); -$hostObject = new Host($dbWeb); +$agentObject = new Agent($db); +$hostObject = new Host($db); include '../app/templates/status-server.php'; @@ -22,7 +22,7 @@ include '../app/templates/status-server.php'; foreach ($platformsAll as $platform) { // check if we can connect to the jilo database - $response = connectDB($config, 'jilo', $platform['jilo_database'], $platform['id']); + $response = connectJiloDB($config, $platform['jilo_database'], $platform['id']); if ($response['error'] !== null) { $jilo_database_status = $response['error']; } else { diff --git a/doc/database/main.sql b/doc/database/main.sql index 680a979..155feab 100644 --- a/doc/database/main.sql +++ b/doc/database/main.sql @@ -20,8 +20,8 @@ CREATE TABLE `users` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; INSERT INTO `users` (`id`, `username`, `password`) VALUES -(1,'demo','$2y$10$tLCLvgYu91gf/zBoc58Am.iVls/SOMcIXO3ykGfgFFei9yneZTrb2'), -(2,'demo1','$2y$10$LtV9m.rMCJ.K/g45e6tzDexZ8C/9xxu3qFCkvz92pUYa7Jg06np0i'); +(1,'demo','$2y$12$AtIKs3eVxD4wTT1IWwJujuuHyGhhmfBJYqSfIrPFFPMDfKu3Rcsx6'), +(2,'demo1','$2y$12$ELwYyhQ8XDkVvX9Xsb0mlORqeQHNFaBOvaBuPQym4n4IomA/DgvLC'); -- -------------------------------------------------------- CREATE TABLE `users_meta` ( @@ -67,7 +67,7 @@ INSERT INTO `rights` (`id`, `name`) VALUES (15,'view jilo config'); -- -------------------------------------------------------- -CREATE TABLE `users_right` ( +CREATE TABLE `users_rights` ( `user_id` int(11) NOT NULL, `right_id` int(11) NOT NULL, PRIMARY KEY (`user_id`,`right_id`), diff --git a/plugins/register/controllers/register.php b/plugins/register/controllers/register.php index 131baca..dc86924 100644 --- a/plugins/register/controllers/register.php +++ b/plugins/register/controllers/register.php @@ -21,13 +21,13 @@ require_once dirname(__FILE__, 4) . '/app/helpers/security.php'; if ($config['registration_enabled'] == true) { try { - global $dbWeb, $logObject, $userObject; + global $db, $logObject, $userObject; if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) { // Apply rate limiting require_once dirname(__FILE__, 4) . '/app/includes/rate_limit_middleware.php'; - checkRateLimit($dbWeb, 'register'); + checkRateLimit($db, 'register'); $security = SecurityHelper::getInstance(); @@ -67,7 +67,7 @@ if ($config['registration_enabled'] == true) { $password = $formData['password']; // registering - $register = new Register($dbWeb); + $register = new Register($db); $result = $register->register($username, $password); // redirect to login diff --git a/public_html/index.php b/public_html/index.php index 6efec19..177230b 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -80,30 +80,13 @@ error_reporting(E_ALL); // edit accordingly, add 'pages/PAGE.php' $allowed_urls = [ 'dashboard', - - 'conferences', - 'participants', - 'components', - - 'graphs', - 'latest', - 'livejs', - - 'agents', - - 'config', - - 'profile', - 'credentials', - + 'conferences','participants','components', + 'graphs','latest','livejs','agents', + 'profile','credentials','config','security', 'settings', - 'security', 'status', 'help', - - 'login', - 'logout', - + 'login','logout', 'about', ]; @@ -137,10 +120,10 @@ require_once __DIR__ . '/../app/core/Router.php'; use App\Core\Router; $currentUser = Router::checkAuth($config, $app_root, $public_pages, $page); -// connect to DB via DatabaseConnector +// Connect to DB via DatabaseConnector require_once __DIR__ . '/../app/core/DatabaseConnector.php'; use App\Core\DatabaseConnector; -$dbWeb = DatabaseConnector::connect($config); +$db = DatabaseConnector::connect($config); // Logging: default to NullLogger, plugin can override require_once __DIR__ . '/../app/core/NullLogger.php'; @@ -151,7 +134,7 @@ require_once __DIR__ . '/../app/helpers/ip_helper.php'; $user_IP = ''; // Plugin: initialize logging system plugin if available -do_hook('logger.system_init', ['db' => $dbWeb]); +do_hook('logger.system_init', ['db' => $db]); // Override defaults if plugin provided real logger if (isset($GLOBALS['logObject'])) { @@ -174,7 +157,7 @@ require '../app/classes/ratelimiter.php'; // get platforms details require '../app/classes/platform.php'; -$platformObject = new Platform($dbWeb); +$platformObject = new Platform($db); $platformsAll = $platformObject->getPlatformDetails(); // by default we connect ot the first configured platform @@ -187,7 +170,7 @@ $platformDetails = $platformObject->getPlatformDetails($platform_id); // init user functions require '../app/classes/user.php'; include '../app/helpers/profile.php'; -$userObject = new User($dbWeb); +$userObject = new User($db); // logout is a special case, as we can't use session vars for notices if ($page == 'logout') { @@ -225,7 +208,7 @@ if ($page == 'logout') { // check if the Jilo Server is running require '../app/classes/server.php'; - $serverObject = new Server($dbWeb); + $serverObject = new Server($db); $server_host = '127.0.0.1'; $server_port = '8080'; @@ -255,9 +238,9 @@ if ($page == 'logout') { // all normal pages if (isset($plugin_controllers[$page])) { include $plugin_controllers[$page]; - } else { - include "../app/pages/{$page}.php"; - } + } else { + include "../app/pages/{$page}.php"; + } } else { // the page is not in allowed urls, loading "not found" page include '../app/templates/error-notfound.php'; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 38fe4ae..8509114 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,9 +12,16 @@ if (!headers_sent()) { ini_set('session.gc_maxlifetime', 1440); // 24 minutes } +// Load plugin Log model and IP helper early so fallback wrapper is bypassed +require_once __DIR__ . '/../plugins/logs/models/Log.php'; +require_once __DIR__ . '/../app/helpers/ip_helper.php'; + // Load Composer's autoloader require_once __DIR__ . '/vendor/autoload.php'; +// Ensure core NullLogger is available during tests +require_once __DIR__ . '/../app/core/NullLogger.php'; + // Set error reporting error_reporting(E_ALL); ini_set('display_errors', 1); @@ -26,18 +33,23 @@ date_default_timezone_set('UTC'); // Define global variables needed by the application $GLOBALS['app_root'] = '/'; $GLOBALS['config'] = [ - 'db' => [ - 'type' => 'sqlite', - 'dbFile' => ':memory:' - ] + 'db_type' => 'mariadb', + 'sql' => [ + 'sql_host' => 'localhost', + 'sql_port' => '3306', + 'sql_database' => 'jilo_test', + 'sql_username' => 'test_jilo', + 'sql_password' => '', + ], + 'environment' => 'testing' ]; // Define global connectDB function if (!function_exists('connectDB')) { function connectDB($config) { - global $dbWeb; + global $db; return [ - 'db' => $dbWeb + 'db' => $db ]; } }